docs(code): honest rationales — Akka restart semantics, per-key shards, EF literal inlining; retire or annotate orphaned members

This commit is contained in:
Joseph Doherty
2026-08-16 05:33:33 -04:00
parent dbc9223206
commit b9e9df4081
11 changed files with 133 additions and 58 deletions
+18 -10
View File
@@ -446,27 +446,35 @@ for the life of a generation, so memoizing the walk is sound. A key's `ExpiresUt
*clock*, not with a deployment — so revocation and expiry are re-checked on every call, outside the
cache. Caching them would have let an expired key keep working until the next deploy.
**The cache is sharded per session, because a flat one let any client evict every other client's
decisions.** It used to be a single dictionary with the session identity folded *into* the composite key
**The cache is sharded per calling KEY, because a flat one let any client evict every other client's
decisions.** It used to be a single dictionary with the caller identity folded *into* the composite key
and one process-wide bound of `MaxEntries = 100_000`, whose only eviction was a wholesale `Clear()` at
the cap. So one machine consumer crawling a large address space past 100k entries wiped the memoized
walks of every other session on the server, and each store paid a `ConcurrentDictionary.Count` — an
all-locks snapshot — to test the cap. It is now a dictionary of per-session shards keyed by the session's
`ApiKeyId ?? Principal`, each holding its own `(operation, realm, nodeIdentifier)` entries, with two
walks of every other caller on the server, and each store paid a `ConcurrentDictionary.Count` — an
all-locks snapshot — to test the cap. It is now a dictionary of shards keyed by
**`ApiKeyId ?? Principal`**, each holding its own `(operation, realm, nodeIdentifier)` entries, with two
bounds:
| Bound | Value | What it clears |
|---|---|---|
| `MaxEntriesPerSession` | 20 000 | that one session's shard |
| `MaxEntriesPerSession` | 20 000 | that one **key's** shard |
| `MaxEntries` | 100 000 | every shard (the shard *objects* are dropped, not emptied) |
Both are compile-time constants, not appsettings keys. Five sessions simultaneously at their per-session
ceiling reach the global cap; **the number of shards is not itself bounded** — the bound is entries.
⚠️ **The shard key is the API key, not the session — the constant's name predates it.** Every session
authenticating with the same `otua_<keyId>_…` credential shares one shard and one 20 000-entry budget,
so a crawl by one of them *can* clear the memoized decisions of its sibling sessions. What the sharding
guarantees is isolation **across distinct keys**: one key's crawl can never evict another key's entries.
(An Anonymous session shards under the reserved `anonymous` principal, so all of them likewise share
one shard.)
Both bounds are compile-time constants, not appsettings keys. Five keys simultaneously at their
per-key ceiling reach the global cap; **the number of shards is not itself bounded** — the bound is
entries.
Things to know before changing it:
- **There is no session-close eviction hook**, deliberately. A closed session's shard is shed the next
time a wholesale clear happens — a generation bump, or the global cap tripping. Dropping the shard
- **There is no session-close eviction hook**, deliberately. The shard of a key with no live sessions
left is shed the next time a wholesale clear happens — a generation bump, or the global cap tripping. Dropping the shard
objects rather than emptying them is what makes that sweep work.
- **The counters are approximate under concurrency, by design.** The cap test is a check-then-act over
`Volatile.Read` of `Interlocked` counters rather than a lock, so concurrent stores can overshoot a cap