Repository navigation
fix(worker): lock the risk while resolving its evidence links - #490
gusfcarvalho merged 5 commits into
Conversation
Two streams of one risk pass at the same time. Each job deletes its own link and counts the rest; under READ COMMITTED neither sees the other's uncommitted delete, so both count one link left and neither remediates the risk. It stays open with no link, where no later passing evidence can reach it. The test runs the second job between the first job's count and its commit, and fails until the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQ3hHdBbTmRTNGECpX8JK
resolveRiskEvidenceLink now takes the risk's id and locks the risk with SELECT ... FOR UPDATE before it deletes its stream's link and counts the rest, so the resolution of the risk's other stream counts only after this one commits, and the last one remediates the risk. It also acts on the status read under the lock rather than the one the caller read earlier: a risk closed or remediated in the meantime is left alone, and an accepted risk keeps its status (the evidence_recovered event is emitted). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rQ3hHdBbTmRTNGECpX8JK
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes. ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
gusfcarvalho
left a comment
There was a problem hiding this comment.
ccf-review: REQUEST_CHANGES
Note
🤖 This review was generated by the CCF Review skill, performed with LLMs (Claude). A CCF maintainer read and triaged every finding before posting; severities come from the shared CCF review rule catalog, not from the model's judgement. If anything looks wrong, please reply on the thread.
1 Must-fix, 1 Should-fix.
|
Hi @puzza007 ! Thanks for this fix! There's just one test case we might be missing - and one nit :) |
Resolution checked "closed or remediated" twice, on its unlocked read and under the risk lock, apart from the risks package's own helper. Export IsTerminalRiskStatus and use it at both sites, so a new terminal status cannot reach one check and miss the other. Lock the risk the way the rest of the code does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolution now acts on the status it reads under the risk lock. Change the status right after its unlocked read of the linked risks: a closed risk keeps its link and stays closed, and an accepted risk loses its link, stays accepted and gets an evidence_recovered event. Both fail on the code before the lock, which remediated the risk from its stale status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hey @gusfcarvalho! Hopefully the last two commits should do it |
gusfcarvalho
left a comment
There was a problem hiding this comment.
ccf-review: APPROVE
Note
🤖 This review was generated by the CCF Review skill, performed with LLMs (Claude). A CCF maintainer read and triaged every finding before posting; severities come from the shared CCF review rule catalog, not from the model's judgement. If anything looks wrong, please reply on the thread.
no findings.
Re-review at c93cc32: both earlier threads are fixed and resolved.
- CORE-TEST-001:
TestResolutionLeavesRiskClosedMeanwhileandTestResolutionKeepsRiskAcceptedMeanwhilecover the status re-read under the lock. - CORE-DUP-001: both worker sites now use
risks.IsTerminalRiskStatus.
Verified locally: build, vet, lint and swag are clean, and TestRiskResolutionRaceIntegrationSuite passes. With main's worker code, all three tests fail as expected. Thanks for the fix!
|
yup those would do! Thanks! <3 |
🤖 I have created a release *beep* *boop* --- ## [0.21.1](v0.21.0...v0.21.1) (2026-10-14) ### Bug Fixes * **deps:** update module github.com/moby/go-archive to v0.3.0 [security] ([#492](#492)) ([e71ddf2](e71ddf2)) * **deps:** update module golang.org/x/crypto to v0.56.0 [security] ([#494](#494)) ([9b8abe7](9b8abe7)) * **deps:** update module golang.org/x/mod to v0.40.0 [security] ([#495](#495)) ([f968a6b](f968a6b)) * **deps:** update module golang.org/x/net to v0.60.0 [security] ([#501](#501)) ([bdd97c6](bdd97c6)) * **worker:** lock the risk while resolving its evidence links ([#490](#490)) ([b9958ae](b9958ae)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: ccf-release-bot[bot] <253916425+ccf-release-bot[bot]@users.noreply.github.com>
Bug
When a risk's evidence streams pass, the worker deletes that stream's link to the risk, counts the risk's remaining links, and marks the risk remediated if none are left. If two passing streams for the same risk are processed at the same time, each job deletes its own link but still sees the other's link, because the other job's delete hasn't committed yet. Neither job remediates the risk, so it stays open with no links left, and no later evidence can close it.
Fix
resolveRiskEvidenceLinknow locks the risk row (SELECT … FOR UPDATE) at the start of its transaction. The second job waits until the first commits, then counts zero links and remediates the risk. It also acts on the status read under the lock, so a risk closed or remediated in the meantime is left alone.Test
TestConcurrentPassingStreamsRemediateTheRisk(integration) uses a GORM callback to start the second job right after the first job's link count. Onmainit fails with the risk stillopenand no links. With the fix it passes. The test is in the first commit and the fix in the second.Summary by CodeRabbit