You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
docs(saas-api): name the hookless-fork startup window as an API-81 sampling exception (LAB-12412) - #227
API-81 requires an independent pseudo-random draw per call when an SDK samples L1 hit latency. cachekit-py (cachekit-io/cachekit-py#691) reseeds its sampling draw in an at-fork hook, and on a fork that runs no hook (uWSGI without --py-call-uwsgi-fork-hooks) it reseeds on the child's first report construction or use. Until then such a worker draws the same sequence as its siblings. This PR names that window as the one exception in the Sampling paragraph, bounded exactly as cachekit-py ships it.
The new spec text
One exception applies to a child process that inherits a backend instance through a fork that runs none of the SDK's at-fork handlers, such as a fork made from C without the language runtime's fork hooks. Until that child creates a report accumulator for a backend instance, or first uses an accumulator it inherited (its first request other than GET /v1/cache/health on an instance that has one, or its first timed L1 hit), its draws MAY repeat the sequence that its parent and every other process forked from the same state draw. Each call is still timed with probability 1/N, but those processes time the same call positions. Building or using a backend instance that has no accumulator, such as one with telemetry turned off or on a clock that does not qualify, does not end the window. Every other draw, including every draw such a child makes after that point, is independent as above.
What cachekit-py main says
src/cachekit/decorators/l1_latency.py:72-80:
The at-fork hook reseeds it; a fork made from C runs no hook (uWSGI without --py-call-uwsgi-fork-hooks), so the child's first report construction or use reseeds it too: a backend it builds, or its first request or first timed hit on one it inherited, whichever comes first. Until then such a worker draws the same sequence as its siblings: every call is still timed with probability 1/N, but the workers time the same call positions.
docs/backends/cachekitio.md, "L1 hit latency":
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 spec names the health check explicitly because the code's report use on a request is take(), which CachekitIOBackend skips for the health endpoint (backend.py:594), so a health request does not end the window. The docs already say the health check never carries a report.
The end condition is per process: cachekit-py holds one sampling generator per process, so the first report construction or use on any backend ends the window for all of them.
The boundary is the report accumulator, not the backend. CachekitIOBackend builds an L1LatencyReport only when telemetry is on and the clock qualifies (backend.py:398), and only L1LatencyReport construction and use reseed the draw. A child that builds a backend with telemetry off, or makes requests on one, keeps its inherited sequence. The cachekit-py texts above say "a backend it builds" as shorthand for a backend that has a report.
What does not change
The independent-draw MUST and the "never by … the call's position in a sequence" rule hold for every other case; the exception adds no MUST and no requirement id.
No header name, value format or range of N changes. cachekit-ts and cachekit-rs do not reseed on fork, so this text makes no claim about them.
tools/conformance.py check passes and conformance/coverage.md stays current (the API-81 excerpt is unchanged).
Clarified that a forked child process inheriting a backend without running the SDK’s at-fork handlers may repeat its parent’s and siblings’ L1 latency sampling sequence until it creates its own backend or first uses the inherited report accumulator.
After that point, sampling draws are independent. Each call continues to be timed with probability 1/N; the header and the value format and range of N are unchanged.
Walkthrough
The L1 hit-latency sampling rules now allow a forked child to repeat inherited sampling draws until it creates a backend instance or first uses the inherited report accumulator. The changelog records this exception and the unchanged sampling probability and N format.
The specification allows inherited draws to repeat until the child creates a backend instance or first uses the inherited report accumulator. It requires subsequent draws to be independent. The changelog notes the exception and unchanged probability and N format.
cachekit-io/protocol#223: Defines the L1 hit-latency sampling contract, including independent draws and the 1/N rule.
Suggested reviewers: kodus-27b
Merge Risk: Minimal · up to fce94
This documentation change adds a narrow, permissive exception for hookless-fork children to the L1 latency sampling rules and does not alter the header format or N. No concrete runtime or conformance risk was found. The author may still want to confirm whether the exception is intended to apply to every SDK.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title clearly identifies the main change: documenting the hookless-fork startup window as an API-81 sampling exception.
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Description check
The pull request description directly explains the documented fork-related exception to API-81 L1 latency sampling, which matches the changelog and specification changes.
Finishing Touches
Generate unit tests (beta)
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
The reason will be displayed to describe this comment to others. Learn more.
Additional comments: 1
spec/saas-api.md-599-599: 1
599-599: Major · Data Integrity & Integration · Quick win
Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.
Scope the exception to the verified SDK.
API-81 is indexed as an SDK requirement without an sdks restriction. This paragraph therefore applies to cachekit-py, cachekit-ts and cachekit-rs. The new MAY permits correlated fork draws for all three SDKs, although the PR scope only verifies cachekit-py. Scope the exception to cachekit-py, or state that the allowance is intentionally protocol-wide. Update the changelog fragment to match. (raw.githubusercontent.com)
The PR objective states that this change is scoped to cachekit-py and makes no claim about cachekit-ts or cachekit-rs.
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
API-81 requires an independent pseudo-random draw per call when an SDK samples L1 hit latency. cachekit-py (cachekit-io/cachekit-py#691) reseeds its sampling draw in an at-fork hook, and on a fork that runs no hook (uWSGI without
--py-call-uwsgi-fork-hooks) it reseeds on the child's first report construction or use. Until then such a worker draws the same sequence as its siblings. This PR names that window as the one exception in the Sampling paragraph, bounded exactly as cachekit-py ships it.The new spec text
What cachekit-py main says
src/cachekit/decorators/l1_latency.py:72-80:docs/backends/cachekitio.md, "L1 hit latency":The spec names the health check explicitly because the code's report use on a request is
take(), whichCachekitIOBackendskips for the health endpoint (backend.py:594), so a health request does not end the window. The docs already say the health check never carries a report.The end condition is per process: cachekit-py holds one sampling generator per process, so the first report construction or use on any backend ends the window for all of them.
The boundary is the report accumulator, not the backend.
CachekitIOBackendbuilds anL1LatencyReportonly when telemetry is on and the clock qualifies (backend.py:398), and onlyL1LatencyReportconstruction and use reseed the draw. A child that builds a backend with telemetry off, or makes requests on one, keeps its inherited sequence. The cachekit-py texts above say "a backend it builds" as shorthand for a backend that has a report.What does not change
tools/conformance.py checkpasses andconformance/coverage.mdstays current (the API-81 excerpt is unchanged).Changelog fragment:
changelog.d/20261102_l1-latency-sampling-fork-window.md.