fix(admin): stop a slow tag-write errors read from overwriting newer errors - #952
Merged
Merged
Conversation
…errors loadTagWriteErrors had the race loadTable had before #949. The Tagging tab and the settings button read the list unforced when they mount, and the WebSocket forces a reload whenever it changes. If a slow mount read landed after the forced reload, it put the older list back and stamped it when it arrived, so every unforced read for the next 5 s was served the stale list. clearTagWriteErrors wrote [] directly, so a read that went out before the clear could put the cleared errors back the same way. Both now use loadTable's request counter. #949's landing check moves into a claimLanding(key, request) helper that all three share, keyed like timestamps. The tag-write reads take a number when they go out, land only if no later request has landed, and stamp the time they went out. The clear takes a number when its DELETE goes out, so an older read can't undo it, and a read that went out after it and already landed isn't wiped by it. The singleton settings loads (tagging defaults, email, OIDC, site defaults, throttling) are unchanged. Nothing forces them, and their only writer is the same tab's own Save. That can't realistically fire before the tab's mount read comes back. Co-Authored-By: Claude Opus 5.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.
Problem
loadTagWriteErrorsinfrontend/src/stores/admin.jshad the same race that #949 fixed forloadTable. It didn't check the order its responses came back in, and it stamped the list when the response arrived.loadTagWriteErrors()unforced. Read A is in flight.TAG_WRITE_ERRORS_CHANGED, andsocket.jsforces reload B, which lands the new list.clearTagWriteErrorswrote[]directly, so it had the same problem. A read that went out before the clear and landed after it put the cleared errors back.Fix
fix(admin): stop a slow table read from overwriting newer rows #949's landing check becomes a small
claimLanding(key, request)helper.loadTable,loadTagWriteErrorsandclearTagWriteErrorsall use it, with the sametableRequestscounter.landedis keyed liketimestamps, andTagWriteErrorsis one of those keys.Tag-write reads take a request number when they go out. They land only if no later request has landed, and they stamp the time they went out.
The clear counts as a request. It takes its number when the DELETE goes out, not when the response comes back:
TAG_WRITE_ERRORS_CHANGED, so this session's own forced reload can land first.If the number were taken on arrival, that later reload would be dropped.
The store id, state shape and member names are unchanged. The counter stays module-level.
Singleton loaders: not changed
loadTaggingDefaults,loadEmailSettings,loadOidcSettings,loadSettingsDefaultsandloadThrottleSettingsare only called unforced on mount, and nothing forces them: no WebSocket message reloads them. Their only writer is the same tab's own Save. It could race a mount read only when the tab remounts after its 5 s cache has expired and an admin edits and saves before that read comes back. A save on a first mount isn't possible: every one of these tabs shows a spinner until its settings load. Two overlapping unforced reads both return the same server state, so their order doesn't matter. I found no realistic overlap, so I left them as they are.Tests
The new
frontend/tests/unit/admin-tag-write-errors-store.test.jsuses deferred promises, like theloadTable response orderblock:[], stamped with the clear's request time.Six of the eight cases fail against the old store code. The two "still lands an earlier read" cases pass on both versions: they guard against the fix dropping too much.
bunx vitest run: 89 files, 930 tests passedbunx eslintandbunx prettier --checkon the changed files: clean🤖 Generated with Claude Code