Skip to content

Commit f64edbe

Browse files
committed
WIP with some failures
1 parent f965573 commit f64edbe

10 files changed

Lines changed: 58 additions & 69 deletions

File tree

lib/minigun/execution/executor.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ def fork_for_item(item, stage, user_context, output_queue)
232232
stage,
233233
[capture_queue],
234234
{},
235-
{},
236235
stage_stats: stage_stats
237236
)
238237

@@ -279,7 +278,6 @@ def fork_for_item(item, stage, user_context, output_queue)
279278
stage,
280279
[capture_queue],
281280
{},
282-
{},
283281
stage_stats: stage_stats
284282
)
285283
if stage.respond_to?(:block) && stage.block

lib/minigun/pipeline.rb

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Minigun
55
# A Pipeline can be standalone or part of a multi-pipeline Task
66
class Pipeline
77
attr_reader :name, :config, :stages, :hooks, :dag, :output_queues, :stats,
8-
:context, :stage_hooks, :stage_input_queues, :runtime_edges, :input_queues, :parent_pipeline, :task
8+
:context, :stage_hooks, :runtime_edges, :input_queues, :parent_pipeline, :task
99

1010
def initialize(name, task, parent_pipeline, config = {}, stages: nil, hooks: nil, stage_hooks: nil, dag: nil, stats: nil)
1111
@name = name
@@ -283,8 +283,8 @@ def run_pipeline(_context)
283283
# Insert router stages for fan-out
284284
insert_router_stages_for_fan_out
285285

286-
# Create one input queue per stage (except producers)
287-
@stage_input_queues = build_stage_input_queues
286+
# Create one input queue per stage (except producers) and register with Task
287+
build_stage_input_queues
288288
@produced_count = Concurrent::AtomicFixnum.new(0)
289289
@stage_threads = []
290290

@@ -303,10 +303,8 @@ def run_pipeline(_context)
303303
@stage_threads.each(&:join)
304304
end
305305

306-
# Build one input queue per stage (except producers)
306+
# Build one input queue per stage (except producers) and register with Task
307307
def build_stage_input_queues
308-
queues = {}
309-
310308
# Find entrance infrastructure (router or single entry stage)
311309
entry_stages = @stages.select do |s|
312310
s.run_mode != :autonomous && @dag.upstream(s).empty?
@@ -318,17 +316,13 @@ def build_stage_input_queues
318316

319317
# Entrance router uses PipelineStage's queue
320318
if stage == @entrance_router && @input_queues && @input_queues[:input]
321-
queue = @input_queues[:input]
322-
queues[stage] = queue
323-
register_queue(stage, queue)
319+
register_queue(stage, @input_queues[:input])
324320
next
325321
end
326322

327323
# Single entry stage uses PipelineStage's queue directly (no router)
328324
if entry_stages.size == 1 && stage == entry_stages.first && @input_queues && @input_queues[:input]
329-
queue = @input_queues[:input]
330-
queues[stage] = queue
331-
register_queue(stage, queue)
325+
register_queue(stage, @input_queues[:input])
332326
next
333327
end
334328

@@ -339,11 +333,8 @@ def build_stage_input_queues
339333
else
340334
SizedQueue.new(size) # Bounded queue with backpressure
341335
end
342-
queues[stage] = queue
343336
register_queue(stage, queue)
344337
end
345-
346-
queues
347338
end
348339

349340
# Insert RouterStage instances for fan-out patterns

lib/minigun/queue_wrappers.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ def pop
4040

4141
# Wrapper around stage output that routes to downstream queues
4242
class OutputQueue
43-
def initialize(stage, downstream_queues, all_stage_queues, runtime_edges, stage_stats: nil)
43+
def initialize(stage, downstream_queues, runtime_edges, stage_stats: nil)
4444
@stage = stage
4545
@downstream_queues = downstream_queues # Array of Queue objects
46-
@all_stage_queues = all_stage_queues # Hash keyed by Stage objects
4746
@runtime_edges = runtime_edges # Track dynamic routing (keyed by Stage objects)
4847
@stage_stats = stage_stats # Stats object for tracking (optional)
4948
@to_cache = {} # Memoization cache for .to() results
@@ -79,7 +78,6 @@ def to(target)
7978
@to_cache[target] = OutputQueue.new(
8079
@stage,
8180
[target_queue],
82-
@all_stage_queues,
8381
@runtime_edges,
8482
stage_stats: @stage_stats
8583
)

lib/minigun/stage.rb

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ module Minigun
99
:stage,
1010
:dag,
1111
:runtime_edges,
12-
:stage_input_queues,
1312
:stage_stats,
1413
# Worker-specific (nil/empty for producers)
1514
:worker,
@@ -162,11 +161,11 @@ def create_input_queue(stage_ctx)
162161
def create_output_queue(stage_ctx)
163162
# DAG and queues now use Stage objects
164163
downstream = stage_ctx.dag.downstream(stage_ctx.stage)
165-
downstream_queues = downstream.filter_map { |ds| stage_ctx.stage_input_queues[ds] }
164+
task = stage_ctx.stage.task
165+
downstream_queues = downstream.filter_map { |ds| task&.find_queue(ds) }
166166
OutputQueue.new(
167167
stage_ctx.stage,
168168
downstream_queues,
169-
stage_ctx.stage_input_queues,
170169
stage_ctx.runtime_edges,
171170
stage_stats: stage_ctx.stage_stats
172171
)
@@ -177,11 +176,13 @@ def send_end_signals(stage_ctx)
177176
dag_downstream = stage_ctx.dag.downstream(stage_ctx.stage)
178177
dynamic_targets = stage_ctx.runtime_edges[stage_ctx.stage].to_a
179178
all_targets = (dag_downstream + dynamic_targets).uniq
179+
task = stage_ctx.stage.task
180180

181181
all_targets.each do |target|
182-
next unless stage_ctx.stage_input_queues[target]
182+
queue = task&.find_queue(target)
183+
next unless queue
183184

184-
stage_ctx.stage_input_queues[target] << EndOfSource.new(stage_ctx.stage)
185+
queue << EndOfSource.new(stage_ctx.stage)
185186
end
186187
end
187188

@@ -388,15 +389,19 @@ def initialize(name, pipeline, targets, options = {})
388389

389390
def send_end_signals(worker_ctx)
390391
# Broadcast EndOfSource to ALL router targets
392+
task = worker_ctx.stage.task
391393
@targets.each do |target|
392-
worker_ctx.stage_input_queues[target] << EndOfSource.new(worker_ctx.stage)
394+
queue = task&.find_queue(target)
395+
queue&.<< EndOfSource.new(worker_ctx.stage)
393396
end
394397
end
395398
end
396399

397400
# Broadcast router - sends each item to ALL downstream stages
398401
class RouterBroadcastStage < RouterStage
399402
def run_stage(worker_ctx)
403+
task = worker_ctx.stage.task
404+
400405
loop do
401406
item = worker_ctx.input_queue.pop
402407

@@ -410,7 +415,8 @@ def run_stage(worker_ctx)
410415

411416
# Broadcast to all downstream stages (fan-out semantics)
412417
@targets.each do |target|
413-
worker_ctx.stage_input_queues[target] << item
418+
queue = task&.find_queue(target)
419+
queue&.<< item
414420
end
415421
end
416422
ensure
@@ -421,7 +427,8 @@ def run_stage(worker_ctx)
421427
# Round-robin router - distributes items across downstream stages
422428
class RouterRoundRobinStage < RouterStage
423429
def run_stage(worker_ctx)
424-
target_queues = @targets.map { |target| worker_ctx.stage_input_queues[target] }
430+
task = worker_ctx.stage.task
431+
target_queues = @targets.map { |target| task&.find_queue(target) }.compact
425432
round_robin_index = 0
426433

427434
loop do

lib/minigun/worker.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,11 @@ def handle_disconnected_stage(stage_ctx) # rubocop:disable Naming/PredicateMetho
6363

6464
# Send EndOfSource to all downstream stages so they don't deadlock
6565
# DAG and queues now use Stage objects
66+
task = stage_ctx.stage.task
6667
downstream = stage_ctx.dag.downstream(stage_ctx.stage)
6768
downstream.each do |target|
68-
stage_ctx.stage_input_queues[target] << EndOfSource.new(stage_ctx.stage)
69+
queue = task&.find_queue(target)
70+
queue&.<< EndOfSource.new(stage_ctx.stage)
6971
end
7072

7173
log_debug 'Done'
@@ -77,7 +79,7 @@ def handle_disconnected_stage(stage_ctx) # rubocop:disable Naming/PredicateMetho
7779

7880
def create_stage_context
7981
dag = @pipeline.dag
80-
stage_input_queues = @pipeline.stage_input_queues
82+
task = @pipeline.task
8183

8284
# Calculate sources for workers (empty for autonomous stages)
8385
# DAG now uses Stage objects instead of names
@@ -109,10 +111,9 @@ def create_stage_context
109111
stage: @stage,
110112
dag: dag,
111113
runtime_edges: @pipeline.runtime_edges,
112-
stage_input_queues: stage_input_queues,
113114
stage_stats: stage_stats,
114115
# Worker-specific (nil/empty for producers)
115-
input_queue: stage_input_queues[@stage],
116+
input_queue: task&.find_queue(@stage),
116117
sources_expected: sources_expected,
117118
sources_done: Set.new
118119
)

spec/minigun/pipeline_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
RSpec.describe Minigun::Pipeline do
66
let(:config) { { max_threads: 3, max_processes: 2 } }
7-
let(:task) { instance_double(Minigun::Task, stage_registry: nil, register_stage_queue: nil) }
7+
let(:task) { Minigun::Task.new }
88
let(:pipeline) { described_class.new(:test_pipeline, task, nil, config) }
99

1010
describe '#initialize' do

spec/minigun/stage_spec.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
RSpec.describe Minigun::Stage do
66
let(:mock_registry) { instance_double(Minigun::StageRegistry, register: nil) }
7-
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry) }
7+
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry, find_queue: nil) }
88
let(:mock_pipeline) { instance_double(Minigun::Pipeline, name: 'test_pipeline', task: mock_task) }
99

1010
describe 'base class' do
@@ -31,7 +31,7 @@
3131

3232
RSpec.describe Minigun::ProducerStage do
3333
let(:mock_registry) { instance_double(Minigun::StageRegistry, register: nil) }
34-
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry) }
34+
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry, find_queue: nil) }
3535
let(:mock_pipeline) { instance_double(Minigun::Pipeline, name: 'test_pipeline', task: mock_task) }
3636

3737
describe 'producer behavior' do
@@ -60,7 +60,7 @@
6060

6161
RSpec.describe Minigun::ConsumerStage do
6262
let(:mock_registry) { instance_double(Minigun::StageRegistry, register: nil) }
63-
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry) }
63+
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry, find_queue: nil) }
6464
let(:mock_pipeline) { instance_double(Minigun::Pipeline, name: 'test_pipeline', task: mock_task) }
6565

6666
describe 'processor behavior' do
@@ -117,7 +117,7 @@
117117

118118
RSpec.describe Minigun::AccumulatorStage do
119119
let(:mock_registry) { instance_double(Minigun::StageRegistry, register: nil) }
120-
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry) }
120+
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry, find_queue: nil) }
121121
let(:mock_pipeline) { instance_double(Minigun::Pipeline, name: 'test_pipeline', task: mock_task) }
122122

123123
it 'is a special batching stage' do
@@ -128,7 +128,7 @@
128128

129129
RSpec.describe 'Stage common behavior' do
130130
let(:mock_registry) { instance_double(Minigun::StageRegistry, register: nil) }
131-
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry) }
131+
let(:mock_task) { instance_double(Minigun::Task, stage_registry: mock_registry, find_queue: nil) }
132132
let(:mock_pipeline) { instance_double(Minigun::Pipeline, name: 'test_pipeline', task: mock_task) }
133133
let(:stage) { Minigun::ConsumerStage.new(:test, mock_pipeline, proc { |x, _output| x * 2 }, { foo: 'bar' }) }
134134

spec/unit/execution/worker_spec.rb

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
require 'spec_helper'
44

55
RSpec.describe Minigun::Worker do
6+
let(:stage_registry) { instance_double(Minigun::StageRegistry, register: nil) }
7+
let(:task) { instance_double(Minigun::Task, find_queue: nil, stage_registry: stage_registry) }
8+
69
let(:pipeline) do
710
instance_double(
811
Minigun::Pipeline,
912
name: 'test_pipeline',
1013
dag: dag,
11-
stage_input_queues: {},
14+
task: task,
1215
runtime_edges: {},
1316
context: user_context,
1417
stats: stats,
@@ -22,7 +25,8 @@
2225
name: :test_stage,
2326
execution_context: nil,
2427
log_type: 'Worker',
25-
run_mode: :streaming
28+
run_mode: :streaming,
29+
task: task
2630
)
2731
end
2832

@@ -55,8 +59,7 @@
5559
describe '#start' do
5660
it 'starts a worker thread' do
5761
input_queue = Queue.new
58-
allow(pipeline).to receive(:stage_input_queues)
59-
.and_return({ test_stage: input_queue })
62+
allow(task).to receive(:find_queue).with(stage).and_return(input_queue)
6063
allow(dag).to receive(:upstream).with(:test_stage).and_return([:upstream])
6164
allow(dag).to receive(:downstream).with(:test_stage).and_return([])
6265

@@ -89,8 +92,7 @@
8992
describe '#join' do
9093
it 'waits for worker thread to complete' do
9194
input_queue = Queue.new
92-
allow(pipeline).to receive(:stage_input_queues)
93-
.and_return({ test_stage: input_queue })
95+
allow(task).to receive(:find_queue).with(stage).and_return(input_queue)
9496

9597
worker = described_class.new(pipeline, stage, config)
9698

@@ -151,10 +153,10 @@
151153

152154
describe 'disconnected stage handling' do
153155
it 'exits early if no upstream sources' do
154-
allow(pipeline).to receive(:stage_input_queues)
155-
.and_return({ test_stage: Queue.new })
156-
allow(dag).to receive(:upstream).with(:test_stage).and_return([])
157-
allow(dag).to receive(:downstream).with(:test_stage).and_return([])
156+
input_queue = Queue.new
157+
allow(task).to receive(:find_queue).with(stage).and_return(input_queue)
158+
allow(dag).to receive(:upstream).with(stage).and_return([])
159+
allow(dag).to receive(:downstream).with(stage).and_return([])
158160

159161
worker = described_class.new(pipeline, stage, config)
160162

@@ -167,14 +169,14 @@
167169
end
168170

169171
it 'sends END signals to downstream if disconnected' do
170-
allow(pipeline).to receive(:stage_input_queues)
171-
.and_return({ test_stage: Queue.new, downstream: Queue.new })
172-
allow(dag).to receive(:upstream).with(:test_stage).and_return([])
173-
allow(dag).to receive(:downstream).with(:test_stage).and_return([:downstream])
174-
172+
input_queue = Queue.new
173+
downstream_stage = double('downstream_stage', name: :downstream, task: task)
175174
downstream_queue = Queue.new
176-
allow(pipeline).to receive(:stage_input_queues)
177-
.and_return({ test_stage: Queue.new, downstream: downstream_queue })
175+
176+
allow(task).to receive(:find_queue).with(stage).and_return(input_queue)
177+
allow(task).to receive(:find_queue).with(downstream_stage).and_return(downstream_queue)
178+
allow(dag).to receive(:upstream).with(stage).and_return([])
179+
allow(dag).to receive(:downstream).with(stage).and_return([downstream_stage])
178180

179181
worker = described_class.new(pipeline, stage, config)
180182
worker.start
@@ -213,8 +215,9 @@
213215
target_a_queue = Queue.new
214216
target_b_queue = Queue.new
215217

216-
allow(pipeline).to receive(:stage_input_queues)
217-
.and_return({ broadcast_router => input_queue, target_a_stage => target_a_queue, target_b_stage => target_b_queue })
218+
allow(task).to receive(:find_queue).with(broadcast_router).and_return(input_queue)
219+
allow(task).to receive(:find_queue).with(target_a_stage).and_return(target_a_queue)
220+
allow(task).to receive(:find_queue).with(target_b_stage).and_return(target_b_queue)
218221
allow(dag).to receive(:upstream).with(broadcast_router).and_return([source_stage])
219222

220223
# Put items and END signal
@@ -261,8 +264,7 @@
261264
describe 'logging' do
262265
it 'logs when starting' do
263266
input_queue = Queue.new
264-
allow(pipeline).to receive(:stage_input_queues)
265-
.and_return({ test_stage: input_queue })
267+
allow(task).to receive(:find_queue).with(stage).and_return(input_queue)
266268

267269
input_queue << Minigun::EndOfSource.new(:upstream)
268270

@@ -277,8 +279,7 @@
277279

278280
it 'logs when done' do
279281
input_queue = Queue.new
280-
allow(pipeline).to receive(:stage_input_queues)
281-
.and_return({ test_stage: input_queue })
282+
allow(task).to receive(:find_queue).with(stage).and_return(input_queue)
282283
allow(dag).to receive(:upstream).with(:test_stage).and_return([:upstream])
283284
allow(dag).to receive(:downstream).with(:test_stage).and_return([])
284285

spec/unit/queue_wrappers_spec.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
described_class.new(
5050
test_stage,
5151
downstream_queues,
52-
all_stage_queues,
5352
runtime_edges,
5453
stage_stats: stage_stats
5554
)

0 commit comments

Comments
 (0)