Skip to content

Repeated clock advance no longer 500s on a duplicate timeline id - #70

Merged
midagedev merged 1 commit into
mainfrom
fix/idempotent-timeline-insert-v2
Aug 20, 2026
Merged

Repeated clock advance no longer 500s on a duplicate timeline id#70
midagedev merged 1 commit into
mainfrom
fix/idempotent-timeline-insert-v2

Conversation

@midagedev

Copy link
Copy Markdown
Owner

What

A repeated test-clock advance failed with UNIQUE constraint failed: timeline_entries.id, returned as a 500.

Timeline ids are deterministic seeds derived from the billing event — a trial activation is keyed by subscription and period end — so replaying one is a no-op, not an error.

Why it matters

The failure was unrecoverable. The clock is moved before the events are processed, so once processing failed the clock stayed ahead while the subscription stayed behind: every later advance retried the same event, collided again, and the fixture was wedged permanently. Fixture packs that reset subscriptions but keep the ledger hit this on their second run.

Found while driving subscription lifecycle transitions (trial expiry, dunning grace, grace expiry) from an end-to-end suite, where the same fixture is exercised on every run.

Change

insertTimeline now guards with ON CONFLICT(id) DO NOTHING.

Test

TestRecordTimelineIsIdempotentForRepeatedEventIDs records the same entry twice and asserts the ledger holds exactly one copy. With the guard removed it fails with the original UNIQUE constraint failed error.

go test ./internal/... passes; gofmt -l internal/ is clean.

🤖 Generated with Claude Code

…meline id

Timeline ids are deterministic seeds derived from the billing event, so replaying
one is a no-op rather than an error. Recording a duplicate raised a UNIQUE
constraint failure surfaced as a 500, and the failure was unrecoverable: the test
clock had already moved, so every later advance retried the same event and
collided again, wedging the fixture permanently.

Fixture packs that reset subscriptions but keep the ledger hit this on their
second run. Guard the insert with ON CONFLICT DO NOTHING and cover it with a test
that fails with the original UNIQUE error when the guard is removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@midagedev
midagedev merged commit 32b0582 into main Aug 20, 2026
2 of 3 checks passed
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