package main import ( "context" "log" "time" "knowledge-governance/internal/appconfig" "knowledge-governance/internal/libraryexecutor" postgresstore "knowledge-governance/internal/platform/postgres" ) // 为什么由 worker 观测、页面读库:agent socket 只挂载在 worker 容器里,API 那个 // LIBRARY_AGENT_SOCKET 即使配了也 dial 不到。谁真能执行就由谁写下观测, // 否则页面上的"可发起"只是配置字符串的回声。 const libraryExecutorProbeInterval = 30 * time.Second // libraryExecutorProbeTimeout 是一次 health 往返的上限:它只是同 uid unix socket // 上的一次 JSON 往返,慢到秒级就说明执行器有问题,不该拖住心跳。 const libraryExecutorProbeTimeout = 3 * time.Second func recordLibraryExecutorStatus(ctx context.Context, store *postgresstore.Store, config appconfig.Config) { // 归属用**封存源**实例:agent 冻结的是源库写入者,和同步目标(kgseal)不是一套。 instance := config.SourceInstance() if instance == "" { // 没有实例身份,观测就无处归属:写进去只会让页面拿别的实例的状态。 log.Print("library executor probe skipped: LIBRARY_SOURCE_INSTANCE is not configured") return } observation := libraryexecutor.Probe(ctx, instance, config.LibraryAgentSocket, libraryExecutorProbeTimeout, nil) if err := store.UpsertLibraryExecutorStatus(ctx, observation, config.WorkerID, time.Now().UTC()); err != nil { log.Printf("library executor status not recorded: %v", err) } } // runLibraryExecutorProbe 先观测一次再进循环:页面在 worker 刚起来时就该有真实状态。 func runLibraryExecutorProbe(ctx context.Context, store *postgresstore.Store, config appconfig.Config, interval time.Duration) { if interval <= 0 { interval = libraryExecutorProbeInterval } recordLibraryExecutorStatus(ctx, store, config) ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: recordLibraryExecutorStatus(ctx, store, config) } } }