Skip to content

Scope logbook PDF invalidation to the day that actually changed - #10

Merged
Jbithell merged 1 commit into
mainfrom
claude/r2-bucket-single-file-yhkyq4
Aug 15, 2026
Merged

Scope logbook PDF invalidation to the day that actually changed#10
Jbithell merged 1 commit into
mainfrom
claude/r2-bucket-single-file-yhkyq4

Conversation

@Jbithell

@Jbithell Jbithell commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Investigating why the tracker R2 bucket only holds one object.

It isn't the daily workflow

Neither scheduled thing deletes from R2:

  • daily-logbook-email (website/workers/logbookEmailWorkflow.ts, schedules: ["5 0 * * *"]) only ever calls getOrRenderLogbookPdf, which does get then put. There is no delete in it.
  • The GitHub Action (.github/workflows/cloudflare-workers-deploy.yml) runs wrangler deploy --keep-vars plus d1 migrations apply. Neither touches bucket objects, and there has never been a second, daily Action in this repo's history.

There is exactly one R2_BUCKET.delete(...) call in the whole codebase: invalidateLogbookArchive in website/app/logbook/pdfArchive.server.ts.

What was actually emptying it

invalidateLogbookArchive deletes the entire logbook/{deviceId}/ prefix. With a single device that is "empty the bucket", and it was being called for edits that only affect one day — most notably adding a remark on the logbook page. One remark on one day discarded every archived PDF the device had, leaving only whatever the nightly Workflow wrote next. That matches the observed steady state of one object.

The archive has been live since 2026-07-26, so roughly twenty days' worth of renders were being thrown away and re-rendered through Browser Rendering, which is the single most expensive thing the app does and exactly what this cache exists to avoid.

Changes

  • New invalidateLogbookArchiveDay(env, deviceId, dateString) deletes one key. A remark is filed against its own dateString (see LogbookRemarks) and cannot appear in any other day's log, so the remark path in app/routes/date/logbook.tsx now uses it.
  • Whole-device invalidation is unchanged for timing points and logbook config — those genuinely do rewrite every past day — and its doc comment now says when not to reach for it.
  • Fixed the opposite bug on the admin devices page: renaming a device or changing its display distance unit left stale PDFs behind, since both are printed into the document (the name in its title, the unit on every distance). app/routes/admin/devices.tsx now reads those two fields before the update and invalidates the device's archive only when one of them actually changed.
  • Readme records that nothing on a schedule deletes from the bucket, and points at object lifecycle rules as the other thing to check.

Worth checking outside the repo

If the bucket still drains after this, the remaining candidate is an object lifecycle rule on the tracker bucket in the Cloudflare dashboard — that is configured outside this repository and code review cannot rule it out. The key of the one surviving object is a good tell: if it is yesterday's date, invalidation was the cause; an older arbitrary date would point at renders never happening in the first place.

Testing

npx tsc -b passes with no new errors. Two pre-existing failures remain in app/routes/date/timingPoints.tsx and app/routes/date/timingPointsHistoricComparison.tsx; both reproduce unchanged on the base commit and are untouched by this diff.

Adding a remark to one day dropped every archived PDF the device had, because
`invalidateLogbookArchive` deletes the whole `logbook/<deviceId>/` prefix. With
a single device in the bucket that is "delete everything", so one remark threw
away weeks of renders and left only whatever the nightly Workflow wrote next —
the expensive re-rendering this cache exists to avoid. A remark is filed against
its own `dateString` and cannot appear in any other day's log, so the new
`invalidateLogbookArchiveDay` drops just that one object.

The whole-device invalidation is still right for timing points and logbook
config, which do rewrite every past day, and is now also called when a device is
renamed or its display distance unit changes on the admin devices page. Both are
printed into the PDF (the name in its title, the unit on every distance), so
archived copies stopped matching the page after either edit and nothing dropped
them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdZht5FMiuDuD4HhKJHupa
@Jbithell
Jbithell marked this pull request as ready for review August 15, 2026 09:38
Copilot AI lite review requested due to automatic review settings August 15, 2026 09:38
@Jbithell
Jbithell merged commit 7ab3acc into main Aug 15, 2026
1 check passed
@Jbithell
Jbithell deleted the claude/r2-bucket-single-file-yhkyq4 branch August 15, 2026 09:39

Copilot AI 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.

Pull request overview

This PR fixes overly-broad logbook PDF cache invalidation in R2 by scoping deletions to the smallest affected set of archived PDFs, avoiding expensive and unnecessary Browser Rendering re-renders.

Changes:

  • Add invalidateLogbookArchiveDay(env, deviceId, dateString) to delete a single day’s archived PDF key.
  • Update logbook remark submission to invalidate only the edited day’s PDF instead of the entire device prefix.
  • Update admin device updates to invalidate the full device archive only when PDF-affecting device fields (name, display distance unit) actually change, and document the invalidation behavior in the README.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
website/app/routes/date/logbook.tsx Switch remark edits from whole-device invalidation to day-scoped invalidation.
website/app/routes/admin/devices.tsx Invalidate device PDF archive only when name or display distance unit changes.
website/app/logbook/pdfArchive.server.ts Introduce invalidateLogbookArchiveDay and clarify when to use each invalidation function.
Readme.md Document that scheduled jobs don’t delete R2 objects and point to invalidation/lifecycle rules as causes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +126 to +130
await invalidateLogbookArchiveDay(
getCloudflareContext(context).env,
deviceId,
urlDate,
);
Comment on lines +268 to +274
if (
before &&
(before.name !== name ||
before.displayDistanceUnit !== displayDistanceUnit)
) {
await invalidateLogbookArchive(getCloudflareContext(context).env, id);
}
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.

3 participants