From 898ad84534031a6095b2999fcb1f0af7b2705885 Mon Sep 17 00:00:00 2001 From: mh0lt Date: Tue, 28 Apr 2026 06:32:51 +0000 Subject: [PATCH] cmd/utils, node/cli: add --exec.serial flag to clamp workers to 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #20797. Adds a single-purpose boolean that forces serial execution by clamping the parallel executor's worker count to 1 — wins over both --exec.workers and EXEC3_WORKERS. Cleaner than `--exec.workers=1` because it states intent ("force serial") at the call site rather than relying on a magic value, and it can't be accidentally undone by a stale EXEC3_WORKERS env var that gets re-read by tooling. Use case: like-for-like baseline comparisons against the parallel executor (devnet A/B runs, profiling cold paths) where the operator explicitly wants serial irrespective of any other knob. Wiring: applied AFTER --exec.workers in SetEthConfig, so: --exec.serial=true → workers=1 --exec.workers=12 --exec.serial=true → workers=1 (serial wins) --exec.workers=12 → workers=12 --exec.serial=false (default) → no override Test: TestExecPerfFlags_OverrideDbg covers serial-true, serial-vs-workers precedence, and serial-false-leaves-untouched. Co-Authored-By: Claude Opus 4.7 (1M context) --- cmd/utils/flags.go | 10 ++++++++++ cmd/utils/flags_test.go | 25 ++++++++++++++++++++++++- node/cli/default_flags.go | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index bb72e1a62f4..ee8b248b4ad 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1165,6 +1165,11 @@ var ( Usage: "Parallel executor worker count (equivalent to EXEC3_WORKERS). Default: half the number of CPU cores, other half reserved for snapshots build/merge/prune.", Value: runtime.NumCPU() / 2, } + ExecSerialFlag = cli.BoolFlag{ + Name: "exec.serial", + Usage: "Force serial execution by clamping the parallel executor to a single worker. Wins over --exec.workers and EXEC3_WORKERS — use to disable parallelism for diagnostics or like-for-like baseline comparisons.", + Value: false, + } ExecNoMergeFlag = cli.BoolFlag{ Name: "exec.no-merge", Usage: "Disable state-aggregator file merges for Domain / History / Inverted-Index (equivalent to NO_MERGE=true). Diagnostic / perf-comparison use only.", @@ -2003,6 +2008,11 @@ func SetEthConfig(ctx *cli.Context, nodeConfig *nodecfg.Config, cfg *ethconfig.C dbg.SetExec3Workers(n) cfg.ExecWorkerCount = n } + // --exec.serial wins over --exec.workers / EXEC3_WORKERS: clamp to 1. + if ctx.IsSet(ExecSerialFlag.Name) && ctx.Bool(ExecSerialFlag.Name) { + dbg.SetExec3Workers(1) + cfg.ExecWorkerCount = 1 + } if ctx.IsSet(ExecNoMergeFlag.Name) { dbg.SetNoMerge(ctx.Bool(ExecNoMergeFlag.Name)) } diff --git a/cmd/utils/flags_test.go b/cmd/utils/flags_test.go index 1f335babb80..21d7cbff909 100644 --- a/cmd/utils/flags_test.go +++ b/cmd/utils/flags_test.go @@ -132,6 +132,9 @@ func TestExecPerfFlags_OverrideDbg(t *testing.T) { if ctx.IsSet(ExecWorkersFlag.Name) { dbg.SetExec3Workers(ctx.Int(ExecWorkersFlag.Name)) } + if ctx.IsSet(ExecSerialFlag.Name) && ctx.Bool(ExecSerialFlag.Name) { + dbg.SetExec3Workers(1) + } if ctx.IsSet(ExecNoMergeFlag.Name) { dbg.SetNoMerge(ctx.Bool(ExecNoMergeFlag.Name)) } @@ -145,7 +148,7 @@ func TestExecPerfFlags_OverrideDbg(t *testing.T) { app := cli.NewApp() app.Flags = []cli.Flag{ &ExecBatchedIOFlag, &ExecStateCacheFlag, &ExecWorkersFlag, - &ExecNoMergeFlag, &ExecNoPruneFlag, + &ExecSerialFlag, &ExecNoMergeFlag, &ExecNoPruneFlag, } app.Action = apply require.NoError(t, app.Run(append([]string{"test"}, args...))) @@ -195,6 +198,26 @@ func TestExecPerfFlags_OverrideDbg(t *testing.T) { require.Equal(t, 7, dbg.Exec3Workers) }) + t.Run("serial=true clamps Exec3Workers to 1", func(t *testing.T) { + dbg.SetExec3Workers(8) + run("--exec.serial=true") + require.Equal(t, 1, dbg.Exec3Workers) + }) + + t.Run("serial=true wins over --exec.workers", func(t *testing.T) { + dbg.SetExec3Workers(1) + // flags are applied in declaration order (workers first, then serial), + // so serial should override workers regardless of CLI argument order. + run("--exec.workers=12", "--exec.serial=true") + require.Equal(t, 1, dbg.Exec3Workers) + }) + + t.Run("serial=false leaves Exec3Workers untouched", func(t *testing.T) { + dbg.SetExec3Workers(8) + run("--exec.serial=false") + require.Equal(t, 8, dbg.Exec3Workers) + }) + t.Run("no-merge and no-prune set to true", func(t *testing.T) { dbg.SetNoMerge(false) dbg.SetNoPrune(false) diff --git a/node/cli/default_flags.go b/node/cli/default_flags.go index 10ab20144d8..b8bd78fc271 100644 --- a/node/cli/default_flags.go +++ b/node/cli/default_flags.go @@ -50,6 +50,7 @@ var DefaultFlags = []cli.Flag{ &utils.ExecBatchedIOFlag, &utils.ExecStateCacheFlag, &utils.ExecWorkersFlag, + &utils.ExecSerialFlag, &utils.ExecNoMergeFlag, &utils.ExecNoPruneFlag, &BatchSizeFlag,