fractor 0.1.4 → 0.1.7
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.
- checksums.yaml +4 -4
- data/.rubocop-https---raw-githubusercontent-com-riboseinc-oss-guides-main-ci-rubocop-yml +552 -0
- data/.rubocop.yml +14 -8
- data/.rubocop_todo.yml +284 -43
- data/README.adoc +111 -950
- data/docs/.lycheeignore +16 -0
- data/docs/Gemfile +24 -0
- data/docs/README.md +157 -0
- data/docs/_config.yml +151 -0
- data/docs/_features/error-handling.adoc +1192 -0
- data/docs/_features/index.adoc +80 -0
- data/docs/_features/monitoring.adoc +589 -0
- data/docs/_features/signal-handling.adoc +202 -0
- data/docs/_features/workflows.adoc +1235 -0
- data/docs/_guides/continuous-mode.adoc +736 -0
- data/docs/_guides/cookbook.adoc +1133 -0
- data/docs/_guides/index.adoc +55 -0
- data/docs/_guides/pipeline-mode.adoc +730 -0
- data/docs/_guides/troubleshooting.adoc +358 -0
- data/docs/_pages/architecture.adoc +1390 -0
- data/docs/_pages/core-concepts.adoc +1392 -0
- data/docs/_pages/design-principles.adoc +862 -0
- data/docs/_pages/getting-started.adoc +290 -0
- data/docs/_pages/installation.adoc +143 -0
- data/docs/_reference/api.adoc +1080 -0
- data/docs/_reference/error-reporting.adoc +670 -0
- data/docs/_reference/examples.adoc +181 -0
- data/docs/_reference/index.adoc +96 -0
- data/docs/_reference/troubleshooting.adoc +862 -0
- data/docs/_tutorials/complex-workflows.adoc +1022 -0
- data/docs/_tutorials/data-processing-pipeline.adoc +740 -0
- data/docs/_tutorials/first-application.adoc +384 -0
- data/docs/_tutorials/index.adoc +48 -0
- data/docs/_tutorials/long-running-services.adoc +931 -0
- data/docs/assets/images/favicon-16.png +0 -0
- data/docs/assets/images/favicon-32.png +0 -0
- data/docs/assets/images/favicon-48.png +0 -0
- data/docs/assets/images/favicon.ico +0 -0
- data/docs/assets/images/favicon.png +0 -0
- data/docs/assets/images/favicon.svg +45 -0
- data/docs/assets/images/fractor-icon.svg +49 -0
- data/docs/assets/images/fractor-logo.svg +61 -0
- data/docs/index.adoc +131 -0
- data/docs/lychee.toml +39 -0
- data/examples/api_aggregator/README.adoc +627 -0
- data/examples/api_aggregator/api_aggregator.rb +376 -0
- data/examples/auto_detection/README.adoc +407 -29
- data/examples/auto_detection/auto_detection.rb +9 -9
- data/examples/continuous_chat_common/message_protocol.rb +53 -0
- data/examples/continuous_chat_fractor/README.adoc +217 -0
- data/examples/continuous_chat_fractor/chat_client.rb +303 -0
- data/examples/continuous_chat_fractor/chat_common.rb +83 -0
- data/examples/continuous_chat_fractor/chat_server.rb +167 -0
- data/examples/continuous_chat_fractor/simulate.rb +345 -0
- data/examples/continuous_chat_server/README.adoc +135 -0
- data/examples/continuous_chat_server/chat_client.rb +303 -0
- data/examples/continuous_chat_server/chat_server.rb +359 -0
- data/examples/continuous_chat_server/simulate.rb +343 -0
- data/examples/error_reporting.rb +207 -0
- data/examples/file_processor/README.adoc +170 -0
- data/examples/file_processor/file_processor.rb +615 -0
- data/examples/file_processor/sample_files/invalid.csv +1 -0
- data/examples/file_processor/sample_files/orders.xml +24 -0
- data/examples/file_processor/sample_files/products.json +23 -0
- data/examples/file_processor/sample_files/users.csv +6 -0
- data/examples/hierarchical_hasher/README.adoc +629 -41
- data/examples/hierarchical_hasher/hierarchical_hasher.rb +12 -8
- data/examples/image_processor/README.adoc +610 -0
- data/examples/image_processor/image_processor.rb +349 -0
- data/examples/image_processor/processed_images/sample_10_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_1_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_2_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_3_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_4_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_5_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_6_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_7_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_8_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_9_processed.jpg.json +12 -0
- data/examples/image_processor/test_images/sample_1.png +1 -0
- data/examples/image_processor/test_images/sample_10.png +1 -0
- data/examples/image_processor/test_images/sample_2.png +1 -0
- data/examples/image_processor/test_images/sample_3.png +1 -0
- data/examples/image_processor/test_images/sample_4.png +1 -0
- data/examples/image_processor/test_images/sample_5.png +1 -0
- data/examples/image_processor/test_images/sample_6.png +1 -0
- data/examples/image_processor/test_images/sample_7.png +1 -0
- data/examples/image_processor/test_images/sample_8.png +1 -0
- data/examples/image_processor/test_images/sample_9.png +1 -0
- data/examples/log_analyzer/README.adoc +662 -0
- data/examples/log_analyzer/log_analyzer.rb +579 -0
- data/examples/log_analyzer/sample_logs/apache.log +20 -0
- data/examples/log_analyzer/sample_logs/json.log +15 -0
- data/examples/log_analyzer/sample_logs/nginx.log +15 -0
- data/examples/log_analyzer/sample_logs/rails.log +29 -0
- data/examples/multi_work_type/README.adoc +576 -26
- data/examples/multi_work_type/multi_work_type.rb +30 -29
- data/examples/performance_monitoring.rb +120 -0
- data/examples/pipeline_processing/README.adoc +740 -26
- data/examples/pipeline_processing/pipeline_processing.rb +16 -16
- data/examples/priority_work_example.rb +155 -0
- data/examples/producer_subscriber/README.adoc +889 -46
- data/examples/producer_subscriber/producer_subscriber.rb +20 -16
- data/examples/scatter_gather/README.adoc +829 -27
- data/examples/scatter_gather/scatter_gather.rb +29 -28
- data/examples/simple/README.adoc +347 -0
- data/examples/simple/sample.rb +5 -5
- data/examples/specialized_workers/README.adoc +622 -26
- data/examples/specialized_workers/specialized_workers.rb +88 -45
- data/examples/stream_processor/README.adoc +206 -0
- data/examples/stream_processor/stream_processor.rb +284 -0
- data/examples/web_scraper/README.adoc +625 -0
- data/examples/web_scraper/web_scraper.rb +285 -0
- data/examples/workflow/README.adoc +406 -0
- data/examples/workflow/circuit_breaker/README.adoc +360 -0
- data/examples/workflow/circuit_breaker/circuit_breaker_workflow.rb +225 -0
- data/examples/workflow/conditional/README.adoc +483 -0
- data/examples/workflow/conditional/conditional_workflow.rb +215 -0
- data/examples/workflow/dead_letter_queue/README.adoc +374 -0
- data/examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb +217 -0
- data/examples/workflow/fan_out/README.adoc +381 -0
- data/examples/workflow/fan_out/fan_out_workflow.rb +202 -0
- data/examples/workflow/retry/README.adoc +248 -0
- data/examples/workflow/retry/retry_workflow.rb +195 -0
- data/examples/workflow/simple_linear/README.adoc +267 -0
- data/examples/workflow/simple_linear/simple_linear_workflow.rb +175 -0
- data/examples/workflow/simplified/README.adoc +329 -0
- data/examples/workflow/simplified/simplified_workflow.rb +222 -0
- data/exe/fractor +10 -0
- data/lib/fractor/cli.rb +288 -0
- data/lib/fractor/configuration.rb +307 -0
- data/lib/fractor/continuous_server.rb +183 -0
- data/lib/fractor/error_formatter.rb +72 -0
- data/lib/fractor/error_report_generator.rb +152 -0
- data/lib/fractor/error_reporter.rb +244 -0
- data/lib/fractor/error_statistics.rb +147 -0
- data/lib/fractor/execution_tracer.rb +162 -0
- data/lib/fractor/logger.rb +230 -0
- data/lib/fractor/main_loop_handler.rb +406 -0
- data/lib/fractor/main_loop_handler3.rb +135 -0
- data/lib/fractor/main_loop_handler4.rb +299 -0
- data/lib/fractor/performance_metrics_collector.rb +181 -0
- data/lib/fractor/performance_monitor.rb +215 -0
- data/lib/fractor/performance_report_generator.rb +202 -0
- data/lib/fractor/priority_work.rb +93 -0
- data/lib/fractor/priority_work_queue.rb +189 -0
- data/lib/fractor/result_aggregator.rb +33 -1
- data/lib/fractor/shutdown_handler.rb +168 -0
- data/lib/fractor/signal_handler.rb +80 -0
- data/lib/fractor/supervisor.rb +430 -144
- data/lib/fractor/supervisor_logger.rb +88 -0
- data/lib/fractor/version.rb +1 -1
- data/lib/fractor/work.rb +12 -0
- data/lib/fractor/work_distribution_manager.rb +151 -0
- data/lib/fractor/work_queue.rb +88 -0
- data/lib/fractor/work_result.rb +181 -9
- data/lib/fractor/worker.rb +75 -1
- data/lib/fractor/workflow/builder.rb +210 -0
- data/lib/fractor/workflow/chain_builder.rb +169 -0
- data/lib/fractor/workflow/circuit_breaker.rb +183 -0
- data/lib/fractor/workflow/circuit_breaker_orchestrator.rb +208 -0
- data/lib/fractor/workflow/circuit_breaker_registry.rb +112 -0
- data/lib/fractor/workflow/dead_letter_queue.rb +334 -0
- data/lib/fractor/workflow/execution_hooks.rb +39 -0
- data/lib/fractor/workflow/execution_strategy.rb +225 -0
- data/lib/fractor/workflow/execution_trace.rb +134 -0
- data/lib/fractor/workflow/helpers.rb +191 -0
- data/lib/fractor/workflow/job.rb +290 -0
- data/lib/fractor/workflow/job_dependency_validator.rb +120 -0
- data/lib/fractor/workflow/logger.rb +110 -0
- data/lib/fractor/workflow/pre_execution_context.rb +193 -0
- data/lib/fractor/workflow/retry_config.rb +156 -0
- data/lib/fractor/workflow/retry_orchestrator.rb +184 -0
- data/lib/fractor/workflow/retry_strategy.rb +93 -0
- data/lib/fractor/workflow/structured_logger.rb +30 -0
- data/lib/fractor/workflow/type_compatibility_validator.rb +222 -0
- data/lib/fractor/workflow/visualizer.rb +211 -0
- data/lib/fractor/workflow/workflow_context.rb +132 -0
- data/lib/fractor/workflow/workflow_executor.rb +669 -0
- data/lib/fractor/workflow/workflow_result.rb +55 -0
- data/lib/fractor/workflow/workflow_validator.rb +295 -0
- data/lib/fractor/workflow.rb +333 -0
- data/lib/fractor/wrapped_ractor.rb +66 -91
- data/lib/fractor/wrapped_ractor3.rb +161 -0
- data/lib/fractor/wrapped_ractor4.rb +242 -0
- data/lib/fractor.rb +93 -3
- metadata +192 -6
- data/tests/sample.rb.bak +0 -309
- data/tests/sample_working.rb.bak +0 -209
|
@@ -5,11 +5,14 @@ require_relative "../../lib/fractor"
|
|
|
5
5
|
module SpecializedWorkers
|
|
6
6
|
# First work type: Compute-intensive operations
|
|
7
7
|
class ComputeWork < Fractor::Work
|
|
8
|
+
attr_reader :work_type
|
|
9
|
+
|
|
8
10
|
def initialize(data, operation = :default, parameters = {})
|
|
9
11
|
super({
|
|
10
12
|
data: data,
|
|
11
13
|
operation: operation,
|
|
12
|
-
parameters: parameters
|
|
14
|
+
parameters: parameters,
|
|
15
|
+
work_type: :compute, # Add work type identifier for Ractor compatibility
|
|
13
16
|
})
|
|
14
17
|
end
|
|
15
18
|
|
|
@@ -25,6 +28,10 @@ module SpecializedWorkers
|
|
|
25
28
|
input[:parameters]
|
|
26
29
|
end
|
|
27
30
|
|
|
31
|
+
def work_type
|
|
32
|
+
input[:work_type]
|
|
33
|
+
end
|
|
34
|
+
|
|
28
35
|
def to_s
|
|
29
36
|
"ComputeWork: operation=#{operation}, parameters=#{parameters}"
|
|
30
37
|
end
|
|
@@ -32,12 +39,16 @@ module SpecializedWorkers
|
|
|
32
39
|
|
|
33
40
|
# Second work type: Database operations
|
|
34
41
|
class DatabaseWork < Fractor::Work
|
|
35
|
-
|
|
42
|
+
attr_reader :work_type
|
|
43
|
+
|
|
44
|
+
def initialize(data = "", query_type = :select, table = "unknown",
|
|
45
|
+
conditions = {})
|
|
36
46
|
super({
|
|
37
47
|
data: data,
|
|
38
48
|
query_type: query_type,
|
|
39
49
|
table: table,
|
|
40
|
-
conditions: conditions
|
|
50
|
+
conditions: conditions,
|
|
51
|
+
work_type: :database, # Add work type identifier for Ractor compatibility
|
|
41
52
|
})
|
|
42
53
|
end
|
|
43
54
|
|
|
@@ -57,6 +68,10 @@ module SpecializedWorkers
|
|
|
57
68
|
input[:conditions]
|
|
58
69
|
end
|
|
59
70
|
|
|
71
|
+
def work_type
|
|
72
|
+
input[:work_type]
|
|
73
|
+
end
|
|
74
|
+
|
|
60
75
|
def to_s
|
|
61
76
|
"DatabaseWork: query_type=#{query_type}, table=#{table}, conditions=#{conditions}"
|
|
62
77
|
end
|
|
@@ -64,24 +79,28 @@ module SpecializedWorkers
|
|
|
64
79
|
|
|
65
80
|
# First worker type: Handles compute-intensive operations
|
|
66
81
|
class ComputeWorker < Fractor::Worker
|
|
67
|
-
def initialize
|
|
82
|
+
def initialize(name: nil, **options)
|
|
83
|
+
super
|
|
68
84
|
# Setup resources needed for computation
|
|
69
|
-
|
|
85
|
+
# Use Ractor.make_shareable to make the hash shareable across Ractors
|
|
86
|
+
@compute_resources = Ractor.make_shareable({ memory: 1024, cpu_cores: 4 })
|
|
70
87
|
end
|
|
71
88
|
|
|
72
89
|
def process(work)
|
|
73
|
-
# Only handle ComputeWork
|
|
74
|
-
unless work.
|
|
90
|
+
# Only handle ComputeWork - check work_type for Ractor compatibility
|
|
91
|
+
unless work.respond_to?(:work_type) && work.work_type == :compute
|
|
75
92
|
return Fractor::WorkResult.new(
|
|
76
93
|
error: "ComputeWorker can only process ComputeWork, got: #{work.class}",
|
|
77
|
-
work: work
|
|
94
|
+
work: work,
|
|
78
95
|
)
|
|
79
96
|
end
|
|
80
97
|
|
|
81
98
|
# Process based on the requested operation
|
|
82
99
|
result = case work.operation
|
|
83
|
-
when :matrix_multiply then matrix_multiply(work.data,
|
|
84
|
-
|
|
100
|
+
when :matrix_multiply then matrix_multiply(work.data,
|
|
101
|
+
work.parameters)
|
|
102
|
+
when :image_transform then image_transform(work.data,
|
|
103
|
+
work.parameters)
|
|
85
104
|
when :path_finding then path_finding(work.data, work.parameters)
|
|
86
105
|
else default_computation(work.data, work.parameters)
|
|
87
106
|
end
|
|
@@ -90,9 +109,9 @@ module SpecializedWorkers
|
|
|
90
109
|
result: {
|
|
91
110
|
operation: work.operation,
|
|
92
111
|
computation_result: result,
|
|
93
|
-
resources_used: @compute_resources
|
|
112
|
+
resources_used: @compute_resources,
|
|
94
113
|
},
|
|
95
|
-
work: work
|
|
114
|
+
work: work,
|
|
96
115
|
)
|
|
97
116
|
end
|
|
98
117
|
|
|
@@ -110,7 +129,7 @@ module SpecializedWorkers
|
|
|
110
129
|
# Simulate image transformation
|
|
111
130
|
sleep(rand(0.1..0.3))
|
|
112
131
|
transforms = params[:transforms] || %i[rotate scale]
|
|
113
|
-
"Image transformation applied: #{transforms.join(
|
|
132
|
+
"Image transformation applied: #{transforms.join(', ')} with parameters #{params}"
|
|
114
133
|
end
|
|
115
134
|
|
|
116
135
|
def path_finding(_data, params)
|
|
@@ -130,17 +149,19 @@ module SpecializedWorkers
|
|
|
130
149
|
|
|
131
150
|
# Second worker type: Handles database operations
|
|
132
151
|
class DatabaseWorker < Fractor::Worker
|
|
133
|
-
def initialize
|
|
152
|
+
def initialize(name: nil, **options)
|
|
153
|
+
super
|
|
134
154
|
# Setup database connection and resources
|
|
135
|
-
|
|
155
|
+
# Use Ractor.make_shareable to make the hash shareable across Ractors
|
|
156
|
+
@db_connection = Ractor.make_shareable({ pool_size: 5, timeout: 30 })
|
|
136
157
|
end
|
|
137
158
|
|
|
138
159
|
def process(work)
|
|
139
|
-
# Only handle DatabaseWork
|
|
140
|
-
unless work.
|
|
160
|
+
# Only handle DatabaseWork - check work_type for Ractor compatibility
|
|
161
|
+
unless work.respond_to?(:work_type) && work.work_type == :database
|
|
141
162
|
return Fractor::WorkResult.new(
|
|
142
163
|
error: "DatabaseWorker can only process DatabaseWork, got: #{work.class}",
|
|
143
|
-
work: work
|
|
164
|
+
work: work,
|
|
144
165
|
)
|
|
145
166
|
end
|
|
146
167
|
|
|
@@ -148,7 +169,8 @@ module SpecializedWorkers
|
|
|
148
169
|
result = case work.query_type
|
|
149
170
|
when :select then perform_select(work.table, work.conditions)
|
|
150
171
|
when :insert then perform_insert(work.table, work.data)
|
|
151
|
-
when :update then perform_update(work.table, work.data,
|
|
172
|
+
when :update then perform_update(work.table, work.data,
|
|
173
|
+
work.conditions)
|
|
152
174
|
when :delete then perform_delete(work.table, work.conditions)
|
|
153
175
|
else default_query(work.query_type, work.table, work.conditions)
|
|
154
176
|
end
|
|
@@ -159,9 +181,9 @@ module SpecializedWorkers
|
|
|
159
181
|
table: work.table,
|
|
160
182
|
rows_affected: result[:rows_affected],
|
|
161
183
|
data: result[:data],
|
|
162
|
-
execution_time: result[:time]
|
|
184
|
+
execution_time: result[:time],
|
|
163
185
|
},
|
|
164
|
-
work: work
|
|
186
|
+
work: work,
|
|
165
187
|
)
|
|
166
188
|
end
|
|
167
189
|
|
|
@@ -174,8 +196,10 @@ module SpecializedWorkers
|
|
|
174
196
|
record_count = rand(0..20)
|
|
175
197
|
{
|
|
176
198
|
rows_affected: record_count,
|
|
177
|
-
data: record_count
|
|
178
|
-
|
|
199
|
+
data: Array.new(record_count) do |i|
|
|
200
|
+
{ id: i + 1, name: "Record #{i + 1}" }
|
|
201
|
+
end,
|
|
202
|
+
time: rand(0.01..0.05),
|
|
179
203
|
}
|
|
180
204
|
end
|
|
181
205
|
|
|
@@ -185,7 +209,7 @@ module SpecializedWorkers
|
|
|
185
209
|
{
|
|
186
210
|
rows_affected: 1,
|
|
187
211
|
data: { id: rand(1000..9999) },
|
|
188
|
-
time: rand(0.01..0.03)
|
|
212
|
+
time: rand(0.01..0.03),
|
|
189
213
|
}
|
|
190
214
|
end
|
|
191
215
|
|
|
@@ -196,7 +220,7 @@ module SpecializedWorkers
|
|
|
196
220
|
{
|
|
197
221
|
rows_affected: affected,
|
|
198
222
|
data: nil,
|
|
199
|
-
time: rand(0.01..0.05)
|
|
223
|
+
time: rand(0.01..0.05),
|
|
200
224
|
}
|
|
201
225
|
end
|
|
202
226
|
|
|
@@ -207,7 +231,7 @@ module SpecializedWorkers
|
|
|
207
231
|
{
|
|
208
232
|
rows_affected: affected,
|
|
209
233
|
data: nil,
|
|
210
|
-
time: rand(0.01..0.03)
|
|
234
|
+
time: rand(0.01..0.03),
|
|
211
235
|
}
|
|
212
236
|
end
|
|
213
237
|
|
|
@@ -217,7 +241,7 @@ module SpecializedWorkers
|
|
|
217
241
|
{
|
|
218
242
|
rows_affected: 0,
|
|
219
243
|
data: nil,
|
|
220
|
-
time: rand(0.005..0.01)
|
|
244
|
+
time: rand(0.005..0.01),
|
|
221
245
|
}
|
|
222
246
|
end
|
|
223
247
|
end
|
|
@@ -230,14 +254,14 @@ module SpecializedWorkers
|
|
|
230
254
|
# Create separate supervisors for each worker type
|
|
231
255
|
@compute_supervisor = Fractor::Supervisor.new(
|
|
232
256
|
worker_pools: [
|
|
233
|
-
{ worker_class: ComputeWorker, num_workers: compute_workers }
|
|
234
|
-
]
|
|
257
|
+
{ worker_class: ComputeWorker, num_workers: compute_workers },
|
|
258
|
+
],
|
|
235
259
|
)
|
|
236
260
|
|
|
237
261
|
@db_supervisor = Fractor::Supervisor.new(
|
|
238
262
|
worker_pools: [
|
|
239
|
-
{ worker_class: DatabaseWorker, num_workers: db_workers }
|
|
240
|
-
]
|
|
263
|
+
{ worker_class: DatabaseWorker, num_workers: db_workers },
|
|
264
|
+
],
|
|
241
265
|
)
|
|
242
266
|
|
|
243
267
|
@compute_results = []
|
|
@@ -249,13 +273,20 @@ module SpecializedWorkers
|
|
|
249
273
|
compute_work_items = compute_tasks.map do |task|
|
|
250
274
|
ComputeWork.new(task[:data], task[:operation], task[:parameters])
|
|
251
275
|
end
|
|
276
|
+
puts "Created #{compute_work_items.size} compute work items"
|
|
277
|
+
puts "First compute work item: #{compute_work_items.first.inspect}" if compute_work_items.any?
|
|
252
278
|
@compute_supervisor.add_work_items(compute_work_items)
|
|
279
|
+
puts "Added compute work items to supervisor"
|
|
253
280
|
|
|
254
281
|
# Create and add database work items
|
|
255
282
|
db_work_items = db_tasks.map do |task|
|
|
256
|
-
DatabaseWork.new(task[:data], task[:query_type], task[:table],
|
|
283
|
+
DatabaseWork.new(task[:data], task[:query_type], task[:table],
|
|
284
|
+
task[:conditions])
|
|
257
285
|
end
|
|
286
|
+
puts "Created #{db_work_items.size} database work items"
|
|
287
|
+
puts "First db work item: #{db_work_items.first.inspect}" if db_work_items.any?
|
|
258
288
|
@db_supervisor.add_work_items(db_work_items)
|
|
289
|
+
puts "Added database work items to supervisor"
|
|
259
290
|
|
|
260
291
|
# Run the supervisors directly - this is more reliable
|
|
261
292
|
@compute_supervisor.run
|
|
@@ -266,7 +297,19 @@ module SpecializedWorkers
|
|
|
266
297
|
db_results_agg = @db_supervisor.results
|
|
267
298
|
|
|
268
299
|
puts "Received compute results: #{compute_results_agg.results.size} items"
|
|
300
|
+
puts "Received compute errors: #{compute_results_agg.errors.size} items"
|
|
301
|
+
if compute_results_agg.errors.any?
|
|
302
|
+
compute_results_agg.errors.each do |e|
|
|
303
|
+
puts " Error: #{e.error}"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
269
306
|
puts "Received database results: #{db_results_agg.results.size} items"
|
|
307
|
+
puts "Received database errors: #{db_results_agg.errors.size} items"
|
|
308
|
+
if db_results_agg.errors.any?
|
|
309
|
+
db_results_agg.errors.each do |e|
|
|
310
|
+
puts " Error: #{e.error}"
|
|
311
|
+
end
|
|
312
|
+
end
|
|
270
313
|
|
|
271
314
|
# Format and store results
|
|
272
315
|
@compute_results = format_compute_results(compute_results_agg)
|
|
@@ -277,13 +320,13 @@ module SpecializedWorkers
|
|
|
277
320
|
computation: {
|
|
278
321
|
tasks: compute_tasks.size,
|
|
279
322
|
completed: @compute_results.size,
|
|
280
|
-
results: @compute_results
|
|
323
|
+
results: @compute_results,
|
|
281
324
|
},
|
|
282
325
|
database: {
|
|
283
326
|
tasks: db_tasks.size,
|
|
284
327
|
completed: @db_results.size,
|
|
285
|
-
results: @db_results
|
|
286
|
-
}
|
|
328
|
+
results: @db_results,
|
|
329
|
+
},
|
|
287
330
|
}
|
|
288
331
|
end
|
|
289
332
|
|
|
@@ -316,18 +359,18 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
316
359
|
{
|
|
317
360
|
operation: :matrix_multiply,
|
|
318
361
|
data: "Matrix data...",
|
|
319
|
-
parameters: { size: [10, 10] }
|
|
362
|
+
parameters: { size: [10, 10] },
|
|
320
363
|
},
|
|
321
364
|
{
|
|
322
365
|
operation: :image_transform,
|
|
323
366
|
data: "Image data...",
|
|
324
|
-
parameters: { transforms: %i[rotate scale blur], angle: 45, scale: 1.5 }
|
|
367
|
+
parameters: { transforms: %i[rotate scale blur], angle: 45, scale: 1.5 },
|
|
325
368
|
},
|
|
326
369
|
{
|
|
327
370
|
operation: :path_finding,
|
|
328
371
|
data: "Graph data...",
|
|
329
|
-
parameters: { algorithm: :dijkstra, nodes: 20, start: 1, end: 15 }
|
|
330
|
-
}
|
|
372
|
+
parameters: { algorithm: :dijkstra, nodes: 20, start: 1, end: 15 },
|
|
373
|
+
},
|
|
331
374
|
]
|
|
332
375
|
|
|
333
376
|
# Prepare database tasks
|
|
@@ -335,24 +378,24 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
335
378
|
{
|
|
336
379
|
query_type: :select,
|
|
337
380
|
table: "users",
|
|
338
|
-
conditions: { active: true, role: "admin" }
|
|
381
|
+
conditions: { active: true, role: "admin" },
|
|
339
382
|
},
|
|
340
383
|
{
|
|
341
384
|
query_type: :insert,
|
|
342
385
|
table: "orders",
|
|
343
|
-
data: "Order data..."
|
|
386
|
+
data: "Order data...",
|
|
344
387
|
},
|
|
345
388
|
{
|
|
346
389
|
query_type: :update,
|
|
347
390
|
table: "products",
|
|
348
391
|
data: "Product data...",
|
|
349
|
-
conditions: { category: "electronics" }
|
|
392
|
+
conditions: { category: "electronics" },
|
|
350
393
|
},
|
|
351
394
|
{
|
|
352
395
|
query_type: :delete,
|
|
353
396
|
table: "sessions",
|
|
354
|
-
conditions: { expired: true }
|
|
355
|
-
}
|
|
397
|
+
conditions: { expired: true },
|
|
398
|
+
},
|
|
356
399
|
]
|
|
357
400
|
|
|
358
401
|
compute_workers = 2
|
|
@@ -363,7 +406,7 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
363
406
|
start_time = Time.now
|
|
364
407
|
system = SpecializedWorkers::HybridSystem.new(
|
|
365
408
|
compute_workers: compute_workers,
|
|
366
|
-
db_workers: db_workers
|
|
409
|
+
db_workers: db_workers,
|
|
367
410
|
)
|
|
368
411
|
result = system.process_mixed_workload(compute_tasks, db_tasks)
|
|
369
412
|
end_time = Time.now
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
= Real-Time Data Stream Processor Example
|
|
2
|
+
:toc:
|
|
3
|
+
:toclevels: 3
|
|
4
|
+
|
|
5
|
+
Real-time event stream processor using Fractor::ContinuousServer for processing continuous data streams with windowing, metrics tracking, and graceful shutdown.
|
|
6
|
+
|
|
7
|
+
== Purpose
|
|
8
|
+
|
|
9
|
+
This example demonstrates:
|
|
10
|
+
|
|
11
|
+
* Continuous event processing with Fractor::ContinuousServer
|
|
12
|
+
* Sliding window aggregation (5-second windows)
|
|
13
|
+
* Real-time metrics tracking
|
|
14
|
+
* Backpressure handling
|
|
15
|
+
* Graceful shutdown with SIGINT/SIGTERM
|
|
16
|
+
* Performance monitoring
|
|
17
|
+
|
|
18
|
+
== Features
|
|
19
|
+
|
|
20
|
+
=== Continuous Processing
|
|
21
|
+
|
|
22
|
+
Processes events as they arrive without stopping:
|
|
23
|
+
|
|
24
|
+
* Non-blocking event ingestion
|
|
25
|
+
* Parallel processing across workers
|
|
26
|
+
* Result collection in background thread
|
|
27
|
+
* Automatic workload distribution
|
|
28
|
+
|
|
29
|
+
=== Sliding Window Aggregation
|
|
30
|
+
|
|
31
|
+
Maintains a sliding time window of recent events:
|
|
32
|
+
|
|
33
|
+
* Configurable window size (default: 5 seconds)
|
|
34
|
+
* Automatic cleanup of old events
|
|
35
|
+
* Window-based metrics calculation
|
|
36
|
+
* Real-time window size tracking
|
|
37
|
+
|
|
38
|
+
=== Performance Metrics
|
|
39
|
+
|
|
40
|
+
Tracks and displays real-time metrics:
|
|
41
|
+
|
|
42
|
+
* Total events processed
|
|
43
|
+
* Events per second
|
|
44
|
+
* Average processing time
|
|
45
|
+
* Current window size
|
|
46
|
+
|
|
47
|
+
=== Graceful Shutdown
|
|
48
|
+
|
|
49
|
+
Handles termination signals properly:
|
|
50
|
+
|
|
51
|
+
* SIGINT (Ctrl+C) for manual shutdown
|
|
52
|
+
* SIGTERM for system shutdown
|
|
53
|
+
* Preserves in-flight work
|
|
54
|
+
* Final summary report
|
|
55
|
+
|
|
56
|
+
== Usage
|
|
57
|
+
|
|
58
|
+
=== Basic Usage
|
|
59
|
+
|
|
60
|
+
Run with default settings (30 seconds, 10 events/second):
|
|
61
|
+
|
|
62
|
+
[source,bash]
|
|
63
|
+
----
|
|
64
|
+
ruby stream_processor.rb
|
|
65
|
+
----
|
|
66
|
+
|
|
67
|
+
=== Custom Duration
|
|
68
|
+
|
|
69
|
+
Process for 60 seconds:
|
|
70
|
+
|
|
71
|
+
[source,bash]
|
|
72
|
+
----
|
|
73
|
+
ruby stream_processor.rb -d 60
|
|
74
|
+
----
|
|
75
|
+
|
|
76
|
+
=== Custom Event Rate
|
|
77
|
+
|
|
78
|
+
Generate 50 events per second:
|
|
79
|
+
|
|
80
|
+
[source,bash]
|
|
81
|
+
----
|
|
82
|
+
ruby stream_processor.rb -r 50
|
|
83
|
+
----
|
|
84
|
+
|
|
85
|
+
=== Custom Workers
|
|
86
|
+
|
|
87
|
+
Use 8 worker ractors:
|
|
88
|
+
|
|
89
|
+
[source,bash]
|
|
90
|
+
----
|
|
91
|
+
ruby stream_processor.rb -w 8
|
|
92
|
+
----
|
|
93
|
+
|
|
94
|
+
=== Custom Window Size
|
|
95
|
+
|
|
96
|
+
Use 10-second window:
|
|
97
|
+
|
|
98
|
+
[source,bash]
|
|
99
|
+
----
|
|
100
|
+
ruby stream_processor.rb --window 10
|
|
101
|
+
----
|
|
102
|
+
|
|
103
|
+
=== Combined Options
|
|
104
|
+
|
|
105
|
+
[source,bash]
|
|
106
|
+
----
|
|
107
|
+
ruby stream_processor.rb -w 8 -d 120 -r 100 --window 10
|
|
108
|
+
----
|
|
109
|
+
|
|
110
|
+
== Examples
|
|
111
|
+
|
|
112
|
+
=== Example 1: Default Settings
|
|
113
|
+
|
|
114
|
+
[source,bash]
|
|
115
|
+
----
|
|
116
|
+
$ ruby stream_processor.rb
|
|
117
|
+
|
|
118
|
+
Starting Stream Processor...
|
|
119
|
+
Window size: 5 seconds
|
|
120
|
+
Workers: 4
|
|
121
|
+
Press Ctrl+C to stop gracefully
|
|
122
|
+
|
|
123
|
+
Generating event stream for 30 seconds at 10 events/second...
|
|
124
|
+
|
|
125
|
+
Events: 300 | Processed: 298 | Rate: 9.93 e/s | Window: 47 | Avg Time: 0.12 ms
|
|
126
|
+
|
|
127
|
+
Generated 300 events
|
|
128
|
+
|
|
129
|
+
Stopping Stream Processor...
|
|
130
|
+
|
|
131
|
+
============================================================
|
|
132
|
+
FINAL SUMMARY
|
|
133
|
+
============================================================
|
|
134
|
+
Total Events: 300
|
|
135
|
+
Processed: 300
|
|
136
|
+
Errors: 0
|
|
137
|
+
Duration: 30.25 seconds
|
|
138
|
+
Average Rate: 9.92 events/second
|
|
139
|
+
============================================================
|
|
140
|
+
----
|
|
141
|
+
|
|
142
|
+
=== Example 2: High Throughput
|
|
143
|
+
|
|
144
|
+
[source,bash]
|
|
145
|
+
----
|
|
146
|
+
$ ruby stream_processor.rb -w 8 -r 100 -d 10
|
|
147
|
+
|
|
148
|
+
Starting Stream Processor...
|
|
149
|
+
Window size: 5 seconds
|
|
150
|
+
Workers: 8
|
|
151
|
+
|
|
152
|
+
Events: 1000 | Processed: 998 | Rate: 99.8 e/s | Window: 492 | Avg Time: 0.08 ms
|
|
153
|
+
|
|
154
|
+
Generated 1000 events
|
|
155
|
+
|
|
156
|
+
FINAL SUMMARY
|
|
157
|
+
============================================================
|
|
158
|
+
Total Events: 1000
|
|
159
|
+
Processed: 1000
|
|
160
|
+
Duration: 10.05 seconds
|
|
161
|
+
Average Rate: 99.50 events/second
|
|
162
|
+
----
|
|
163
|
+
|
|
164
|
+
=== Example 3: Graceful Shutdown
|
|
165
|
+
|
|
166
|
+
Press Ctrl+C during processing:
|
|
167
|
+
|
|
168
|
+
[source,bash]
|
|
169
|
+
----
|
|
170
|
+
$ ruby stream_processor.rb
|
|
171
|
+
|
|
172
|
+
Events: 150 | Processed: 148 | Rate: 10.1 e/s | Window: 48 | Avg Time: 0.10 ms
|
|
173
|
+
^C
|
|
174
|
+
Stopping Stream Processor...
|
|
175
|
+
|
|
176
|
+
FINAL SUMMARY
|
|
177
|
+
============================================================
|
|
178
|
+
Total Events: 150
|
|
179
|
+
Processed: 150
|
|
180
|
+
Duration: 14.87 seconds
|
|
181
|
+
Average Rate: 10.09 events/second
|
|
182
|
+
----
|
|
183
|
+
|
|
184
|
+
== Architecture
|
|
185
|
+
|
|
186
|
+
The processor consists of:
|
|
187
|
+
|
|
188
|
+
* **Event**: Work item representing a stream event
|
|
189
|
+
* **EventProcessorWorker**: Worker that processes events
|
|
190
|
+
* **StreamProcessor**: Main processor managing the continuous server
|
|
191
|
+
* **EventGenerator**: Test utility for generating event streams
|
|
192
|
+
|
|
193
|
+
== Testing
|
|
194
|
+
|
|
195
|
+
Run the test suite:
|
|
196
|
+
|
|
197
|
+
[source,bash]
|
|
198
|
+
----
|
|
199
|
+
bundle exec rspec spec/examples/stream_processor_spec.rb
|
|
200
|
+
----
|
|
201
|
+
|
|
202
|
+
== See Also
|
|
203
|
+
|
|
204
|
+
* link:../../README.adoc[Fractor Main Documentation]
|
|
205
|
+
* link:../../docs/continuous-mode.adoc[Continuous Mode Guide]
|
|
206
|
+
* link:../log_analyzer/README.adoc[Log Analyzer Example]
|