[CLAUDE] Docs: S56 closeout — pre-golive verify + golive-harden + doc-drift + gotcha #58

STATUS/HANDOFF S56 + session log: WF1 pre-golive-verify (7-stream → GO) + WF2 golive-harden (4 fix, code a20cde8 Run #379 PASS). Test 216→228. Code golive-ready; 2 ops VPS pending (IT user + tzutil); FE Phase 2 deferred.

§L closeout (H1/H2): database-agent executed-file→verified-runtime (agents/README:4, D1 closed); ef-core skill 47→48; sys.tables 92→93 reconciled (cicd ground-truth); root CLAUDE test 203→228 + 92→93 bảng; gotcha #58 NEW (EF read-modify-write lost-update→ExecuteUpdate atomic). agent-memory harvest: cicd Run#379 + Fidelity Serializable-correction (impl/test MEMORY, H2 GATE 4.5/5).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-09 20:20:51 +07:00
parent a20cde89fb
commit a62e797332
11 changed files with 108 additions and 20 deletions

View File

@ -1060,6 +1060,18 @@ for h in resp.points: # ← .points không phải iterable trực tiếp
---
### 58. EF read-modify-write lost-update — dùng `ExecuteUpdateAsync` atomic + Serializable tx (Session 56)
**Triệu chứng:** Handler trừ/cộng counter kiểu đọc-sửa-ghi in-memory: `entity.X += n; await SaveChangesAsync()`. 2 request đồng thời (vd 2 lượt duyệt cuối 1 đơn nghỉ, hoặc admin + approver bấm cùng lúc) cùng đọc `X` cũ → cùng `+= n` → lần ghi sau đè lần trước → **mất 1 update** (quota lệch). Im lặng, không exception, không corruption — chỉ sai số. Reachable: `LeaveBalance.UsedDays` trừ phép (S43 gap, fixed S56).
**Root cause:** read-modify-write KHÔNG atomic dưới READ COMMITTED (default). EF tải value vào RAM, tính ở app, ghi lại — cửa sổ race giữa SELECT và UPDATE.
**Fix (proven S56, NO migration):** atomic server-side increment — `db.Set.Where(pred).ExecuteUpdateAsync(s => s.SetProperty(b => b.X, b => b.X + n), ct)`. EF Core 7+ phát `UPDATE SET X = X + @n` 1 lệnh atomic dưới row-lock → 2 increment đồng thời serialize, zero lost-update, BẤT KỂ isolation. ⚠️ `ExecuteUpdate` **bypass change tracker** → tracked instance giữ value CŨ; KHÔNG đọc lại entity đó (dùng `.AsNoTracking()` re-query / `ChangeTracker.Clear()`), KHÔNG thêm `entity.X += n` (double-count). Bọc trong explicit `BeginTransactionAsync(IsolationLevel.Serializable, ct)` để (a) atomic với các write khác cùng handler, (b) serialize nhánh auto-create row mới (2 insert cùng key). Convention codebase = Serializable (codegen `WorkflowAppCodeGen:34`, ProposalFeatures, TravelVehicle).
**References:** `LeaveOtApprovalFeatures.cs:354-405` (ApproveLeaveRequestHandler terminal DaDuyet) · `LeaveBalanceTests.cs` (TwoSeparateRequests accumulate test) · database-agent design S56 (DB11) · surfaced bởi `pre-golive-verify` workflow.
---
## Checklist debug bug mới
1. Build pass không? → fail → check using + package version compat