fix: cache the project registration created, not a partial copy of it - #363
Open
FrameAutomata wants to merge 2 commits into
Open
fix: cache the project registration created, not a partial copy of it#363FrameAutomata wants to merge 2 commits into
FrameAutomata wants to merge 2 commits into
Conversation
Register and the SSO finish-setup handler both cached a five-field copy of the project they had just created instead of the project itself, so the cached row disagreed with the database on every field the copy omitted: - DropHealthyHealthchecks is true on a new project but false in the copy, and UseClientAuth hands that cached object straight to FilterHealthchecks, so healthy healthchecks were ingested rather than dropped. - CreateWithOrganization mints a source map token for ios projects; the copy left it nil, so GetBySourceMapToken missed and the first symbol upload 401d. - CreatedAt stayed zero, which reorders GetAll(). On the PostgreSQL build the project_cache_changed notification triggers a full refresh that overwrites the bad entry within milliseconds, so this only persisted on the SQLite and DuckDB builds, where NotifyProjectCacheChanged is a no-op and nothing refreshes the cache after boot. It lasted until the process restarted or someone saved the project's settings. Both call sites also ran under middleware.Transactional and mutated the cache before the commit, so a later failure in the same handler rolled the transaction back and left a project in the cache that does not exist. Queue the write with middleware.OnCommit instead, matching what project_batch.go already does. ToProjectWithBackendUrl takes a value receiver and copies, so passing the repository's own pointer aliases nothing the handler goes on to mutate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7f9Ad34w2iGg3mqangVsw
Follow-up to the previous commit, from a review pass over it. middleware.OnCommit and runCommitHooks had no tests at all, so the property the cache fix depends on — a queued side effect fires only once its write is durable — was unverified. Moving runCommitHooks onto the rollback branch left the whole suite green. Cover both outcomes and the ordering. Also from the review: - Assert the cached project against every field of the row rather than the four a past bug happened to drop. The old length-only check on AiFlaggedLanguages passed for a same-length but different pack, which changes which flagged-term packs ingest scans. - Register the ProjectCache cleanup before issuing the request. The commit hook inserts the entry during ServeHTTP, so a t.Fatal between the request and the old cleanup registration leaked an entry pointing at a project whose in-memory database was about to close. - Move initRegistrationJWT into setup_test.go, which owns the shared harness, now that TestRegisterWithoutProject calls it. Its inline copy is gone. - Widen the OnCommit doc comment: three of its five callers are now cache writes, and "visible to other connections" read as excluding an in-process map. Note that only routes carrying the middleware run queued hooks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7f9Ad34w2iGg3mqangVsw
This was referenced Sep 6, 2026
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.
Found while verifying #317. Unrelated to the multi-replica staleness that issue is about — this one only bites the single-instance builds.
The bug
Registerand the SSOFinishSetuphandler both cached a hand-built five-field copy of the project they had just created, instead of the project itself:Every field the copy omits then disagrees with the row, and
UseClientAuthhands that cached object straight to the ingest path:DropHealthyHealthchecksistrueon a new project butfalsein the copy, soFilterHealthchecks→ShouldDropHealthcheckreturnedfalseand healthy healthchecks were ingested rather than dropped for every newly registered project.SourceMapToken—CreateWithOrganizationmints one foriosprojects; the copy left itnil, soGetBySourceMapTokenmissed and the first symbol upload 401'd.CreatedAtstayed zero, which reordersGetAll().On the PostgreSQL build the
project_cache_changednotification triggers a fullRefresh()that overwrites the bad entry within milliseconds, so this only persisted on the SQLite and DuckDB builds, whereNotifyProjectCacheChangedis a no-op and nothing refreshes the cache after boot. It lasted until the process restarted or someone saved the project's settings (UpdateProjectcopies the field and heals it by accident).Both call sites also ran under
middleware.Transactionaland mutated the cache before the commit.Registercan stillAbortWithErrorafterwards atFindByUserIdWithRoles, andFinishSetupatGenerateToken— either rolls the transaction back and leaves a project in the cache that does not exist in the database.The fix
Pass the repository's own project and queue the write with
middleware.OnCommit, matching whatproject_batch.goalready does:ToProjectWithBackendUrltakes a value receiver and copies, so passing the pointer aliases nothing the handler goes on to mutate.Tests
project_cache_registration_test.goasserts the cached project matches the row field for field, and that the handler performs no cache write of its own. Reverting the fix fails it on all five symptoms:The second commit adds
commit_hooks_test.go, becauseOnCommit/runCommitHookshad no coverage at all — the property this fix depends on was unverified, and movingrunCommitHooksonto the rollback branch left the whole suite green. It now fails withhook ran 1 times, want 0. That commit also widens theOnCommitdoc comment, since three of its five callers are now cache writes and "visible to other connections" read as excluding an in-process map.Verification
gofmt,go vet ./...andgo test -race -count=1 ./...are clean, and all three supported tag combinations build (default,transactional_pg telemetry_ch,telemetry_duckdb).Not included
Two pre-existing problems in
Transactionalthat the review surfaced, neither introduced nor worsened here, both worth their own issue: a failedCommit()still delivers the handler's already-written 2xx (so a client can store a valid JWT for a user that was never persisted), andrunCommitHookshas no per-hook recover, so one panicking hook silently skips the rest.🤖 Generated with Claude Code
https://claude.ai/code/session_01S7f9Ad34w2iGg3mqangVsw
Generated by Claude Code