6e67034fbd
build / build (push) Successful in 1m10s
以治理库单一只读事务分开聚合累计、自然周、截止月及当前通讯录使用分母;浏览器不再读取全量长答案来构建看板。 新增迁移52与普通worker周质量冻结,保留真实冻结时点、规则和标量贡献;缺少当时快照留空。提交后冻结头与贡献不可修改、删除或追加;只读接口保留管理员、固定租户、严格JSON与输入冲突保护。 已验证:make verify退出0,686个Go用例和305个前端用例通过,必需跳过0;镜像与组织隔离PostgreSQL竞态回归退出0。真实治理副本浏览器核对第41周累计1885与新增471、十一项和完整PNG下载,390px无外溢、JS异常0;明确模拟503后不伪零且重试恢复。没有修改DobotAI原始问答、真实区域规则或生产发布门禁。
53 lines
1.8 KiB
Go
53 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"knowledge-governance/internal/dobotaiops"
|
|
"knowledge-governance/internal/jobs"
|
|
"knowledge-governance/internal/qaopsmirror"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func configureRecordsSync(pool *pgxpool.Pool, getenv func(string) string, governanceDSN string, handlers map[string]jobs.Handler) (*qaopsmirror.Store, error) {
|
|
settings := dobotaiops.LoadSettings(getenv, governanceDSN)
|
|
if !settings.Enabled {
|
|
return nil, nil
|
|
}
|
|
if settings.TenantKey == "" || len(settings.TenantKey) > 128 || strings.ContainsRune(settings.TenantKey, 0) {
|
|
return nil, qaopsmirror.ErrInvalid
|
|
}
|
|
store := qaopsmirror.NewStore(pool, settings.TenantKey)
|
|
handlers[store.SyncJobType()] = func(ctx context.Context, job jobs.Job) error {
|
|
return store.HandleSync(ctx, job, func(ctx context.Context) (qaopsmirror.Source, error) {
|
|
current := dobotaiops.LoadSettings(getenv, governanceDSN)
|
|
if !current.Enabled || current.Problem != "" || current.TenantKey != settings.TenantKey {
|
|
return nil, dobotaiops.ErrUnavailable
|
|
}
|
|
// One source connection, shared by every manifest and bounded payload read in this job.
|
|
current.Connection.MaxConns = 1
|
|
current.Connection.MinConns = 0
|
|
return dobotaiops.Build(ctx, current.Connection)
|
|
})
|
|
}
|
|
return store, nil
|
|
}
|
|
|
|
// The next due time and automatic operation key live in PostgreSQL, so a worker
|
|
// restart does not reset the five-minute cadence or duplicate an active import.
|
|
func runRecordsSyncScheduler(ctx context.Context, store *qaopsmirror.Store, onError func(error)) {
|
|
tick := time.NewTicker(15 * time.Second)
|
|
defer tick.Stop()
|
|
for {
|
|
if e := runRecordsSyncCycle(ctx, store, time.Now()); e != nil && ctx.Err() == nil && onError != nil {
|
|
onError(e)
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-tick.C:
|
|
}
|
|
}
|
|
}
|