Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ecf9f5f
feat(cachekitio): report L1 hit latency and add a telemetry off switch
27Bslash6 Nov 1, 2026
51f2d2c
fix(cachekitio): a forked child never reports its parent's L1 hits; t…
27Bslash6 Nov 1, 2026
160f2fe
fix(cachekitio): match CACHEKIT_TELEMETRY in any case; empty reports …
27Bslash6 Nov 1, 2026
73aa58c
refactor(cachekitio): the async timed call keeps the callee's result …
27Bslash6 Nov 1, 2026
86ac974
fix(cachekitio): child threads racing a hookless fork agree on one re…
27Bslash6 Nov 1, 2026
cfc0ee9
fix(cachekitio): the sampling draw no longer consumes the global rand…
27Bslash6 Nov 1, 2026
c6387bd
fix(cachekitio): a child forked from C reseeds the sampling draw at i…
27Bslash6 Nov 1, 2026
ebfd4c0
fix(cachekitio): a report built in a child forked from C reseeds the …
27Bslash6 Nov 1, 2026
cb78a7f
fix(cachekitio): redraw the timed hit that reseeds a C-forked child's…
27Bslash6 Nov 1, 2026
1f4a905
fix(cachekitio): drop the reseed redraw; seed the measured workload's…
27Bslash6 Nov 1, 2026
e34f0cc
Merge remote-tracking branch 'origin/main' into agent/irving/lab-1207…
27Bslash6 Nov 1, 2026
a3399c1
fix(cachekitio): a child's report takeover keeps a state published by…
27Bslash6 Nov 1, 2026
f227bd3
Merge remote-tracking branch 'origin/main' into agent/irving/50330bc1…
27Bslash6 Nov 1, 2026
5454f2d
perf(telemetry): record the timed L1 hit in place and time one call i…
27Bslash6 Nov 1, 2026
b8865c1
fix(telemetry): keep a failing latency record from demoting the L1 hit
27Bslash6 Nov 1, 2026
3c3ccda
test(telemetry): check every bucket bound through the report's own count
27Bslash6 Nov 1, 2026
7232a66
fix(telemetry): store a report's sum only after its bucket is counted
27Bslash6 Nov 1, 2026
b6353f1
docs(telemetry): an empty CACHEKIT_TELEMETRY is unset; re-record l1_h…
27Bslash6 Nov 1, 2026
0a14956
docs(telemetry): the session id carries the function name; durations …
27Bslash6 Nov 1, 2026
4c03714
Merge remote-tracking branch 'origin/main' into agent/irving/50330bc1…
27Bslash6 Nov 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ def expensive_api_call(user_id: int):

*cachekit.io is in closed beta — [request access](https://cachekit.io) to get started.*

During the beta the backend reports hit counts and L1 hit latency to cachekit.io by default; `CACHEKIT_TELEMETRY=false` turns that off ([Telemetry](docs/backends/cachekitio.md#telemetry)).

How fresh a cachekit.io read is, and how long a deleted project's data stays readable: [Consistency and Deletion](https://docs.cachekit.io/concepts/consistency/).

</details>
Expand Down
1 change: 1 addition & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ def critical_business_logic():
| `CACHEKIT_API_KEY` | **Yes** | — | API key (`ck_live_...`) for authentication |
| `CACHEKIT_API_URL` | No | `https://api.cachekit.io` | API endpoint override |
| `CACHEKIT_TIMEOUT` | No | `5.0` | HTTP request timeout in seconds |
| `CACHEKIT_TELEMETRY` | No | unset (on) | `false` turns off the usage telemetry headers ([Telemetry](backends/cachekitio.md#telemetry)) |

#### Usage

Expand Down
41 changes: 41 additions & 0 deletions docs/backends/cachekitio.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ backend = CachekitIOBackend(
api_key="ck_live_...", # default: CACHEKIT_API_KEY
api_url="https://api.cachekit.io", # default: CACHEKIT_API_URL, then api.cachekit.io
timeout=5.0, # default: CACHEKIT_TIMEOUT, then 5.0 seconds
telemetry=None, # False turns telemetry off; see Telemetry below
)
```

Expand Down Expand Up @@ -181,8 +182,48 @@ The API URL is validated on construction — HTTPS required, credentials in the
CACHEKIT_API_KEY=ck_live_... # API key — or pass api_key= to CachekitIOBackend / @cache.io
CACHEKIT_API_URL=https://api.cachekit.io # Optional — defaults to api.cachekit.io
CACHEKIT_TIMEOUT=5.0 # Optional — request timeout in seconds
CACHEKIT_TELEMETRY=false # Optional — turns telemetry off (see Telemetry)
```

## Telemetry

While cachekit.io is in closed beta, a `CachekitIOBackend` sends usage telemetry to cachekit.io by default. It
becomes opt-in when the service launches. It goes only to the API URL the backend is configured with, as headers
on the cache requests the backend already makes; nothing is sent on its own, and the Redis, Memcached and File
backends and L1-only caches send none.

| Headers | What they carry |
|---------|-----------------|
| `X-CacheKit-Session-ID`, `X-CacheKit-Session-Start` | A per-function session id, a random per-process id joined to the decorated function's `module.qualname` (so it carries the function's name), and its start time |
| `X-CacheKit-L1-Hits`, `X-CacheKit-L2-Hits`, `X-CacheKit-Misses` | The calling function's hit and miss counts this session |
| `X-CacheKit-L1-Hit-Rate` | L1 hits / (L1 hits + L2 hits + misses), three decimals |
| `X-CacheKit-L1-Latency-Buckets`, `-Sum-Ns`, `-One-In` | How long the L1 hits timed since the previous request took (below) |

**L1 hit latency.** Each `@cache` call through the backend is timed with probability 1/256, chosen at random before
the call runs. A timed call that is an L1 hit adds its duration, from entering the wrapper to returning the value,
to a report: sixteen duration buckets doubling from 128 ns, and their sum in nanoseconds. A duration over 60 s is
dropped and counts toward neither. The next request to
cachekit.io carries the report and empties it, so each hit is reported once. A call that waited on another call's
miss for the same key, a miss, an L2 hit and a direct `backend.get()` are never timed, and the health check never
carries a report. A forked child starts with an empty report and its own random draws. Under uWSGI without
`--py-call-uwsgi-fork-hooks`, a worker draws the same sequence as its siblings until it builds a backend or makes its
first request or timed hit on one it inherited. Each call is still timed with probability 1/256, but those workers
time the same call positions. The flag removes the window. Hits timed after the last request are not sent.

**Turning it off.** Pass `telemetry=False`, or set `CACHEKIT_TELEMETRY` (name and value in any case) to `false`, `0`, `off` or `no`.
Off from either wins over on from the other; both unset is on. An empty or blank value counts as unset, so it leaves
telemetry on. Any other value of the variable also turns telemetry off and logs one warning. Both are read once, when the backend is constructed: set the variable before creating it.
`@cache.io()` builds its own backend, so use the variable there.
Comment thread
27Bslash6 marked this conversation as resolved.
Resolved

```python notest
from cachekit.backends.cachekitio import CachekitIOBackend

backend = CachekitIOBackend(telemetry=False)
```

With telemetry off, requests carry none of the headers above. They still carry `X-CacheKit-L1-Status`, which the
API requires, and a `User-Agent` naming cachekit-py and its version.

## When to Use

**Use CachekitIOBackend when**:
Expand Down
5 changes: 5 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ CACHEKIT_TIMEOUT=5.0
# The same variable sizes the Redis pool, whose default is 50
CACHEKIT_CONNECTION_POOL_SIZE=32

# Optional: Turn telemetry off: false, 0, off or no (default: unset, which is on during the closed beta)
# Off here wins over CachekitIOBackend(telemetry=True). Read once, when the backend is constructed
CACHEKIT_TELEMETRY=false

# Optional: Allow custom API hostname - disables SSRF hostname allowlist (default: false)
# Only set to true when pointing at a private test server
CACHEKIT_ALLOW_CUSTOM_HOST=false
Expand All @@ -154,6 +158,7 @@ CACHEKIT_ALLOW_CUSTOM_HOST=false
| `CACHEKIT_TIMEOUT` | `float` | `5.0` | No | Per-request timeout in seconds |
| `CACHEKIT_CONNECTION_POOL_SIZE` | `int` | `32` | No | HTTP connections the backend's one pool keeps, shared by every thread and by async calls. Each concurrent request uses one (HTTP/1.1); a request that finds all in use opens a short-lived extra connection, and urllib3 logs a pool-full warning. The same variable sizes the Redis pool, whose default is 50 |
| `CACHEKIT_ALLOW_CUSTOM_HOST` | `bool` | `false` | No | Disable hostname allowlist (testing only) |
| `CACHEKIT_TELEMETRY` | on/off | unset (on) | No | `false`, `0`, `off` or `no` turns off the usage telemetry headers; `true`, `1`, `on` or `yes` leaves them on; an empty or blank value counts as unset (on); any other value is off. Off from this or from `CachekitIOBackend(telemetry=False)` wins. `CACHEKIT_TELEMETRY` is not bound to the `telemetry` field; the backend combines the two when it is constructed. The name matches in any case, like the other `CACHEKIT_*` variables. See [Telemetry](backends/cachekitio.md#telemetry) |

**Security notes:**
- `CACHEKIT_API_URL` must use HTTPS. HTTP is rejected at startup.
Expand Down
Loading
Loading