@@ -162,13 +162,19 @@ func Bootstrap(ctx context.Context, cfg *config.Config, logger *zap.Logger) (*De
162162
163163 teranodeClient .Start (ctx )
164164
165- // Construct chaintracks once at process startup so every consumer
166- // (chaintracks_server, bump-builder canonical-root validation, watchdog
167- // future use) shares one P2P subscription and header cache. Skipped when
168- // chaintracks_server is disabled — that flag is already the operator's
169- // process-wide "no chaintracks" switch (regtest force-disables it).
165+ // Construct chaintracks once at process startup so every in-process
166+ // consumer (chaintracks_server, bump-builder canonical-root validation)
167+ // shares one P2P subscription and header cache. Skipped when:
168+ // - cfg.ChaintracksServer.Enabled is false (operator-wide off switch;
169+ // regtest force-disables it via config.validate), or
170+ // - the configured mode never dereferences deps.Chaintracks. Without
171+ // this gate every microservice pod (api-server, sse, propagation, …)
172+ // would spin up an embedded ChainManager and poll the upstream node
173+ // even though nothing in that pod reads from it. In microservice
174+ // deployments the chaintracks pod runs embedded and bump-builder
175+ // points chaintracks.mode=remote at it; everything else skips here.
170176 var chainTracks chaintrackslib.Chaintracks
171- if cfg .ChaintracksServer .Enabled {
177+ if cfg .ChaintracksServer .Enabled && modeNeedsChaintracks ( cfg . Mode ) {
172178 ct , ctErr := initChaintracks (ctx , cfg , logger )
173179 if ctErr != nil {
174180 _ = st .Close ()
@@ -233,6 +239,25 @@ func Bootstrap(ctx context.Context, cfg *config.Config, logger *zap.Logger) (*De
233239 return deps , cleanup , nil
234240}
235241
242+ // modeNeedsChaintracks reports whether the configured service mode constructs
243+ // at least one service that reads from deps.Chaintracks. Other modes
244+ // (api-server, sse, propagation, p2p-client, watchdog) never dereference it,
245+ // so initializing it would spin up an unused embedded ChainManager + upstream
246+ // header poller in every pod — which is exactly the wrong thing for
247+ // microservice deployments where the dedicated chaintracks pod owns the
248+ // header store and bump-builder reads via chaintracks.mode=remote.
249+ //
250+ // Keep this list in sync with BuildServices: any new mode that wires
251+ // deps.Chaintracks into a service must be added here.
252+ func modeNeedsChaintracks (mode string ) bool {
253+ switch mode {
254+ case "all" , "chaintracks" , "bump-builder" :
255+ return true
256+ default :
257+ return false
258+ }
259+ }
260+
236261// initChaintracks brings up the embedded go-chaintracks instance shared
237262// across the process. Caller gates the enabled-ness check; this function
238263// always tries to construct and returns an error on failure.
0 commit comments