Skip to content

feat: on-demand refresh mode - #3

Merged
mattdrees merged 4 commits into
mainfrom
feat/on-demand-refresh
Aug 4, 2026
Merged

feat: on-demand refresh mode#3
mattdrees merged 4 commits into
mainfrom
feat/on-demand-refresh

Conversation

@mattdrees

Copy link
Copy Markdown
Member

Adds an on-demand refresh mode for runtimes that freeze between requests (Cloud Run, Lambda), per Brian's ask in the feature-flags thread.

  • Client(refresh_mode="on-demand"), or CRU_FLAGS_REFRESH_MODE=on-demand for a deploy-time switch. Default stays background.
  • On-demand starts no thread: the refresh rides on the reading call, and only once the snapshot is older than poll_seconds. Bounded by fetch_timeout; reads still never raise.
  • New refresh(force=False) for an explicit poke (useful in both modes).
  • ready() in on-demand performs the first attempt rather than waiting on a thread.
  • Contract tests for both modes; docs/design.md §5.1 and README updated.

Rationale for polling on the request path instead of a wake-up-the-app poller: on scale-to-zero the background timer either doesn't run or keeps the instance warm; a synchronous refresh every poll_seconds is fast enough and cheaper.

mattdrees and others added 4 commits July 31, 2026 15:38
Background polling assumes the process keeps running between requests.
On CPU-throttled / scale-to-zero runtimes (Cloud Run, Lambda between
invocations) the timer either doesn't fire or wakes the instance for work
nobody asked for.

`refresh_mode="on-demand"` (or `CRU_FLAGS_REFRESH_MODE=on-demand`) starts
no thread and refreshes on the reading thread when the snapshot is
`poll_seconds` or older. Concurrent readers coalesce onto one fetch;
staleness is anchored on the last attempt, so a dead service costs one
request per interval, not one per read. Also adds `refresh(force=False)`,
useful in both modes.

Trade-off: in on-demand mode `enabled()` can block for up to
`fetch_timeout`, once per interval. Background mode is unchanged and
remains the default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The test service is a ThreadingHTTPServer; its per-request handler thread
could still be alive at the assert, failing on CI (3.11/3.12).

Co-authored-by: Claude <noreply@anthropic.com>
Full rationale stays in design.md §5.1; README, CLAUDE.md and docstrings
keep the contract and point there.

Co-authored-by: Claude <noreply@anthropic.com>
@mattdrees
mattdrees marked this pull request as ready for review August 4, 2026 16:08
@mattdrees
mattdrees merged commit 8f068dd into main Aug 4, 2026
5 checks passed
@mattdrees
mattdrees deleted the feat/on-demand-refresh branch August 4, 2026 17:32
mattdrees pushed a commit that referenced this pull request Aug 4, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.2](v0.1.1...v0.1.2)
(2026-08-04)


### Added

* on-demand refresh mode
([#3](#3))
([8f068dd](8f068dd))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants