You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Update README and SPEC for the per-register cache design, then
add proxy-level tests for miss -> fetch -> store -> hit and stale
fallback on upstream errors. Introduce a small upstream client
interface so tests can use a mock upstream without a real Modbus
connection.
-**Request coalescing**: Multiple identical requests during a cache miss share a single upstream fetch
135
+
-**Key format**: values are cached per register/coil as `{slave_id}:{function_code}:{address}`
136
+
-**Read requests**: Served from cache only if every register/coil in the requested range is present and not expired
137
+
-**Cache misses**: If any value in the requested range is missing or expired, the full range is fetched from upstream and decomposed into per-register/coil cache entries
138
+
-**Write requests**: Forwarded to upstream (if allowed), then invalidate the written address range so overlapping cached reads cannot return stale values
139
+
-**Request coalescing**: Multiple identical range requests during a cache miss share a single upstream fetch using `{slave_id}:{function_code}:{start_address}:{quantity}` as the coalescing key
140
+
-**Stale fallback**: If enabled, expired entries are retained and can be served when upstream requests fail
-**Read Operations**: Check the per-register/coil cache first. Return from cache only if every value in the requested range is present and not expired.
74
+
-**Cache Misses**: If any value in the requested range is missing or expired, fetch the full requested range from upstream, then decompose the response into per-register/coil cache entries.
75
+
-**Write Operations**: Always forward to the device when writes are allowed, then invalidate each cached register/coil in the written address range. This prevents overlapping cached read ranges from serving stale values after frequent writes.
68
76
-**TTL**: Configurable (default: 10 seconds)
69
-
-**Cleanup**: Time-based expiration (entries removed when TTL expires)
70
-
-**Staleness**: Option to serve stale data on upstream failure (default: off)
77
+
-**Cleanup**: Time-based expiration. Expired entries are removed during cleanup unless stale serving is enabled.
78
+
-**Staleness**: Option to serve stale data on upstream failure (default: off). When enabled, expired entries are retained so they remain available for fallback.
The cache also exposes `Coalesce(ctx, rangeKey, fetch)` for request coalescing. It does not read or write cache entries directly; the proxy performs cache lookups and stores decomposed responses.
215
+
173
216
### Request Flow
174
217
175
218
1. Client sends Modbus TCP request
176
219
2. Parse request: extract slave ID, function code, address, quantity
177
220
3.**For reads**:
178
-
-Build cache key
179
-
-Check cache → if hit & valid, return cached data
180
-
- On miss: forward to upstream device
181
-
-Store response in cache
221
+
-Check every per-register/coil cache key in the requested range
222
+
-If all values are present and valid, reassemble and return the Modbus response
223
+
- On any miss or expired value: coalesce identical in-flight range requests, then forward to upstream device
224
+
-Decompose successful upstream responses into per-register/coil cache entries
182
225
- Return response to client
183
226
4.**For writes**:
184
227
- Check readonly mode
185
-
- If allowed: forward to upstream, optionally invalidate cache
228
+
- If allowed: forward to upstream, then invalidate every cached register/coil in the written address range
0 commit comments