diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-12 22:36:11 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-12 22:36:11 +0300 |
| commit | a2c067cc49b96968da81031275de9c44c4ba2ee9 (patch) | |
| tree | 84d0507274599cf3cad0daa9b1be613549b74304 /internal | |
| parent | 235eb7541d6396e860b23aad63ed44c734cdf767 (diff) | |
introduce RuntimeBuilder to separate construction from orchestration in ior.go
RuntimeBuilder encapsulates allocation of per-trace-session components
(statsengine.Engine, streamrow.RingBuffer, Sequencer, flamegraph.LiveTrie);
buildTUIRuntime, buildTestFlamesRuntime, and buildTestLiveFlamesRuntime now
delegate construction to RuntimeBuilder.Build() and focus only on wiring.
wireRuntimeBindings is extracted to isolate the context-binding wiring step.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/ior.go | 85 | ||||
| -rw-r--r-- | internal/runtime_builder.go | 49 |
2 files changed, 98 insertions, 36 deletions
diff --git a/internal/ior.go b/internal/ior.go index b6070dc..aff1355 100644 --- a/internal/ior.go +++ b/internal/ior.go @@ -163,29 +163,28 @@ func tuiTestLiveFlamesStarter(cfg flags.Config) runtime.TraceStarter { } // buildTestFlamesRuntime allocates a stats engine, stream buffer, and seeded -// live trie for static test-flames mode. +// live trie for static test-flames mode. Component allocation is delegated to +// RuntimeBuilder so this function focuses on the seed step only. func buildTestFlamesRuntime(cfg flags.Config) (*statsengine.Engine, *streamrow.RingBuffer, *flamegraph.LiveTrie) { - engine := statsengine.NewEngine(64) - streamBuf := streamrow.NewRingBuffer() - liveTrie := flamegraph.NewLiveTrie(cfg.CollapsedFields, cfg.CountField) - flamegraph.SeedTestFlameData(liveTrie) - return engine, streamBuf, liveTrie + components := newRuntimeBuilder(cfg).Build() + flamegraph.SeedTestFlameData(components.liveTrie) + return components.engine, components.streamBuf, components.liveTrie } // buildTestLiveFlamesRuntime allocates a stats engine, stream buffer, and live // trie for live test-flames mode, then launches a goroutine to update the trie. +// Component allocation is delegated to RuntimeBuilder; this function handles +// only the seed step and the background updater goroutine. func buildTestLiveFlamesRuntime(ctx context.Context, cfg flags.Config) (*statsengine.Engine, *streamrow.RingBuffer, *flamegraph.LiveTrie) { - engine := statsengine.NewEngine(64) - streamBuf := streamrow.NewRingBuffer() - liveTrie := flamegraph.NewLiveTrie(cfg.CollapsedFields, cfg.CountField) - flamegraph.SeedTestLiveFlameData(liveTrie, 0) + components := newRuntimeBuilder(cfg).Build() + flamegraph.SeedTestLiveFlameData(components.liveTrie, 0) interval := cfg.LiveInterval if interval <= 0 { interval = 200 * time.Millisecond } - go runSyntheticLiveFlames(ctx, liveTrie, interval) - return engine, streamBuf, liveTrie + go runSyntheticLiveFlames(ctx, components.liveTrie, interval) + return components.engine, components.streamBuf, components.liveTrie } func runSyntheticLiveFlames(ctx context.Context, liveTrie *flamegraph.LiveTrie, interval time.Duration) { @@ -225,40 +224,54 @@ type tuiRuntime struct { filterEpoch uint64 } -// buildTUIRuntime allocates fresh trace-session state and, when persistent -// runtime bindings exist in ctx, wires them in (reusing the existing stream -// buffer / sequencer and reading the parquet recorder and filter epoch). +// buildTUIRuntime constructs fresh trace-session components via RuntimeBuilder +// and then wires them into any persistent runtime bindings found in ctx. +// Construction (allocating engine, buffer, sequencer, trie) is handled by +// RuntimeBuilder; this function focuses on the wiring: reusing the persistent +// stream buffer and sequencer from the TUI, reading the recorder and filter +// epoch, and publishing the new components back to the runtime bindings. func buildTUIRuntime(ctx context.Context, cfg flags.Config) (*tuiRuntime, error) { + components := newRuntimeBuilder(cfg).Build() rt := &tuiRuntime{ - engine: statsengine.NewEngine(64), - streamSeq: streamrow.NewSequencer(0), - liveTrie: flamegraph.NewLiveTrie(cfg.CollapsedFields, cfg.CountField), + engine: components.engine, + streamBuf: components.streamBuf, + streamSrc: components.streamBuf, + streamSeq: components.streamSeq, + liveTrie: components.liveTrie, } - buf := streamrow.NewRingBuffer() - rt.streamBuf = buf - rt.streamSrc = buf if bindings, ok := runtime.RuntimeBindingsFromContext(ctx); ok { - if persistent := bindings.StreamBuffer(); persistent != nil { - rt.streamSrc = persistent - sink, ok := persistent.(streamEventSink) - if !ok { - return nil, fmt.Errorf("runtime stream source does not support event pushes") - } - rt.streamBuf = sink - } - if persistentSeq := bindings.StreamSequencer(); persistentSeq != nil { - rt.streamSeq = persistentSeq + if err := wireRuntimeBindings(rt, bindings); err != nil { + return nil, err } - rt.recorder = bindings.Recorder() - rt.filterEpoch = bindings.FilterEpoch() - bindings.SetDashboardSnapshotSource(rt.engine) - bindings.SetEventStreamSource(rt.streamSrc) - bindings.SetLiveTrie(rt.liveTrie) } return rt, nil } +// wireRuntimeBindings reuses persistent TUI-owned state (stream buffer, +// sequencer, recorder, filter epoch) from bindings and publishes the freshly +// built components back to the TUI so the new trace session is visible. +// It is called only when a TraceRuntimeBindings is present in the context. +func wireRuntimeBindings(rt *tuiRuntime, bindings runtime.TraceRuntimeBindings) error { + if persistent := bindings.StreamBuffer(); persistent != nil { + rt.streamSrc = persistent + sink, ok := persistent.(streamEventSink) + if !ok { + return fmt.Errorf("runtime stream source does not support event pushes") + } + rt.streamBuf = sink + } + if persistentSeq := bindings.StreamSequencer(); persistentSeq != nil { + rt.streamSeq = persistentSeq + } + rt.recorder = bindings.Recorder() + rt.filterEpoch = bindings.FilterEpoch() + bindings.SetDashboardSnapshotSource(rt.engine) + bindings.SetEventStreamSource(rt.streamSrc) + bindings.SetLiveTrie(rt.liveTrie) + return nil +} + // makeTUIEventLoopConfigurer returns the func(*eventLoop) callback that wires // the event loop into the TUI runtime: it sets the initial filter, installs // the print callback that fans out to engine/stream/trie, and registers the diff --git a/internal/runtime_builder.go b/internal/runtime_builder.go new file mode 100644 index 0000000..bc1c228 --- /dev/null +++ b/internal/runtime_builder.go @@ -0,0 +1,49 @@ +package internal + +import ( + "ior/internal/flags" + "ior/internal/flamegraph" + "ior/internal/statsengine" + "ior/internal/streamrow" +) + +const defaultEngineCapacity = 64 + +// runtimeComponents holds the freshly allocated trace-session components +// produced by RuntimeBuilder.Build. All fields are non-nil after a successful +// build. The caller is responsible for wiring these into the runtime bindings +// and event-loop callbacks. +type runtimeComponents struct { + engine *statsengine.Engine + streamBuf *streamrow.RingBuffer + streamSeq *streamrow.Sequencer + liveTrie *flamegraph.LiveTrie +} + +// RuntimeBuilder encapsulates the allocation of per-trace-session runtime +// components. Construct one with newRuntimeBuilder, then call Build to +// produce a fresh runtimeComponents struct ready for wiring. +type RuntimeBuilder struct { + // cfg holds the configuration used to size and configure the components. + cfg flags.Config +} + +// newRuntimeBuilder creates a RuntimeBuilder configured from cfg. +// The builder captures cfg at construction time; later cfg changes do not affect +// a builder already created. +func newRuntimeBuilder(cfg flags.Config) RuntimeBuilder { + return RuntimeBuilder{cfg: cfg} +} + +// Build allocates a fresh set of runtime components for one trace session. +// It creates a new stats engine, stream ring buffer, sequencer, and live trie +// each time it is called; callers must not share the returned components across +// concurrent trace sessions. +func (b RuntimeBuilder) Build() runtimeComponents { + return runtimeComponents{ + engine: statsengine.NewEngine(defaultEngineCapacity), + streamBuf: streamrow.NewRingBuffer(), + streamSeq: streamrow.NewSequencer(0), + liveTrie: flamegraph.NewLiveTrie(b.cfg.CollapsedFields, b.cfg.CountField), + } +} |
