mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-30 01:32:25 +01:00
fix(auth): swap the API token cache atomically instead of clearing it
`_refresh_token_cache` rebuilt the bearer-token map in two steps, `clear()` then `update()`. Between them the dict a concurrent reader was already holding was empty, so a valid token landing in that window found zero candidates and got a 401. The refresh runs on a worker thread via `to_thread`, so the window is real rather than theoretical. Build the new map and rebind the name. The reader dereferences the global once and then holds a map that is complete — the previous one if it read early, the new one if it read late, never a half-built one. `app.state._token_cache` is rebound with it, because it was bound once at startup and would otherwise point at the abandoned dict. Ported from public `dev` (`984337b3`), with its test. The upstream test does not pin the fix: both of its concurrency cases pass against the pre-fix code, because landing a GIL switch inside a window a few bytecodes wide does not happen across 100 refreshes. They are kept as written and `TestRefreshLeavesTheReadersMapAlone` is added next to them, stating the same invariant at object level — a map a reader already holds is not mutated by a later refresh — which fails on the pre-fix code without depending on thread scheduling.
This commit is contained in:
@@ -316,6 +316,7 @@ if AUTH_ENABLED:
|
||||
|
||||
def _refresh_token_cache():
|
||||
"""Rebuild the prefix→[(id,hash)] map from the DB."""
|
||||
global _token_cache
|
||||
from collections import defaultdict
|
||||
new_map = defaultdict(list)
|
||||
db = SessionLocal()
|
||||
@@ -334,8 +335,8 @@ if AUTH_ENABLED:
|
||||
new_map[r.token_prefix].append((r.id, r.token_hash, owner_key, scopes))
|
||||
finally:
|
||||
db.close()
|
||||
_token_cache.clear()
|
||||
_token_cache.update(new_map)
|
||||
_token_cache = dict(new_map)
|
||||
app.state._token_cache = _token_cache
|
||||
app.state._token_cache_dirty = False
|
||||
|
||||
# Headers that prove a request was forwarded by a proxy/tunnel (cloudflared,
|
||||
|
||||
Reference in New Issue
Block a user