fix(ollama): 测试仓储到期查询深拷贝消除数据竞争

ollamaUsageTestRepo.ListDueOllamaCloudUsageAccounts 返回浅拷贝共享
Extra/Credentials map,RunDue 过滤循环的无锁读与组写协程在 r.mu 下的
map 删改构成数据竞争(-race 可复现;无 -race 时也可能触发 runtime
concurrent map read/write fatal 导致 CI 偶发崩溃)。两条返回路径改为
r.mu 下 mergeMap 深拷贝,并补全 ListOllamaCloudUsageGroupAccounts
只拷 Extra 不拷 Credentials 的半克隆,与基类 GetByID 惯例对齐。
生产仓储每次查询返回全新行,不受影响。
This commit is contained in:
shaw
2026-07-23 17:14:10 +08:00
parent 2faa0891e4
commit 1be6f30188
@@ -54,16 +54,19 @@ func (r *ollamaUsageTestRepo) ListOllamaCloudUsageGroupAccounts(_ context.Contex
if _, match := wanted[fingerprint]; !ok || !match {
continue
}
clone := *account
clone.Extra = make(map[string]any, len(account.Extra))
for key, value := range account.Extra {
clone.Extra[key] = value
}
result = append(result, clone)
result = append(result, cloneOllamaUsageTestAccount(*account))
}
return result, nil
}
// cloneOllamaUsageTestAccount 深拷贝共享 map,模拟真实仓储每次查询返回全新行:
// 组写在 r.mu 下改成员 map,浅拷贝会让 RunDue 过滤循环无锁读到同一 map 而竞争。
func cloneOllamaUsageTestAccount(account Account) Account {
account.Credentials = mergeMap(nil, account.Credentials)
account.Extra = mergeMap(nil, account.Extra)
return account
}
func (r *ollamaUsageTestRepo) SaveOllamaCloudUsageSession(_ context.Context, expected *Account, ciphertext string, autoRefresh bool) error {
r.mu.Lock()
defer r.mu.Unlock()
@@ -183,14 +186,18 @@ func applyOllamaUsageTestManagedExtra(account, source *Account) {
}
func (r *ollamaUsageTestRepo) ListDueOllamaCloudUsageAccounts(_ context.Context, _ time.Time, limit int) ([]Account, error) {
if len(r.due) > 0 {
return append([]Account(nil), r.due[:min(limit, len(r.due))]...), nil
}
r.mu.Lock()
defer r.mu.Unlock()
if len(r.due) > 0 {
out := make([]Account, 0, min(limit, len(r.due)))
for _, account := range r.due[:min(limit, len(r.due))] {
out = append(out, cloneOllamaUsageTestAccount(account))
}
return out, nil
}
out := make([]Account, 0, len(r.accounts))
for _, account := range r.accounts {
out = append(out, *account)
out = append(out, cloneOllamaUsageTestAccount(*account))
if len(out) == limit {
break
}