3e7fcce0b1
build / build (push) Successful in 1m11s
新增迁移50与每5分钟/手动持久同步,API只读本地;同步失败保留、权限阻断、租约与质量失效原子激活。 保留全量稳定分页/固定列,已存质量批量读取,元数据不反复解压长答案;详情安全Markdown与原文。 已运行整仓 make verify、源与副本隔离PG -race、真实只读源及隔离浏览器。 实际同步2525条;固定窗口640条本地中位1.46秒,对照直接源33.10秒。 停worker、缺源凭据、重启API后仍可查询;未更新8088,未修改生产原问答。
72 lines
1.8 KiB
Go
72 lines
1.8 KiB
Go
package qaopsmirror
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
pg "knowledge-governance/internal/platform/postgres"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLocalMirrorDurableQueueCoalescesAndAudits(t *testing.T) {
|
|
p := mirrorFixture(t)
|
|
s := NewStore(p, "fixture-queue")
|
|
q := pg.NewStore(p)
|
|
ctx := context.Background()
|
|
if _, _, e := s.EnqueueSync(ctx, "fixture-no-worker", "fixture-admin"); !errors.Is(e, ErrWorkerUnavailable) {
|
|
t.Fatal("offline worker accepted work", e)
|
|
}
|
|
if _, e := q.Claim(ctx, "fixture-worker", []string{s.SyncJobType()}, time.Now(), time.Minute); e != nil {
|
|
t.Fatal(e)
|
|
}
|
|
var wg sync.WaitGroup
|
|
ids := make(chan string, 8)
|
|
errs := make(chan error, 8)
|
|
for range 8 {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
r, _, e := s.EnqueueSync(ctx, "fixture-same-action", "fixture-admin")
|
|
if e != nil {
|
|
errs <- e
|
|
return
|
|
}
|
|
ids <- r.JobID
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
close(errs)
|
|
close(ids)
|
|
for e := range errs {
|
|
t.Fatal("durable enqueue failed", e)
|
|
}
|
|
id := ""
|
|
n := 0
|
|
for got := range ids {
|
|
if got == "" || id != "" && got != id {
|
|
t.Fatal("same action created different effects")
|
|
}
|
|
id = got
|
|
n++
|
|
}
|
|
if n != 8 {
|
|
t.Fatal("lost enqueue results")
|
|
}
|
|
again, _, e := s.EnqueueSync(ctx, "fixture-another-click", "fixture-admin")
|
|
if e != nil || !again.Deduplicated || again.JobID != id {
|
|
t.Fatal("in-flight synchronization did not coalesce", e)
|
|
}
|
|
if _, _, e = s.EnqueueSync(ctx, "fixture-same-action", "different-admin"); !errors.Is(e, ErrConflict) {
|
|
t.Fatal("replay changed actor", e)
|
|
}
|
|
var count int
|
|
if p.QueryRow(ctx, `SELECT count(*) FROM jobs WHERE type=$1`, s.SyncJobType()).Scan(&count) != nil || count != 1 {
|
|
t.Fatal("duplicate jobs")
|
|
}
|
|
job, e := q.Claim(ctx, "fixture-worker", []string{s.SyncJobType()}, time.Now(), time.Minute)
|
|
if e != nil || job == nil || job.ID != id {
|
|
t.Fatal("job not durable", e)
|
|
}
|
|
}
|