PerUserCache¶
- class gafaelfawr.cache.PerUserCache¶
Bases:
BaseCache
Base class for a cache with per-user locking.
Notes
There is a moderately complex locking structure at play here. When there’s a cache miss for data for a specific user, the goal is to block the expensive lookups or token creation for that user until the first requester either looks up the data or creates a new token, either way adding it to the cache. Hopefully then subsequent requests that were blocked on the lock can be answered from the cache.
There is therefore a dictionary of per-user locks, but since we don’t know the list of users in advance, we have to populate those locks on the fly. It shouldn’t be necessary to protect the dict of per-user locks with another lock because we only need to worry about asyncio concurrency, but since FastAPI does use a thread pool, err on the side of caution and use the same locking strategy that would be used for multithreaded code.
Note that the per-user lock must be acquired before the general lock is released, so the
lock
method cannot simply return the per-user lock. To see why, imagine that one code path retrieves the per-user lock in preparation for acquiring it, and then another code path callsclear
.clear
acquires the global lock and then deletes the per-user lock, but the first caller still has a copy of the per-user lock and thinks it’s valid. It may then take that per-user lock, but a third code path could also try to lock the same user and get a new per-user lock from the post-clearing cache. Both the first and third code paths will think they have a lock and may conflict.UserLockManager
is used to handle this.Methods Summary
clear
()Invalidate the cache.
Initialize the cache.
lock
(username)Return the per-user lock for locking.
Methods Documentation
- async clear()¶
Invalidate the cache.
Used primarily for testing. Calls the
initialize
method provided by derivative classes, with proper locking, to reinitialize the cache.- Return type:
- abstract initialize()¶
Initialize the cache.
This will be called by
clear
and should also be called by the derived class’s__init__
method.- Return type:
- async lock(username)¶
Return the per-user lock for locking.
The return value should be used with
async with
to hold a lock around checking for a cached token and, if one is not found, creating and storing a new token.- Parameters:
username (
str
) – Per-user lock to hold.- Returns:
Async context manager that will take the user lock.
- Return type: