Cache
A disk cache backed by SQLite, shared safely across threads and processes.
Usage
Cache(
directory,
timeout=None,
*,
size_limit=None,
cull_limit=None,
serializer="msgpack",
pickle=False,
types=None,
clock=None,
max_connections=None,
sqlite_cache_size=None,
wal_size_limit=None,
wal_checkpoint="manual",
on_decode_error=None,
durable=False
)Every thread and process that opens the same directory shares one cache. The API follows diskcache.Cache; the deliberate differences are listed in the Divergences guide.
Parameters
directory: str | os.PathLike[str]-
The cache directory. It’s created (owner-only,
0700) if it doesn’t exist. timeout: float | None = None-
Seconds a call waits for a lock or a pending fork before raising Timeout (default 60). It has to exceed work in flight plus a fork: see the Concurrency and Forks guide.
size_limit: int | None = None-
The disk budget in bytes, database plus value files (default 1 GiB, minimum 1 MiB). Stored in the cache, so it applies to every process.
cull_limit: int | None = None-
How many least-recently-stored entries a set() evicts when the cache is over size_limit (default 10).
serializer: str = "msgpack"-
'msgpack'(the default) or'json', for values that aren’t plainstrorbytes. pickle: bool = False-
Allow pickle for values the serializer can’t represent exactly. Without it, such values raise
TypeErrorand pickled values are never read. Once the encoder has used a registration (types, or the globalregistry) for any part of a value, that value is never pickled: it is stored through the registration, or raises. types: TypeRegistry | None = None-
A
TypeRegistryconsulted before the process-widediskcache_oxide.registry, for a cache that needs its own encoding of a type. Register types at import time, in every process that opens the directory, since a row written through a registration can only be read where it is registered. clock: Callable[[], float] | None = None-
A function returning the current time in seconds, used instead of the native clock. Tests pass
clock=lambda: time.time()to follow a patchedtime.time. max_connections: int | None = None-
The most SQLite connections this process opens for the directory, shared by every handle on it (default 8).
sqlite_cache_size: int | None = None-
SQLite’s page cache per connection, in 4 KiB pages (default 256, 1 MiB).
wal_size_limit: int | None = None-
The write-ahead log is trimmed back to half this many bytes whenever it resets (default 64 MiB, minimum 1 MiB).
wal_checkpoint: Literal["manual", "auto"] = "manual"-
'manual'(the default) leaves checkpoints to checkpoint() and close().'auto'also checkpoints after writes once the log is overwal_size_limit. on_decode_error: Literal["raise", "miss"] | Callable[[str, Exception], object] | None = None-
What a read does with a stored value it can’t decode.
'raise'(the default, unlessdurable=True, where it’s'miss') raises.'miss'treats it as a miss, logs a warning on thediskcache_oxidelogger, and deletes the row if no build could ever read it (a class removed since it was pickled, malformed bytes). A row another version or host might read (ForeignValueError) is left in place. A callable does the same as'miss', but is called with(key, exc)instead of the warning, after the row is dealt with; whatever it raises is logged. UnsafeValueError, Timeout,MemoryErrorand other errors that aren’t about the stored bytes raise, though underdurable=Truea Timeout,OSErrororRecursionErrorfrom a pickle hook is a miss, with the row kept, and so is aRecursionErrorfrom a value too deep for this thread’s stack. Covers get,cache[key]and pop, which decodes before it deletes, so it keeps a row it can’t read. Under'raise', pop still deletes a value no build could read, then raises. durable: bool = False-
Keep the cache from taking a request down, for an app whose every caller can fall back to its source of truth (default False). An outage on a single-key call (a Timeout, a full disk, an I/O error, a damaged database) doesn’t raise: get, pop and
ingive the default or False, set, add, touch and delete give False, and incr and decr givedefault + delta. It’s reported on thediskcache_oxidelogger, at most once a minute per kind of failure. Bugs still raise (TypeError,ValueError, UnsafeValueError), as doMemoryErrorandKeyboardInterrupt, aRecursionErrorfrom encoding a value too deep for this thread’s stack, the constructor, admin calls (len, volume, clear, expire, cull, checkpoint, iteration) and anything inside a transaction held on this cache. This isn’t durability in the database sense: this option can drop writes, and a dropped set, delete, pop or incr means stale data until the entry expires, or forever if it has no expiry. See “A cache that can’t take a request down” in the Getting Started guide before you use it.
Raises
ValueError-
A setting is out of range, such as a size_limit under 1 MiB.
TypeError-
An argument has the wrong type, including a
boolfor timeout. FormatMismatchError-
The directory holds a database in a format this version can’t read.
LayoutMismatchError-
The directory is a FanoutCache root or one of its shards. Also a
ValueError, and a FormatMismatchError. Timeout- Another process held the database locked, or a fork stayed pending, for longer than timeout.
Examples
from diskcache_oxide import Cache
cache = Cache('/var/cache/my-app', timeout=1.0) # every worker opens the same directory
cache.set('user:42', {'name': 'Sam', 'roles': {'admin'}}, expire=300)
cache.get('user:42') # the dict, with roles still a set; None on a miss
cache['greeting'] = 'hello' # never expires
if cache.add('job:nightly', 1, expire=3600): # exactly one process wins
run_nightly_job()
cache.close() # or `with Cache(...) as cache:`