Repository navigation
fix(project): serialize concurrent project updates - #202
Aether-254 wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
(resolved)
The FOR UPDATE approach fixes the lost-update problem between concurrent UpdateProject requests, but it introduces an inconsistent lock order with DeleteProject.
UpdateProject now effectively locks in this order:
projects -> project_tags -> project_items
while DeleteProject currently does:
project_tags -> project_items -> projects
This can deadlock when an update and a delete for the same project run concurrently. For example, the update can hold the project row while waiting on tag rows, while the delete holds the tag rows and later waits on the project row.
Could we make DeleteProject acquire the same project-row FOR UPDATE lock at the beginning of its transaction before touching tags/items? Ideally the receiver_id check should also be performed after acquiring that lock so the delete decision is made against the serialized state.
It would also be useful to add a concurrency regression test covering two simultaneous updates to ensure TotalItems cannot lose increments.
The serialization added here otherwise looks correct for concurrent UpdateProject calls.
例行检查
关联信息
无
变更内容
SELECT ... FOR UPDATE加行锁。TotalItems基于旧值计算产生覆盖。变更原因
UpdateProject原先在进入事务前加载项目数据,并在事务内通过project.TotalItems += actualItemsCount更新数量。当多个请求并发更新同一项目时,它们可能读取到相同的旧
TotalItems,随后分别计算并保存,导致后提交的请求覆盖前一个请求的计数结果,使TotalItems与实际项目内容数量不一致。通过在事务内重新读取并锁定项目记录,可以确保同一项目的并发更新按顺序执行,并基于最新状态计算项目数量。