Repository navigation
fix: normalise time.Time writes to UTC across auth + tiering (closes #460) - #461
Conversation
…460) PR #459 fixed a `WHERE created_at = ?` round-trip bug in the RBAC FSM apply path by making both proposer and applier UTC. Issue #460 audited the rest of the codebase for the same shape. This PR normalises 11 timestamp sites: - internal/auth/cluster_apply.go (3 sites): Phase A token FSM applier now writes UTC. No current WHERE-equality readback pairs with these, but the latent shape matches the one PR #459 fixed for RBAC. - internal/auth/rbac_manager.go (7 sites): RBAC OSS direct-SQLite path now writes UTC. Cluster path already UTC via nextProposerTimestamp; OSS now matches for symmetry and to prevent a future regression if any OSS readback adds equality matching. - internal/tiering/migrator.go (1 site): MigrateFile's startTime now UTC. time.Since(startTime) is location-independent so duration math is unaffected. Adds 3 regression tests, each verified to catch a re-introduced bug by temp-revert experiment: - TestProposer_CreateRole_NonUTCRoundTrip — exercises the RBAC cluster round-trip under TZ=America/Argentina/Buenos_Aires, fails with "sql: no rows in result set" if either proposer or applier drops .UTC() (proposer/applier UTC parity check). - TestOSS_RBACWrites_StampUTC — exercises every OSS-path RBAC Create* + Update* under non-UTC TZ, asserts CreatedAt.Location() == time.UTC; covers the 7 OSS sites this PR fixes. - TestApply_TokenCreate_NonUTC — exercises ApplyCreateToken under non-UTC TZ, reads the raw SQLite text storage via CAST(... AS TEXT), asserts a +00:00 or Z suffix; covers the 3 cluster_apply.go sites.
There was a problem hiding this comment.
Code Review
This pull request standardizes database timestamp writes to UTC across both cluster and OSS-path operations (such as token creation, organization updates, and file migrations) to prevent timezone round-trip issues in SQLite. It also introduces comprehensive regression tests that simulate a non-UTC local timezone environment. The reviewer recommended explicitly checking errors returned by GetOrganization and GetTeam in the tests to avoid silent failures if those lookups fail.
Gemini PR #461 round 1: the `if got != nil && ...UpdatedAt.Location() != time.UTC` shape silently skipped the assertion if the GetX call returned (nil, err) — a readback failure would make the test pass by accident instead of fail loudly. Now t.Fatalf on the error first, then assert directly on `got.UpdatedAt.Location()`.
|
@gemini-code-assist please review |
There was a problem hiding this comment.
Code Review
This pull request ensures that timestamps are consistently handled and stored in UTC across the authentication, RBAC, and tiering components to prevent timezone-related round-trip issues with SQLite. Specifically, it updates various database insertion and update operations to use UTC time, and introduces comprehensive regression tests to verify this behavior when running under non-UTC local timezones. I have no feedback to provide as there are no review comments.
Summary
WHERE created_at = ?round-trip bug in the RBAC FSM apply path under non-UTC server timezone. This PR normalises 11 more timestamp sites inauth+tieringto UTC for consistency, paired with 3 regression tests that catch this whole class of bug under a non-UTCtime.Local.Why
Go's SQLite driver text-encodes
time.Timeusing the value's location, so byte-for-byte equality (WHERE created_at = ?) only matches if both sides agree on timezone. PR #459 fixed the RBAC path. The rest of the codebase had the same shape — no current bug because the other paths don't do equality readback, but the latent shape would bite the next time a readback got added.What changed
Timestamps normalised to UTC (11 sites)
internal/auth/cluster_apply.gointernal/auth/rbac_manager.gonextProposerTimestamp; OSS now matches for symmetryinternal/tiering/migrator.goMigrateFilestartTime)time.Since(startTime)is location-independent, but matches the rest ofinternal/tiering/metadata.goNew regression tests (3)
Each forces
time.Local = America/Argentina/Buenos_Airesand exercises the affected path. Each was verified to catch a re-introduced bug via temp-revert experiment:TestProposer_CreateRole_NonUTCRoundTrip— RBAC cluster round-trip (proposer + applier UTC parity). Fails withsql: no rows in result setif either side drops.UTC().TestOSS_RBACWrites_StampUTC— OSS-path RBAC Create* + Update* — assertsCreatedAt.Location() == time.UTC. Covers the 7 OSS sites this PR fixes.TestApply_TokenCreate_NonUTC— Phase A token applier — reads raw SQLite text viaCAST(... AS TEXT), asserts UTC offset suffix. Covers the 3cluster_apply.gosites.Tests skip gracefully via
t.Skipfif tzdata is missing (e.g. minimal Alpine).Internal review process
t.Cleanupordering). All 3 addressed in this commit before opening.time.Time → SQLitesite acrossauth/,audit/,cluster/,database/,license/,tiering/. The auditor undercounted RBAC OSS sites (said 4, actually 7) — I caught the miss during the matrix step.Test plan
go build ./cmd/... ./internal/...cleango test -race -count=1 ./internal/auth/... ./internal/tiering/... ./internal/cluster/...cleangofmt -l+go vetclean on touched files🤖 Generated with Claude Code