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