From 3f12c42cadf87fb14286dc341d7af7f15f907fdc Mon Sep 17 00:00:00 2001 From: "erigon-copilot[bot]" Date: Tue, 26 May 2026 23:57:09 +0200 Subject: [PATCH] execution: make ValidateChain non-blocking to fix flaky timeout ValidateChain blocked synchronously while holding the semaphore. Under -race on CI, executing high-gas blocks took long enough for the HTTP client timeout to fire, producing a non-retryable error in RetryEngine and causing "context deadline exceeded" failures. Spawn validation work in a background goroutine (using bacgroundCtx) and return ExecutionStatusBusy when the caller's context is cancelled, matching the existing UpdateForkChoice async pattern. Subsequent retries pick up the cached result from validHashes. Co-Authored-By: Claude Opus 4.6 Co-authored-by: Giulio Rebuffo --- execution/execmodule/exec_module.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/execution/execmodule/exec_module.go b/execution/execmodule/exec_module.go index eee5a3772e2..ec211a91d7a 100644 --- a/execution/execmodule/exec_module.go +++ b/execution/execmodule/exec_module.go @@ -389,8 +389,32 @@ func (e *ExecModule) ValidateChain(ctx context.Context, blockHash common.Hash, b ValidationStatus: ExecutionStatusBusy, }, nil } - defer e.semaphore.Release(1) + type validationOutcome struct { + result ValidationResult + err error + } + outcomeCh := make(chan validationOutcome, 1) + done := make(chan struct{}) + + go func() { + defer close(done) + defer e.semaphore.Release(1) + result, err := e.validateChainImpl(e.bacgroundCtx, blockHash, blockNumber) + outcomeCh <- validationOutcome{result, err} + }() + + select { + case o := <-outcomeCh: + <-done + return o.result, o.err + case <-ctx.Done(): + e.logger.Debug("treating ValidateChain as asynchronous as caller context is done") + return ValidationResult{ValidationStatus: ExecutionStatusBusy}, nil + } +} + +func (e *ExecModule) validateChainImpl(ctx context.Context, blockHash common.Hash, blockNumber uint64) (ValidationResult, error) { e.hook.LastNewBlockSeen(blockNumber) // used by eth_syncing e.currentContext.ResetPendingUpdates() e.forkValidator.ClearWithUnwind()