per-doc 锁只串行同一文档,不同 docID 仍裸读写 memDocs 等 map。GetDocument 与另一文档写入撞上时运行时 fatal,代理把全部 API 变成 500。
50 lines
1.2 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|