fractor 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,395 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../lib/fractor"
4
+
5
+ module SpecializedWorkers
6
+ # First work type: Compute-intensive operations
7
+ class ComputeWork < Fractor::Work
8
+ def initialize(data, operation = :default, parameters = {})
9
+ super({
10
+ data: data,
11
+ operation: operation,
12
+ parameters: parameters
13
+ })
14
+ end
15
+
16
+ def data
17
+ input[:data]
18
+ end
19
+
20
+ def operation
21
+ input[:operation]
22
+ end
23
+
24
+ def parameters
25
+ input[:parameters]
26
+ end
27
+
28
+ def to_s
29
+ "ComputeWork: operation=#{operation}, parameters=#{parameters}"
30
+ end
31
+ end
32
+
33
+ # Second work type: Database operations
34
+ class DatabaseWork < Fractor::Work
35
+ def initialize(data = "", query_type = :select, table = "unknown", conditions = {})
36
+ super({
37
+ data: data,
38
+ query_type: query_type,
39
+ table: table,
40
+ conditions: conditions
41
+ })
42
+ end
43
+
44
+ def data
45
+ input[:data]
46
+ end
47
+
48
+ def query_type
49
+ input[:query_type]
50
+ end
51
+
52
+ def table
53
+ input[:table]
54
+ end
55
+
56
+ def conditions
57
+ input[:conditions]
58
+ end
59
+
60
+ def to_s
61
+ "DatabaseWork: query_type=#{query_type}, table=#{table}, conditions=#{conditions}"
62
+ end
63
+ end
64
+
65
+ # First worker type: Handles compute-intensive operations
66
+ class ComputeWorker < Fractor::Worker
67
+ def initialize
68
+ # Setup resources needed for computation
69
+ @compute_resources = { memory: 1024, cpu_cores: 4 }
70
+ end
71
+
72
+ def process(work)
73
+ # Only handle ComputeWork
74
+ unless work.is_a?(ComputeWork)
75
+ return Fractor::WorkResult.new(
76
+ error: "ComputeWorker can only process ComputeWork, got: #{work.class}",
77
+ work: work
78
+ )
79
+ end
80
+
81
+ # Process based on the requested operation
82
+ result = case work.operation
83
+ when :matrix_multiply then matrix_multiply(work.data, work.parameters)
84
+ when :image_transform then image_transform(work.data, work.parameters)
85
+ when :path_finding then path_finding(work.data, work.parameters)
86
+ else default_computation(work.data, work.parameters)
87
+ end
88
+
89
+ Fractor::WorkResult.new(
90
+ result: {
91
+ operation: work.operation,
92
+ computation_result: result,
93
+ resources_used: @compute_resources
94
+ },
95
+ work: work
96
+ )
97
+ end
98
+
99
+ # Computation methods
100
+ private
101
+
102
+ def matrix_multiply(_data, params)
103
+ # Simulate matrix multiplication
104
+ sleep(rand(0.05..0.2))
105
+ size = params[:size] || [3, 3]
106
+ "Matrix multiplication result: #{size[0]}x#{size[1]} matrix, determinant=#{rand(1..100)}"
107
+ end
108
+
109
+ def image_transform(_data, params)
110
+ # Simulate image transformation
111
+ sleep(rand(0.1..0.3))
112
+ transforms = params[:transforms] || %i[rotate scale]
113
+ "Image transformation applied: #{transforms.join(", ")} with parameters #{params}"
114
+ end
115
+
116
+ def path_finding(_data, params)
117
+ # Simulate path finding algorithm
118
+ sleep(rand(0.2..0.5))
119
+ algorithm = params[:algorithm] || :a_star
120
+ nodes = params[:nodes] || 10
121
+ "Path found using #{algorithm}: #{rand(1..nodes)} steps, cost=#{rand(10..100)}"
122
+ end
123
+
124
+ def default_computation(data, _params)
125
+ # Default computation
126
+ sleep(rand(0.01..0.1))
127
+ "Default computation result for input: #{data}"
128
+ end
129
+ end
130
+
131
+ # Second worker type: Handles database operations
132
+ class DatabaseWorker < Fractor::Worker
133
+ def initialize
134
+ # Setup database connection and resources
135
+ @db_connection = { pool_size: 5, timeout: 30 }
136
+ end
137
+
138
+ def process(work)
139
+ # Only handle DatabaseWork
140
+ unless work.is_a?(DatabaseWork)
141
+ return Fractor::WorkResult.new(
142
+ error: "DatabaseWorker can only process DatabaseWork, got: #{work.class}",
143
+ work: work
144
+ )
145
+ end
146
+
147
+ # Process based on query type
148
+ result = case work.query_type
149
+ when :select then perform_select(work.table, work.conditions)
150
+ when :insert then perform_insert(work.table, work.data)
151
+ when :update then perform_update(work.table, work.data, work.conditions)
152
+ when :delete then perform_delete(work.table, work.conditions)
153
+ else default_query(work.query_type, work.table, work.conditions)
154
+ end
155
+
156
+ Fractor::WorkResult.new(
157
+ result: {
158
+ query_type: work.query_type,
159
+ table: work.table,
160
+ rows_affected: result[:rows_affected],
161
+ data: result[:data],
162
+ execution_time: result[:time]
163
+ },
164
+ work: work
165
+ )
166
+ end
167
+
168
+ # Database operation methods
169
+ private
170
+
171
+ def perform_select(_table, _conditions)
172
+ # Select query simulation
173
+ sleep(rand(0.01..0.1))
174
+ record_count = rand(0..20)
175
+ {
176
+ rows_affected: record_count,
177
+ data: record_count.times.map { |i| { id: i + 1, name: "Record #{i + 1}" } },
178
+ time: rand(0.01..0.05)
179
+ }
180
+ end
181
+
182
+ def perform_insert(_table, _data)
183
+ # Insert query simulation
184
+ sleep(rand(0.01..0.05))
185
+ {
186
+ rows_affected: 1,
187
+ data: { id: rand(1000..9999) },
188
+ time: rand(0.01..0.03)
189
+ }
190
+ end
191
+
192
+ def perform_update(_table, _data, _conditions)
193
+ # Update query simulation
194
+ sleep(rand(0.01..0.1))
195
+ affected = rand(0..10)
196
+ {
197
+ rows_affected: affected,
198
+ data: nil,
199
+ time: rand(0.01..0.05)
200
+ }
201
+ end
202
+
203
+ def perform_delete(_table, _conditions)
204
+ # Delete query simulation
205
+ sleep(rand(0.01..0.05))
206
+ affected = rand(0..5)
207
+ {
208
+ rows_affected: affected,
209
+ data: nil,
210
+ time: rand(0.01..0.03)
211
+ }
212
+ end
213
+
214
+ def default_query(_type, _table, _conditions)
215
+ # Default query handling
216
+ sleep(rand(0.01..0.02))
217
+ {
218
+ rows_affected: 0,
219
+ data: nil,
220
+ time: rand(0.005..0.01)
221
+ }
222
+ end
223
+ end
224
+
225
+ # Controller class that manages both worker types
226
+ class HybridSystem
227
+ attr_reader :compute_results, :db_results
228
+
229
+ def initialize(compute_workers: 2, db_workers: 2)
230
+ # Create separate supervisors for each worker type
231
+ @compute_supervisor = Fractor::Supervisor.new(
232
+ worker_pools: [
233
+ { worker_class: ComputeWorker, num_workers: compute_workers }
234
+ ]
235
+ )
236
+
237
+ @db_supervisor = Fractor::Supervisor.new(
238
+ worker_pools: [
239
+ { worker_class: DatabaseWorker, num_workers: db_workers }
240
+ ]
241
+ )
242
+
243
+ @compute_results = []
244
+ @db_results = []
245
+ end
246
+
247
+ def process_mixed_workload(compute_tasks, db_tasks)
248
+ # Create and add compute work items
249
+ compute_work_items = compute_tasks.map do |task|
250
+ ComputeWork.new(task[:data], task[:operation], task[:parameters])
251
+ end
252
+ @compute_supervisor.add_work_items(compute_work_items)
253
+
254
+ # Create and add database work items
255
+ db_work_items = db_tasks.map do |task|
256
+ DatabaseWork.new(task[:data], task[:query_type], task[:table], task[:conditions])
257
+ end
258
+ @db_supervisor.add_work_items(db_work_items)
259
+
260
+ # Run the supervisors directly - this is more reliable
261
+ @compute_supervisor.run
262
+ @db_supervisor.run
263
+
264
+ # Get results directly from the supervisors
265
+ compute_results_agg = @compute_supervisor.results
266
+ db_results_agg = @db_supervisor.results
267
+
268
+ puts "Received compute results: #{compute_results_agg.results.size} items"
269
+ puts "Received database results: #{db_results_agg.results.size} items"
270
+
271
+ # Format and store results
272
+ @compute_results = format_compute_results(compute_results_agg)
273
+ @db_results = format_db_results(db_results_agg)
274
+
275
+ # Return combined results
276
+ {
277
+ computation: {
278
+ tasks: compute_tasks.size,
279
+ completed: @compute_results.size,
280
+ results: @compute_results
281
+ },
282
+ database: {
283
+ tasks: db_tasks.size,
284
+ completed: @db_results.size,
285
+ results: @db_results
286
+ }
287
+ }
288
+ end
289
+
290
+ private
291
+
292
+ def format_compute_results(results_aggregator)
293
+ # Format computation results
294
+ results_aggregator.results.map(&:result)
295
+ end
296
+
297
+ def format_db_results(results_aggregator)
298
+ # Format database results
299
+ results_aggregator.results.map(&:result)
300
+ end
301
+ end
302
+ end
303
+
304
+ # Example usage
305
+ if __FILE__ == $PROGRAM_NAME
306
+ puts "Starting Specialized Workers Example"
307
+ puts "==================================="
308
+ puts "This example demonstrates two specialized worker types:"
309
+ puts "1. ComputeWorker: Handles compute-intensive operations"
310
+ puts "2. DatabaseWorker: Handles database operations"
311
+ puts "Each worker is designed to process a specific type of work."
312
+ puts
313
+
314
+ # Prepare compute tasks
315
+ compute_tasks = [
316
+ {
317
+ operation: :matrix_multiply,
318
+ data: "Matrix data...",
319
+ parameters: { size: [10, 10] }
320
+ },
321
+ {
322
+ operation: :image_transform,
323
+ data: "Image data...",
324
+ parameters: { transforms: %i[rotate scale blur], angle: 45, scale: 1.5 }
325
+ },
326
+ {
327
+ operation: :path_finding,
328
+ data: "Graph data...",
329
+ parameters: { algorithm: :dijkstra, nodes: 20, start: 1, end: 15 }
330
+ }
331
+ ]
332
+
333
+ # Prepare database tasks
334
+ db_tasks = [
335
+ {
336
+ query_type: :select,
337
+ table: "users",
338
+ conditions: { active: true, role: "admin" }
339
+ },
340
+ {
341
+ query_type: :insert,
342
+ table: "orders",
343
+ data: "Order data..."
344
+ },
345
+ {
346
+ query_type: :update,
347
+ table: "products",
348
+ data: "Product data...",
349
+ conditions: { category: "electronics" }
350
+ },
351
+ {
352
+ query_type: :delete,
353
+ table: "sessions",
354
+ conditions: { expired: true }
355
+ }
356
+ ]
357
+
358
+ compute_workers = 2
359
+ db_workers = 2
360
+ puts "Processing with #{compute_workers} compute workers and #{db_workers} database workers..."
361
+ puts
362
+
363
+ start_time = Time.now
364
+ system = SpecializedWorkers::HybridSystem.new(
365
+ compute_workers: compute_workers,
366
+ db_workers: db_workers
367
+ )
368
+ result = system.process_mixed_workload(compute_tasks, db_tasks)
369
+ end_time = Time.now
370
+
371
+ puts "Processing Results:"
372
+ puts "-----------------"
373
+ puts "Compute Tasks: #{result[:computation][:tasks]} submitted, #{result[:computation][:completed]} completed"
374
+ puts "Database Tasks: #{result[:database][:tasks]} submitted, #{result[:database][:completed]} completed"
375
+ puts
376
+
377
+ puts "Computation Results:"
378
+ result[:computation][:results].each_with_index do |compute_result, index|
379
+ puts "Task #{index + 1} (#{compute_result[:operation]}):"
380
+ puts " Result: #{compute_result[:computation_result]}"
381
+ puts " Resources: #{compute_result[:resources_used]}"
382
+ puts
383
+ end
384
+
385
+ puts "Database Results:"
386
+ result[:database][:results].each_with_index do |db_result, index|
387
+ puts "Query #{index + 1} (#{db_result[:query_type]} on #{db_result[:table]}):"
388
+ puts " Rows affected: #{db_result[:rows_affected]}"
389
+ puts " Execution time: #{db_result[:execution_time]} seconds"
390
+ puts " Data: #{db_result[:data].to_s[0..60]}..." unless db_result[:data].nil?
391
+ puts
392
+ end
393
+
394
+ puts "Processing completed in #{end_time - start_time} seconds"
395
+ end
@@ -8,16 +8,25 @@ module Fractor
8
8
  def initialize
9
9
  @results = []
10
10
  @errors = []
11
+ @result_callbacks = []
11
12
  end
12
13
 
13
14
  def add_result(result)
14
15
  if result.success?
15
- puts "Work completed successfully: #{result}"
16
+ puts "Work completed successfully: Result: #{result.result}"
16
17
  @results << result
17
18
  else
18
19
  puts "Error processing work: #{result}"
19
20
  @errors << result
20
21
  end
22
+
23
+ # Call any registered callbacks with the new result
24
+ @result_callbacks.each { |callback| callback.call(result) }
25
+ end
26
+
27
+ # Register a callback to be called when a new result is added
28
+ def on_new_result(&callback)
29
+ @result_callbacks << callback
21
30
  end
22
31
 
23
32
  def to_s