fix: Prevent deadlock in runComponent when Prepare fails (#45609)

issue: #45068
When component.Prepare() fails (e.g., net listener creation error), the
sign channel was never closed, causing runComponent to block
indefinitely at <-sign. This resulted in the entire process hanging
after logging the error message.

Changes:
- Move close(sign) to defer statement in runComponent goroutine
- Ensures sign channel is always closed regardless of success/failure
- Allows proper error propagation through future.Await() mechanism

---------

Signed-off-by: Wei Liu <wei.liu@zilliz.com>
This commit is contained in:
wei liu 2025-11-18 15:41:40 +08:00 committed by GitHub
parent b734de5398
commit 7708abd8fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -114,18 +114,29 @@ func runComponent[T component](ctx context.Context,
) *conc.Future[component] { ) *conc.Future[component] {
sign := make(chan struct{}) sign := make(chan struct{})
future := conc.Go(func() (component, error) { future := conc.Go(func() (component, error) {
factory := dependency.NewFactory(localMsg) // Wrap the creation and preparation phase to enable concurrent component startup
var err error prepareFunc := func() (component, error) {
role, err := creator(ctx, factory) defer close(sign)
factory := dependency.NewFactory(localMsg)
var err error
role, err := creator(ctx, factory)
if err != nil {
return nil, errors.Wrap(err, "create component failed")
}
if err := role.Prepare(); err != nil {
return nil, errors.Wrap(err, "prepare component failed")
}
healthz.Register(role)
metricRegister(Registry.GoRegistry)
return role, nil
}
role, err := prepareFunc()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "create component failed") return nil, err
} }
if err := role.Prepare(); err != nil {
return nil, errors.Wrap(err, "prepare component failed") // Run() executes after sign is closed, allowing components to start concurrently
}
close(sign)
healthz.Register(role)
metricRegister(Registry.GoRegistry)
if err := role.Run(); err != nil { if err := role.Run(); err != nil {
return nil, errors.Wrap(err, "run component failed") return nil, errors.Wrap(err, "run component failed")
} }