Skip to content

update the age header when responding from cache - #15052

Open
asharpe wants to merge 6 commits into
cloudflare:mainfrom
asharpe:cache-hit-age
Open

update the age header when responding from cache#15052
asharpe wants to merge 6 commits into
cloudflare:mainfrom
asharpe:cache-hit-age

Conversation

@asharpe

@asharpe asharpe commented Aug 6, 2026

Copy link
Copy Markdown

Fixes #[insert GH or internal issue link(s)].

When responding from the cache (HIT), update the Age header to be relative to the time the object was stored.


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because:

A picture of a cute animal (not mandatory, but encouraged)


Open in Devin Review

@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f3dcb37

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
miniflare Patch
@cloudflare/deploy-helpers Patch
@cloudflare/pages-shared Patch
@cloudflare/remote-bindings Patch
@cloudflare/runtime-types Patch
@cloudflare/vite-plugin Patch
@cloudflare/vitest-pool-workers Patch
wrangler Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@workers-devprod
workers-devprod requested review from a team and NuroDev and removed request for a team August 6, 2026 04:47
@workers-devprod

workers-devprod commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/increment-cache-response-age.md: [@cloudflare/wrangler]
  • packages/miniflare/src/workers/cache/cache.worker.ts: [@cloudflare/wrangler]

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment thread packages/miniflare/src/workers/cache/cache.worker.ts Outdated
Comment on lines +306 to +309
const now = this.timers.now();
const age = parseInt(resHeaders.get("age") || "0", 10);
const cachedDuration = Math.round((now - (cached.metadata.stored || now)) / 1000)
resHeaders.set("Age", age + cachedDuration);

@devin-ai-integration devin-ai-integration Bot Aug 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Behaviour change to cached response headers ships without any test coverage

The change that rewrites the age of responses served from the local cache (resHeaders.set("Age", ...) at packages/miniflare/src/workers/cache/cache.worker.ts:309) has no accompanying test, which the repository requires for new functionality.
Impact: A regression in the reported age of cached responses could ship unnoticed.

Repository testing requirement

CONTRIBUTING.md ("PR Tests") states: "Every PR should include tests for the functionality that's being added", and Miniflare tests live in packages/miniflare/test (e.g. test/plugins/cache/index.spec.ts). The PR modifies cache HIT behaviour and stores a new stored metadata field but adds no spec covering either the incremented age or the fallback for entries stored before this change.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 2 additional findings in Devin Review.

Open in Devin Review

headers: Object.entries(headers),
status: res.status,
size,
stored: this.timers.now(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Cached responses can report an age that is too young for slow or large uploads

The time a cached item was saved is recorded (this.timers.now() at packages/miniflare/src/workers/cache/cache.worker.ts:366) only after the whole body has finished being written, rather than when the item was received, so slow or large responses are later reported as newer than they are.
Impact: Clients can be told a cached response is fresher than it really is, which can make them hold on to stale content longer.

Why the timestamp lands late: metadata promise resolves after the blob write

metadata is a promise created from sizePromise (packages/miniflare/src/workers/cache/cache.worker.ts:355-367). KeyValueStorage.put() first awaits this.#blob.put(entry.value) and only then awaits entry.metadata (packages/miniflare/src/workers/shared/keyvalue.worker.ts:208-223), so this.timers.now() inside the .then() executes after the entire body has been streamed. By contrast, the entry's expiration is computed synchronously at request time (packages/miniflare/src/workers/cache/cache.worker.ts:372), so stored and expiration use different clocks. Capturing the timestamp once, before starting the stream, and reusing it for both would keep them consistent.

Prompt for agents
In packages/miniflare/src/workers/cache/cache.worker.ts, the `put` handler records `stored: this.timers.now()` inside the metadata promise (`sizePromise.then(...)`). Because `KeyValueStorage.put()` awaits the blob write before awaiting the metadata promise (packages/miniflare/src/workers/shared/keyvalue.worker.ts), this timestamp is taken after the whole response body has been streamed, not when the response was received. The `expiration` passed to `storage.put` is computed synchronously at request time, so the two values are based on different clock readings; for large or slow bodies the entry's recorded store time is later than its effective expiry basis, making the Age header computed in `match` too small. Consider capturing a single `now` value before starting the stream and using it for both `stored` and `expiration`.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@NuroDev NuroDev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some tests to this PR so we can validate this change actually works as intended.

Additionally can you update the PR description to fill out the missing details.

"miniflare": patch
---

Increment the Age response header when responding from cache.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some more details to this changeset so it makes it a bit clearer on the change and what it actually fixes.

Andrew Sharpe and others added 6 commits August 12, 2026 07:02
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/increment-cache-response-age.md: [@cloudflare/wrangler]
  • packages/miniflare/src/workers/cache/cache.worker.ts: [@cloudflare/wrangler]

@petebacondarwin

Copy link
Copy Markdown
Contributor

We recently landed #14994, a large change to Miniflare's configuration internals that touched ~177 files across the repo. Leaving this PR on its old base was likely to cause conflicts, so we've rebased it onto the latest main and force-pushed the result.

Your local copy of this branch is now out of date. Before you push again, please reset to the new version:

git fetch origin
git checkout cache-hit-age
git reset --hard origin/cache-hit-age

Because the base moved a long way, it's also worth reinstalling before you carry on — the lockfile changed:

pnpm install

Sorry for the interruption. If the rebase looks wrong, or CI now fails in a way that seems related to the Miniflare config change rather than your own work, comment here and we'll help get it sorted.

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.

4 participants