From 7708abd8fe456820a84ccdfadc03321ed14763af Mon Sep 17 00:00:00 2001 From: wei liu Date: Tue, 18 Nov 2025 15:41:40 +0800 Subject: [PATCH] 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 --- cmd/roles/roles.go | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/cmd/roles/roles.go b/cmd/roles/roles.go index b288e9e36a..277fc46770 100644 --- a/cmd/roles/roles.go +++ b/cmd/roles/roles.go @@ -114,18 +114,29 @@ func runComponent[T component](ctx context.Context, ) *conc.Future[component] { sign := make(chan struct{}) future := conc.Go(func() (component, error) { - factory := dependency.NewFactory(localMsg) - var err error - role, err := creator(ctx, factory) + // 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) + 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 { - 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") - } - close(sign) - healthz.Register(role) - metricRegister(Registry.GoRegistry) + + // 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") }