From 13bd0d9944f8e80f64ffd5ad5441766641428517 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Thu, 12 Mar 2026 19:25:49 +0100 Subject: [PATCH] fix(collections): start agent pool after http server (#8981) Otherwise if using collections with postgresql we create a deadlock, as we need embeddings to be up Signed-off-by: Ettore Di Giacinto --- core/application/application.go | 33 ++++++++++++++++++-------------- core/cli/run.go | 34 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/core/application/application.go b/core/application/application.go index 7ad486c25..1e4a17916 100644 --- a/core/application/application.go +++ b/core/application/application.go @@ -102,19 +102,24 @@ func (a *Application) start() error { a.agentJobService = agentJobService - // Initialize agent pool service (LocalAGI integration) - if a.applicationConfig.AgentPool.Enabled { - aps, err := services.NewAgentPoolService(a.applicationConfig) - if err == nil { - if err := aps.Start(a.applicationConfig.Context); err != nil { - xlog.Error("Failed to start agent pool", "error", err) - } else { - a.agentPoolService = aps - } - } else { - xlog.Error("Failed to create agent pool service", "error", err) - } - } - return nil } + +// StartAgentPool initializes and starts the agent pool service (LocalAGI integration). +// This must be called after the HTTP server is listening, because backends like +// PostgreSQL need to call the embeddings API during collection initialization. +func (a *Application) StartAgentPool() { + if !a.applicationConfig.AgentPool.Enabled { + return + } + aps, err := services.NewAgentPoolService(a.applicationConfig) + if err != nil { + xlog.Error("Failed to create agent pool service", "error", err) + return + } + if err := aps.Start(a.applicationConfig.Context); err != nil { + xlog.Error("Failed to start agent pool", "error", err) + return + } + a.agentPoolService = aps +} diff --git a/core/cli/run.go b/core/cli/run.go index df84ef790..2aa399859 100644 --- a/core/cli/run.go +++ b/core/cli/run.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "net" "os" "path/filepath" "strings" @@ -425,5 +426,38 @@ func (r *RunCMD) Run(ctx *cliContext.Context) error { } }) + // Start the agent pool after the HTTP server is listening, because + // backends like PostgreSQL need to call the embeddings API during + // collection initialization. + go func() { + waitForServerReady(r.Address, app.ApplicationConfig().Context) + app.StartAgentPool() + }() + return appHTTP.Start(r.Address) } + +// waitForServerReady polls the given address until the HTTP server is +// accepting connections or the context is cancelled. +func waitForServerReady(address string, ctx context.Context) { + // Ensure the address has a host component for dialing. + // Echo accepts ":8080" but net.Dial needs a resolvable host. + host, port, err := net.SplitHostPort(address) + if err == nil && host == "" { + address = "127.0.0.1:" + port + } + + for { + select { + case <-ctx.Done(): + return + default: + } + conn, err := net.DialTimeout("tcp", address, 500*time.Millisecond) + if err == nil { + conn.Close() + return + } + time.Sleep(250 * time.Millisecond) + } +}