Skip to content

Commit dedfc60

Browse files
blaze6950Mykyta Zotov
andauthored
docs: update invariant references in documentation for accuracy (#10)
* docs: update invariant references in documentation for accuracy * refactor: update invariants and descriptions for clarity and consistency across documentation * refactor: update invariants and documentation for cache consistency and rebalance execution --------- Co-authored-by: Mykyta Zotov <mykyta.zotov@ihsmarkit.com>
1 parent aff1721 commit dedfc60

30 files changed

Lines changed: 497 additions & 471 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ catch (Exception ex)
224224
- **Strong consistency**: `GetDataAndWaitForIdleAsync` — always waits for idle regardless of `CacheInteraction`
225225

226226
**Serialized Access Requirement for Hybrid/Strong Modes:**
227-
`GetDataAndWaitOnMissAsync` and `GetDataAndWaitForIdleAsync` provide their warm-cache guarantee only under **serialized (one-at-a-time) access**. Under parallel access, `WaitForIdleAsync`'s "was idle at some point" semantics (Invariant H.49) may return the old completed TCS, missing the rebalance triggered by the concurrent request. These methods remain safe (no crashes/hangs) but the guarantee degrades under parallelism.
227+
`GetDataAndWaitOnMissAsync` and `GetDataAndWaitForIdleAsync` provide their warm-cache guarantee only under **serialized (one-at-a-time) access**. Under parallel access, `WaitForIdleAsync`'s "was idle at some point" semantics (Invariant H.3) may return the old completed TCS, missing the rebalance triggered by the concurrent request. These methods remain safe (no crashes/hangs) but the guarantee degrades under parallelism.
228228

229229
**Lock-Free Operations:**
230230
```csharp

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ Canonical guide: `docs/diagnostics.md`.
402402

403403
By default, `GetDataAsync` is **eventually consistent**: data is returned immediately while the cache window converges asynchronously in the background. Two opt-in extension methods provide stronger consistency guarantees. Both require a `using SlidingWindowCache.Public;` import.
404404

405-
> **Serialized access requirement:** The hybrid and strong consistency modes provide their warm-cache guarantee only when requests are made one at a time (serialized). Under concurrent/parallel callers they remain safe (no crashes or hangs) but the guarantee degrades — due to `AsyncActivityCounter`'s "was idle at some point" semantics (Invariant H.49) and a brief gap between the counter increment and TCS publication in `IncrementActivity`, a concurrent waiter may observe a previously completed idle TCS and return without waiting for the new rebalance.
405+
> **Serialized access requirement:** The hybrid and strong consistency modes provide their warm-cache guarantee only when requests are made one at a time (serialized). Under concurrent/parallel callers they remain safe (no crashes or hangs) but the guarantee degrades — due to `AsyncActivityCounter`'s "was idle at some point" semantics (Invariant H.3) and a brief gap between the counter increment and TCS publication in `IncrementActivity`, a concurrent waiter may observe a previously completed idle TCS and return without waiting for the new rebalance.
406406
407407
### Eventual Consistency (Default)
408408

docs/actors.md

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,18 @@ Non-responsibilities
3535
- Does not compute `DesiredCacheRange` (belongs to Cache Geometry Policy).
3636

3737
Invariant ownership
38-
- -1. User Path and Rebalance Execution never write to cache concurrently
39-
- 0. User Path has higher priority than rebalance execution
40-
- 0a. User Request MAY cancel any ongoing or pending Rebalance Execution ONLY when a new rebalance is validated as necessary
41-
- 1. User Path always serves user requests
42-
- 2. User Path never waits for rebalance execution
43-
- 3. User Path is the sole source of rebalance intent
44-
- 5. Performs only work necessary to return data
45-
- 6. May synchronously request from IDataSource
46-
- 7. May read cache and source, but does not mutate cache state
47-
- 8. MUST NOT mutate cache under any circumstance (read-only)
48-
- 10. Always returns exactly RequestedRange
49-
- 24e. Intent MUST contain delivered data (RangeData)
50-
- 24f. Delivered data represents what user actually received
38+
- A.1. User Path and Rebalance Execution never write to cache concurrently
39+
- A.2. User Path has higher priority than rebalance execution
40+
- A.2a. User Request MAY cancel any ongoing or pending Rebalance Execution ONLY when a new rebalance is validated as necessary
41+
- A.3. User Path always serves user requests
42+
- A.4. User Path never waits for rebalance execution
43+
- A.5. User Path is the sole source of rebalance intent
44+
- A.7. Performs only work necessary to return data
45+
- A.8. May synchronously request from IDataSource
46+
- A.11. May read cache and source, but does not mutate cache state
47+
- A.12. MUST NOT mutate cache under any circumstance (read-only)
48+
- C.8e. Intent MUST contain delivered data (RangeData)
49+
- C.8f. Delivered data represents what user actually received
5150

5251
Components
5352
- `WindowCache<TRange, TData, TDomain>` (facade / composition root; also owns `RuntimeCacheOptionsHolder` and exposes `UpdateRuntimeOptions`)
@@ -69,12 +68,12 @@ Non-responsibilities
6968
- Does not perform I/O.
7069

7170
Invariant ownership
72-
- 29. DesiredCacheRange computed from RequestedRange + config
73-
- 30. Independent of current cache contents
74-
- 31. Canonical target cache state
75-
- 32. Sliding window geometry defined by configuration
76-
- 33. NoRebalanceRange derived from current cache range + config
77-
- 35. Threshold sum constraint (leftThreshold + rightThreshold ≤ 1.0)
71+
- E.1. DesiredCacheRange computed from RequestedRange + config
72+
- E.2. Independent of current cache contents
73+
- E.3. Canonical target cache state
74+
- E.4. Sliding window geometry defined by configuration
75+
- E.5. NoRebalanceRange derived from current cache range + config
76+
- E.6. Threshold sum constraint (leftThreshold + rightThreshold ≤ 1.0)
7877

7978
Components
8079
- `ProportionalRangePlanner<TRange, TDomain>` — computes `DesiredCacheRange`; reads configuration from `RuntimeCacheOptionsHolder` at invocation time
@@ -95,11 +94,11 @@ Non-responsibilities
9594
- Does not call `IDataSource`.
9695

9796
Invariant ownership
98-
- 24. Decision Path is purely analytical (CPU-only, no I/O)
99-
- 25. Never mutates cache state
100-
- 26. No rebalance if inside NoRebalanceRange (Stage 1 validation)
101-
- 27. No rebalance if DesiredCacheRange == CurrentCacheRange (Stage 4 validation)
102-
- 28. Rebalance triggered only if ALL validation stages confirm necessity
97+
- D.1. Decision Path is purely analytical (CPU-only, no I/O)
98+
- D.2. Never mutates cache state
99+
- D.3. No rebalance if inside NoRebalanceRange (Stage 1 validation)
100+
- D.4. No rebalance if DesiredCacheRange == CurrentCacheRange (Stage 4 validation)
101+
- D.5. Rebalance triggered only if ALL validation stages confirm necessity
103102

104103
Components
105104
- `RebalanceDecisionEngine<TRange, TDomain>`
@@ -121,14 +120,14 @@ Non-responsibilities
121120
- Does not determine rebalance necessity (delegates to Decision Engine).
122121

123122
Invariant ownership
124-
- 17. At most one active rebalance intent
125-
- 18. Older intents may become logically superseded
126-
- 19. Executions can be cancelled based on validation results
127-
- 20. Obsolete intent must not start execution
128-
- 21. At most one rebalance execution active
129-
- 22. Execution reflects latest access pattern and validated necessity
130-
- 23. System eventually stabilizes under load through work avoidance
131-
- 24. Intent does not guarantee execution — execution is opportunistic and validation-driven
123+
- C.1. At most one active rebalance intent
124+
- C.2. Older intents may become logically superseded
125+
- C.3. Executions can be cancelled based on validation results
126+
- C.4. Obsolete intent must not start execution
127+
- C.5. At most one rebalance execution active
128+
- C.6. Execution reflects latest access pattern and validated necessity
129+
- C.7. System eventually stabilizes under load through work avoidance
130+
- C.8. Intent does not guarantee execution — execution is opportunistic and validation-driven
132131

133132
Components
134133
- `IntentController<TRange, TData, TDomain>`
@@ -164,18 +163,18 @@ Non-responsibilities
164163
- Does not check if `DesiredCacheRange == CurrentCacheRange` (Stage 4 already passed).
165164

166165
Invariant ownership
167-
- A.4. Rebalance is asynchronous relative to User Path
168-
- F.35. MUST support cancellation at all stages
169-
- F.35a. MUST yield to User Path requests immediately upon cancellation
170-
- F.35b. Partially executed or cancelled execution MUST NOT leave cache inconsistent
171-
- F.36. Only path responsible for cache normalization (single-writer architecture)
172-
- F.36a. Mutates cache ONLY for normalization using delivered data from intent
173-
- F.37. May replace / expand / shrink cache to achieve normalization
174-
- F.38. Requests data only for missing subranges (not covered by delivered data)
175-
- F.39. Does not overwrite intersecting data
176-
- F.40. Upon completion: CacheData corresponds to DesiredCacheRange
177-
- F.41. Upon completion: CurrentCacheRange == DesiredCacheRange
178-
- F.42. Upon completion: NoRebalanceRange recomputed
166+
- A.6. Rebalance is asynchronous relative to User Path
167+
- F.1. MUST support cancellation at all stages
168+
- F.1a. MUST yield to User Path requests immediately upon cancellation
169+
- F.1b. Partially executed or cancelled execution MUST NOT leave cache inconsistent
170+
- F.2. Only path responsible for cache normalization (single-writer architecture)
171+
- F.2a. Mutates cache ONLY for normalization using delivered data from intent
172+
- F.3. May replace / expand / shrink cache to achieve normalization
173+
- F.4. Requests data only for missing subranges (not covered by delivered data)
174+
- F.5. Does not overwrite intersecting data
175+
- F.6. Upon completion: CacheData corresponds to DesiredCacheRange
176+
- F.7. Upon completion: CurrentCacheRange == DesiredCacheRange
177+
- F.8. Upon completion: NoRebalanceRange recomputed
179178

180179
Components
181180
- `RebalanceExecutor<TRange, TData, TDomain>`
@@ -190,12 +189,12 @@ Responsibilities
190189
- Coordinate single-writer access between User Path (reads) and Rebalance Execution (writes).
191190

192191
Invariant ownership
193-
- 11. CacheData and CurrentCacheRange are consistent
194-
- 12. Changes applied atomically
195-
- 13. No permanent inconsistent state
196-
- 14. Temporary inefficiencies are acceptable
197-
- 15. Partial / cancelled execution cannot break consistency
198-
- 16. Only latest intent results may be applied
192+
- B.1. CacheData and CurrentCacheRange are consistent
193+
- B.2. Changes applied atomically
194+
- B.3. No permanent inconsistent state
195+
- B.4. Temporary inefficiencies are acceptable
196+
- B.5. Partial / cancelled execution cannot break consistency
197+
- B.6. Only latest intent results may be applied
199198

200199
Components
201200
- `CacheState<TRange, TData, TDomain>`

docs/components/decision.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,6 @@ The system prioritizes **decision correctness and work avoidance** over aggressi
7575

7676
## See Also
7777

78-
- `docs/invariants.md` — formal Decision Path invariant specifications (D.24–D.29)
78+
- `docs/invariants.md` — formal Decision Path invariant specifications (D.1–D.5)
7979
- `docs/architecture.md` — Decision-Driven Execution section
8080
- `docs/components/overview.md` — Invariant Implementation Mapping (Decision subsection)

docs/components/execution.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ The execution subsystem performs debounced, cancellable background work and is t
5353
6. Update `CacheState.NoRebalanceRange` — new stability zone
5454
7. Set `CacheState.IsInitialized = true` (if first execution)
5555

56-
**Cancellation checkpoints** (Invariant F.35):
56+
**Cancellation checkpoints** (Invariant F.1):
5757
- Before I/O: avoids unnecessary fetches
5858
- After I/O: discards fetched data if superseded
5959
- Before mutation: guarantees only latest validated execution applies changes
@@ -67,7 +67,7 @@ The execution subsystem performs debounced, cancellable background work and is t
6767
- Merges new data with preserved existing data (union operation)
6868
- Propagates `CancellationToken` to `IDataSource.FetchAsync`
6969

70-
**Invariants**: F.38 (incremental fetching), F.39 (data preservation during expansion).
70+
**Invariants**: F.4 (incremental fetching), F.5 (data preservation during expansion).
7171

7272
## Responsibilities
7373

@@ -100,21 +100,21 @@ In both cases, `OperationCanceledException` is reported via `ICacheDiagnostics.R
100100

101101
| Invariant | Description |
102102
|-----------|--------------------------------------------------------------------------------------------------------|
103-
| A.7 | Only `RebalanceExecutor` writes to `CacheState` (single-writer) |
104-
| A.8 | User path never blocks waiting for rebalance |
105-
| B.12 | Cache updates are atomic (all-or-nothing via `Rematerialize`) |
106-
| B.13 | Consistency under cancellation: mutations discarded if cancelled |
107-
| B.15 | Cache contiguity maintained after every `Rematerialize` |
108-
| B.16 | Obsolete results never applied (cancellation token identity check) |
109-
| C.21 | Serial execution: at most one active rebalance at a time |
110-
| F.35 | Multiple cancellation checkpoints: before I/O, after I/O, before mutation |
111-
| F.35a | Cancellation-before-mutation guarantee |
112-
| F.37 | `Rematerialize` accepts arbitrary range and data (full replacement) |
113-
| F.38 | Incremental fetching: only missing subranges fetched |
114-
| F.39 | Data preservation: existing cached data merged during expansion |
115-
| G.45 | I/O isolation: `IDataSource` called by execution path only (not by user path during normal cache hits) |
116-
| H.47 | Activity counter incremented before channel write / task chain step |
117-
| H.48 | Activity counter decremented in `finally` blocks |
103+
| A.12a/F.2 | Only `RebalanceExecutor` writes to `CacheState` (single-writer) |
104+
| A.4 | User path never blocks waiting for rebalance |
105+
| B.2 | Cache updates are atomic (all-or-nothing via `Rematerialize`) |
106+
| B.3 | Consistency under cancellation: mutations discarded if cancelled |
107+
| B.5 | Cancelled rebalance cannot violate `CacheData ↔ CurrentCacheRange` consistency |
108+
| B.6 | Obsolete results never applied (cancellation token identity check) |
109+
| C.5 | Serial execution: at most one active rebalance at a time |
110+
| F.1 | Multiple cancellation checkpoints: before I/O, after I/O, before mutation |
111+
| F.1a | Cancellation-before-mutation guarantee |
112+
| F.3 | `Rematerialize` accepts arbitrary range and data (full replacement) |
113+
| F.4 | Incremental fetching: only missing subranges fetched |
114+
| F.5 | Data preservation: existing cached data merged during expansion |
115+
| G.3 | I/O isolation: User Path MAY call `IDataSource` for U1/U5 (cold start / full miss); Rebalance Execution calls it for background normalization only |
116+
| H.1 | Activity counter incremented before channel write / task chain step |
117+
| H.2 | Activity counter decremented in `finally` blocks |
118118

119119
See `docs/invariants.md` (Sections A, B, C, F, G, H) for full specification.
120120

docs/components/intent-management.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Intent management bridges the user path and background work. It receives access
2424
Called by `UserRequestHandler` after serving a request:
2525

2626
1. Atomically replaces pending intent via `Interlocked.Exchange` (latest wins; previous intent superseded)
27-
2. Increments `AsyncActivityCounter` (before signalling — ordering required by Invariant H.47)
27+
2. Increments `AsyncActivityCounter` (before signalling — ordering required by Invariant H.1)
2828
3. Releases semaphore (wakes up `ProcessIntentsAsync` if sleeping)
2929
4. Records `RebalanceIntentPublished` diagnostic event
3030
5. Returns immediately (fire-and-forget)
@@ -80,15 +80,15 @@ User burst: intent₁ → intent₂ → intent₃
8080

8181
| Invariant | Description |
8282
|-----------|--------------------------------------------------------------------------|
83-
| C.17 | At most one pending intent at any time (atomic replacement) |
84-
| C.18 | Previous intents become obsolete when superseded |
85-
| C.19 | Cancellation is cooperative via `CancellationToken` |
86-
| C.20 | Cancellation checked after debounce before execution starts |
87-
| C.21 | At most one active rebalance scheduled at a time |
88-
| C.24 | Intent does not guarantee execution |
89-
| C.24e | Intent carries `deliveredData` (the data the user actually received) |
90-
| H.47 | Activity counter incremented before semaphore signal (ordering) |
91-
| H.48 | Activity counter decremented in `finally` blocks (unconditional cleanup) |
83+
| C.1 | At most one pending intent at any time (atomic replacement) |
84+
| C.2 | Previous intents become obsolete when superseded |
85+
| C.3 | Cancellation is cooperative via `CancellationToken` |
86+
| C.4 | Cancellation checked after debounce before execution starts |
87+
| C.5 | At most one active rebalance scheduled at a time |
88+
| C.8 | Intent does not guarantee execution |
89+
| C.8e | Intent carries `deliveredData` (the data the user actually received) |
90+
| H.1 | Activity counter incremented before semaphore signal (ordering) |
91+
| H.2 | Activity counter decremented in `finally` blocks (unconditional cleanup) |
9292

9393
See `docs/invariants.md` (Section C: Intent invariants, Section H: Activity counter invariants) for full specification.
9494

0 commit comments

Comments
 (0)