fix(genesis-writer): emit release_date as RFC3339, not Postgres text - #519
Merged
Merged
Conversation
The tracks query selected release_date::text, which yields Postgres's own "2026-09-06 22:06:00". The indexer's parseReleaseDate accepts RFC3339, RFC3339Nano and "Mon Jan 02 2006 15:04:05 GMT-0700" -- none of which match -- so releaseDateOrDefault silently fell back to block time, i.e. the source row's created_at. That is not a cosmetic date difference. A track whose release_date lands in the past is then picked up by ScheduledReleasePublisher, which sets is_unlisted = false and updated_at = now(). Measured on the 2026-08-07 snapshot: 372 unlisted tracks with a future release_date had the date rewritten 368 of them were published early 498 -> 136 tracks still holding a future release_date Verified end to end on track 2073330890: source release_date 2026-09-06 22:06:00 and is_unlisted true, the transaction carried both correctly, and the indexed row came out release_date 2025-08-10 20:57:26 (exactly its created_at) with is_unlisted false. Every other timestamp the writer emits already goes out as RFC3339; this one was the outlier.
Merged
rickyrombo
pushed a commit
that referenced
this pull request
Aug 20, 2026
time.RFC3339 has no fractional-second component, so every release_date carrying microseconds was rounded down to the whole second on the way out. A full-snapshot replay compared against its source showed 26,129 current tracks affected, e.g. 2026-02-02T15:53:12.050585Z emitted as 2026-02-02T15:53:12Z. The indexer already accepts RFC3339Nano, and a value with no fractional part formats identically under both layouts, so rows without microseconds are unchanged. This is a different failure from the one #519 fixed. That emitted Postgres text format, which no accepted layout matched, so the indexer fell back to block time. This parses cleanly and quietly rounds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rickyrombo
added a commit
that referenced
this pull request
Aug 20, 2026
time.RFC3339 has no fractional-second component, so every release_date carrying microseconds was rounded down to the whole second on the way out. A full-snapshot replay compared against its source showed 26,129 current tracks affected, e.g. 2026-02-02T15:53:12.050585Z emitted as 2026-02-02T15:53:12Z. The indexer already accepts RFC3339Nano, and a value with no fractional part formats identically under both layouts, so rows without microseconds are unchanged. This is a different failure from the one #519 fixed. That emitted Postgres text format, which no accepted layout matched, so the indexer fell back to block time. This parses cleanly and quietly rounds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rickyrombo
added a commit
that referenced
this pull request
Aug 20, 2026
…tamp (#534) * fix(genesis-writer): keep sub-second precision on release_date time.RFC3339 has no fractional-second component, so every release_date carrying microseconds was rounded down to the whole second on the way out. A full-snapshot replay compared against its source showed 26,129 current tracks affected, e.g. 2026-02-02T15:53:12.050585Z emitted as 2026-02-02T15:53:12Z. The indexer already accepts RFC3339Nano, and a value with no fractional part formats identically under both layouts, so rows without microseconds are unchanged. This is a different failure from the one #519 fixed. That emitted Postgres text format, which no accepted layout matched, so the indexer fell back to block time. This parses cleanly and quietly rounds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(genesis-writer): keep sub-second precision on every emitted timestamp time.RFC3339 has no fractional-second component, so every timestamp the writer emitted was rounded down to the whole second. Verified against a full-snapshot replay: track_downloads 78,032 of 78,032 source rows carry sub-second created_at; the migrated table has zero subscriptions 1,022,554 playlist_tracks 1,014,903 saves 380,604 reposts 361,803 follows 104,614 comments 7,530 tracks.release_date 26,129 created_at is not cosmetic here: the indexer replays each migrated row as of its created_at (migrationBlockTime), and parity keys track_downloads on it -- which is why every sampled row of that table reported as missing rather than mismatched. Safe for every reader. parseMigrationTimestamp tries RFC3339Nano before RFC3339, and Go's parser accepts a fractional second against a layout that lacks one, so event_create.go's stricter RFC3339 parse still succeeds. A value with no fractional part formats identically under both layouts, so rows without microseconds are untouched. The UTC guard now matches both layouts, so reintroducing the truncating form still trips its .UTC() half. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Unreleased tracks are published early
The tracks query selected
release_date::text, which yields Postgres's own2026-09-06 22:06:00. The indexer'sparseReleaseDateaccepts RFC3339,RFC3339Nano and
Mon Jan 02 2006 15:04:05 GMT-0700— none of which match — soreleaseDateOrDefaultsilently fell back to block time (the source row'screated_at).That is not a cosmetic date difference. One format mismatch produces three symptoms:
release_dateis rewritten from the future into the pastScheduledReleasePublisherthen matchesis_unlisted = true AND is_scheduled_release = true AND release_date <= now()and setsis_unlisted = falseupdated_at = now(), which is why some rows carried the replay wall-clockMeasured on the 2026-08-07 snapshot
is_unlistedflipped)Verified end to end — track
2073330890Fix
Scan
release_dateas a timestamp and format it RFC3339. Every other timestampthe writer emits already goes out as RFC3339 — this one was the outlier.
TestReleaseDateIsEmittedInAnAcceptedLayoutasserts the output parses under thelayout the indexer actually uses, and that the Postgres text shape cannot come back.
🤖 Generated with Claude Code