Caching and usage statistics

basef1.client.BaseF1Client caches successful JSON responses for idempotent GET requests. Each logical terminal call (get_seasons, query()....get_races(), etc.) goes through the same path: build URL and limit/offset query parameters, look up a cache entry, and only hit the network on a miss.

What gets cached

  • Only responses that become a valid top-level JSON object (dict) after transport and parsing are stored.

  • Failures (basef1.exceptions.JolpicaHTTPError) are not cached.

  • Stored values are deep copies on read and write, so mutating a response body in application code does not corrupt the cache.

Cache keys

Keys are produced by basef1.cache.cache_key_for_request() using the HTTP method (always GET here), full URL, and sorted query parameters. Two calls with the same URL and the same parameters hit the same entry regardless of dict iteration order in your code.

In-memory backend (default)

The default backend is basef1.cache.TTLCache:

  • Entries expire after ttl_seconds (client default: basef1.client.DEFAULT_CACHE_TTL_SECONDS).

  • Capacity is bounded by max_entries (default 256). When full, the oldest inserted entry is evicted first (LRU-ish insertion order).

SQLite backend

cache_backend="sqlite" uses basef1.cache.SQLiteCache:

  • Database path defaults to basef1.cache.default_sqlite_cache_path() (~/.cache/basef1/http_cache.sqlite), unless you pass cache_path=.

  • WAL journaling is enabled. Expired rows are removed opportunistically on read and write.

  • Entries persist across processes until TTL expiry—useful for notebooks or repeated scripts.

Always close the client (context manager or close()) so the SQLite connection is released.

Constructor cheat sheet

These rules mirror basef1.client.BaseF1Client:

  • cache=False — disable caching (every logical request issues an HTTP GET unless you use a custom stack).

  • cache_ttl= / cache_max_entries= / cache_backend= / cache_path= — build an internal TTLCache or SQLiteCache. Do not combine cache_ttl= with cache=.

  • cache=some_cache — inject your own TTLCache or SQLiteCache. Do not combine with cache_backend=.

Usage statistics and request log

The client tracks:

  • logical_requests — how many terminal GETs ran through usage_snapshot() accounting (includes cache hits).

  • http_requests — how many HTTP GETs went on the wire.

  • cache_hits — logical requests satisfied from cache.

Inspect programmatically:

from basef1 import BaseF1Client

with BaseF1Client() as client:
    client.get_seasons(limit=30, offset=0)
    client.get_seasons(limit=30, offset=0)
    snap = client.usage_snapshot()
    print(snap.logical_requests, snap.http_requests, snap.cache_hits)
    for entry in snap.request_log:
        print(entry.method, entry.url, entry.source)

Print a human-readable summary (includes approximate cache hit rate and up to 10 recent log lines):

client.stats()

Redirect output when logging:

import io
buf = io.StringIO()
client.stats(file=buf)

The in-memory request log length is capped by request_log_maxlen (default 512; None means unlimited). Each entry records method, URL, sorted params, and cache vs network.

Note: Counting happens when the client commits to a cache lookup or network fetch. Failed HTTP or invalid JSON still increments logical_requests and http_requests where those attempts occur.