Skip to content

fix(plugin): reclaim raw pages after 48h, not 7 days; v0.78.0 - #186

Merged
harper-joseph merged 1 commit into
mainfrom
fix/raw-cache-ttl
Sep 18, 2026
Merged

harper-joseph merged 1 commit into
mainfrom
fix/raw-cache-ttl

Conversation

@harper-joseph

Copy link
Copy Markdown
Contributor

Two different expiries were confused in the table definition #181 shipped.

what it governs value
expiresAt (per row) when the row stops being served — render.raw.expiry, typically the next local midnight ≤24h
@table(expiration:) when the row and its blob are reclaimed was 7 days

So every raw page sat in storage for six days after it could no longer answer anything — roughly 3.5× the footprint for no benefit, replicated to every node. On a corpus of catalog facets that is the difference between a few GB per node and a few tens of GB.

48h leaves a full day of slack past a midnight expiry.

The coupling this creates, and the guard for it

Because it is a schema constant it also bounds render.raw.expiryMs, which is configurable. Set an interval near or past 48h and rows are reclaimed while still nominally servable — which surfaces as a silent miss and a re-fetch, not an error. config.js now warns above 36h.

Verified: a 48h interval warns, 6h does not, and expiry: midnight does not.

Why a constant rather than config

Harper's schema is static GraphQL, so table expiration cannot be driven from config. A constant is the only option; the warning is what keeps the configurable half honest against it.

1155 plugin tests, 276 console, lint and format clean.

Found while answering a question about whether the expiration could be smaller — it could, and it should have been from the start.

🤖 Generated with Claude Code

Two different expiries were confused in the original table definition.

`expiresAt` is when a row stops being SERVED — `render.raw.expiry`, typically the
next local midnight, so <= 24h. The table's `expiration` is when the row and its
BLOB are reclaimed, and it shipped at 7 days. So every raw page sat in storage for
six days after it could no longer answer anything: ~3.5x the footprint for no
benefit, replicated to every node. On a corpus of catalog facets that is the
difference between a few GB per node and a few tens of GB.

48h leaves a full day of slack past a midnight expiry.

Because it is a schema constant it also bounds `render.raw.expiryMs`, which IS
configurable — set an interval near or past 48h and rows are reclaimed while still
nominally servable, which reads as a silent miss rather than an error. config.js now
warns above 36h, since that is the one raw-cache setting that can be made incoherent
from config alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@harper-joseph
harper-joseph merged commit 68f999b into main Sep 18, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request bumps the version of @harperfast/prerender to 0.78.0 and reduces the RawPage table expiration from 7 days to 48 hours in the GraphQL schema to optimize storage footprint. Additionally, a configuration warning is introduced in config.js to alert users when render.raw.expiryMs is set close to or beyond this 48-hour limit. Feedback suggests replacing the magic number 129600000 with 36 * HOUR to improve readability.

// nominally servable — the row and its blob vanish, and the next request is a miss that silently
// re-fetches. The schema constant is 48h (schemas/schema.graphql, RawPage), and it cannot be
// configured, so this is the one raw-cache setting that can be made incoherent from config alone.
if (target.render.raw.enabled && target.render.raw.expiry === 'interval' && target.render.raw.expiryMs > 129600000) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The magic number 129600000 represents 36 hours. Using the HOUR constant imported at the top of the file improves readability and maintainability.

Suggested change
if (target.render.raw.enabled && target.render.raw.expiry === 'interval' && target.render.raw.expiryMs > 129600000) {
if (target.render.raw.enabled && target.render.raw.expiry === 'interval' && target.render.raw.expiryMs > 36 * HOUR) {

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.

1 participant