Skip to content

fix(ops): the nightly R2 prune never leaves the first page #107

Closed

Description

@dev-bobsong

Found while reviewing the release that became v0.2.0 (tracking: #116).

Problem

The nightly cron prunes expired cache objects with:

const listed = await env.CACHE_BUCKET.list({ prefix: "corx/v1/", limit: 100 });   // app/server.ts

It never reads listed.cursor / listed.truncated, so it only ever inspects the first 100 keys in lexicographic order. Cache keys are corx/v1/<sha256> — random order — so an expired object at position 5,000 becomes visible only after the 4,900 before it have been deleted. In practice the bucket grows without bound for any deployment with more than a page of distinct cached URLs (free tier storage is 10 GB). FEATURES.md says "a 100-object batch", which is honest about the size but not about the consequence.

Lazy expiry on read (cache.ts → getCached) only reclaims URLs that are requested again, so a long tail of one-shot URLs is never freed at all.

Change

  • Sweep pages: follow cursor while truncated, bounded by a per-run budget (a page count and/or key count) so the cron stays inside its own Class A ops budget.
  • Keep the run idempotent and non-fatal (it is today).
  • State the real bound in FEATURES.md / README.md instead of "a 100-object batch".

Acceptance

  • A bucket with 250 keys, 200 of them expired, is fully pruned across runs (test the multi-page walk with a cursor-returning mock).
  • The budget caps the number of list calls per run and is documented.
  • No change to lazy expiry on read.

Files

app/server.ts, test/daily-stats.test.ts

Activity

  1. added
    opsCost, limits, alerting, production safety
    on Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    opsCost, limits, alerting, production safety

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions