Files
weichen a90cce73b9 fix(docgov): 保护共享内存 map,避免并发读写把进程打崩
per-doc 锁只串行同一文档,不同 docID 仍裸读写 memDocs 等 map。GetDocument 与另一文档写入撞上时运行时 fatal,代理把全部 API 变成 500。
2026-09-28 08:52:44 +00:00

50 lines
1.2 KiB
Go

package docgov
import (
"context"
"fmt"
"sync"
"testing"
)
// TestMemMapsNoRaceAcrossDocs 复现 2026-09-28 dev 崩溃:不同 docID 并发读写共享 map。
// 修复前 go test -race 会报告 concurrent map read/write(运行时则 fatal)。
func TestMemMapsNoRaceAcrossDocs(t *testing.T) {
e := NewGovernanceEngine(nil)
ctx := context.Background()
const n = 48
var wg sync.WaitGroup
wg.Add(n * 3)
for i := 0; i < n; i++ {
id := fmt.Sprintf("race-doc-%d", i)
go func() {
defer wg.Done()
if _, err := e.CreateOrGetDocument(ctx, id, int64(i+1), "task", "title", "stage", "", 1, "author", 2, "reviewer", 3, "approver"); err != nil {
t.Errorf("create %s: %v", id, err)
}
}()
go func() {
defer wg.Done()
_, _ = e.GetDocument(ctx, id)
_, _ = e.ListAuditLogs(ctx, id)
_, _ = e.ListComments(ctx, id)
}()
go func() {
defer wg.Done()
_ = e.addCommentInternal(id, 1, "author", RoleAuthor, "", CommentSuggestion, "note")
}()
}
wg.Wait()
for i := 0; i < n; i++ {
id := fmt.Sprintf("race-doc-%d", i)
doc, err := e.GetDocument(ctx, id)
if err != nil {
t.Fatalf("get %s: %v", id, err)
}
if doc.DocID != id {
t.Fatalf("doc id = %s, want %s", doc.DocID, id)
}
}
}