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
|
@@ -0,0 +1,1080 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: API Reference
|
|
4
|
+
nav_order: 2
|
|
5
|
+
---
|
|
6
|
+
= API Reference
|
|
7
|
+
|
|
8
|
+
This document provides a comprehensive reference for the Fractor API, including all public classes, methods, and their usage.
|
|
9
|
+
|
|
10
|
+
== Core Classes
|
|
11
|
+
|
|
12
|
+
=== Fractor::Work
|
|
13
|
+
|
|
14
|
+
Represents a unit of work to be processed by workers.
|
|
15
|
+
|
|
16
|
+
==== Class Methods
|
|
17
|
+
|
|
18
|
+
===== `new(payload)`
|
|
19
|
+
|
|
20
|
+
Creates a new Work instance.
|
|
21
|
+
|
|
22
|
+
[cols="1,1,3"]
|
|
23
|
+
|===
|
|
24
|
+
|Parameter |Type |Description
|
|
25
|
+
|
|
26
|
+
|`payload`
|
|
27
|
+
|Hash or Any
|
|
28
|
+
|The data to be processed by the worker
|
|
29
|
+
|===
|
|
30
|
+
|
|
31
|
+
**Example:**
|
|
32
|
+
|
|
33
|
+
[source,ruby]
|
|
34
|
+
----
|
|
35
|
+
work = Fractor::Work.new(value: 42, operation: :double)
|
|
36
|
+
----
|
|
37
|
+
|
|
38
|
+
==== Instance Methods
|
|
39
|
+
|
|
40
|
+
===== `input`
|
|
41
|
+
|
|
42
|
+
Returns the work payload.
|
|
43
|
+
|
|
44
|
+
**Returns:** The payload passed during initialization
|
|
45
|
+
|
|
46
|
+
**Example:**
|
|
47
|
+
|
|
48
|
+
[source,ruby]
|
|
49
|
+
----
|
|
50
|
+
work = Fractor::Work.new(value: 42)
|
|
51
|
+
work.input # => { value: 42 }
|
|
52
|
+
----
|
|
53
|
+
|
|
54
|
+
=== Fractor::Worker
|
|
55
|
+
|
|
56
|
+
Base class for all workers. Workers process Work objects and return WorkResult objects.
|
|
57
|
+
|
|
58
|
+
==== Class Methods
|
|
59
|
+
|
|
60
|
+
===== `perform(work)`
|
|
61
|
+
|
|
62
|
+
Main entry point for processing work. Handles error catching and result wrapping.
|
|
63
|
+
|
|
64
|
+
[cols="1,1,3"]
|
|
65
|
+
|===
|
|
66
|
+
|Parameter |Type |Description
|
|
67
|
+
|
|
68
|
+
|`work`
|
|
69
|
+
|Fractor::Work
|
|
70
|
+
|The work item to process
|
|
71
|
+
|===
|
|
72
|
+
|
|
73
|
+
**Returns:** Fractor::WorkResult
|
|
74
|
+
|
|
75
|
+
**Example:**
|
|
76
|
+
|
|
77
|
+
[source,ruby]
|
|
78
|
+
----
|
|
79
|
+
class DoubleWorker < Fractor::Worker
|
|
80
|
+
def process(work)
|
|
81
|
+
result = work.input[:value] * 2
|
|
82
|
+
Fractor::WorkResult.new(result: result, work: work)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
work = Fractor::Work.new(value: 21)
|
|
87
|
+
result = DoubleWorker.perform(work)
|
|
88
|
+
result.result # => 42
|
|
89
|
+
----
|
|
90
|
+
|
|
91
|
+
==== Instance Methods
|
|
92
|
+
|
|
93
|
+
===== `process(work)` (abstract)
|
|
94
|
+
|
|
95
|
+
Process the work and return a result. Must be implemented by subclasses.
|
|
96
|
+
|
|
97
|
+
[cols="1,1,3"]
|
|
98
|
+
|===
|
|
99
|
+
|Parameter |Type |Description
|
|
100
|
+
|
|
101
|
+
|`work`
|
|
102
|
+
|Fractor::Work
|
|
103
|
+
|The work item to process
|
|
104
|
+
|===
|
|
105
|
+
|
|
106
|
+
**Returns:** Fractor::WorkResult
|
|
107
|
+
|
|
108
|
+
**Raises:** NotImplementedError if not overridden
|
|
109
|
+
|
|
110
|
+
**Example:**
|
|
111
|
+
|
|
112
|
+
[source,ruby]
|
|
113
|
+
----
|
|
114
|
+
class CustomWorker < Fractor::Worker
|
|
115
|
+
def process(work)
|
|
116
|
+
# Your processing logic here
|
|
117
|
+
result = perform_computation(work.input)
|
|
118
|
+
Fractor::WorkResult.new(result: result, work: work)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
----
|
|
122
|
+
|
|
123
|
+
=== Fractor::WorkResult
|
|
124
|
+
|
|
125
|
+
Encapsulates the result of work processing, including success/failure status and error information.
|
|
126
|
+
|
|
127
|
+
==== Class Methods
|
|
128
|
+
|
|
129
|
+
===== `new(result: nil, error: nil, work: nil, error_code: nil, error_context: {})`
|
|
130
|
+
|
|
131
|
+
Creates a new WorkResult instance.
|
|
132
|
+
|
|
133
|
+
[cols="1,1,3"]
|
|
134
|
+
|===
|
|
135
|
+
|Parameter |Type |Description
|
|
136
|
+
|
|
137
|
+
|`result`
|
|
138
|
+
|Any
|
|
139
|
+
|The successful result value (optional)
|
|
140
|
+
|
|
141
|
+
|`error`
|
|
142
|
+
|String or Exception
|
|
143
|
+
|Error message or exception (optional)
|
|
144
|
+
|
|
145
|
+
|`work`
|
|
146
|
+
|Fractor::Work
|
|
147
|
+
|The original work item (optional)
|
|
148
|
+
|
|
149
|
+
|`error_code`
|
|
150
|
+
|Symbol
|
|
151
|
+
|Categorized error code (optional)
|
|
152
|
+
|
|
153
|
+
|`error_context`
|
|
154
|
+
|Hash
|
|
155
|
+
|Additional error context (optional)
|
|
156
|
+
|===
|
|
157
|
+
|
|
158
|
+
**Example:**
|
|
159
|
+
|
|
160
|
+
[source,ruby]
|
|
161
|
+
----
|
|
162
|
+
# Success result
|
|
163
|
+
Fractor::WorkResult.new(
|
|
164
|
+
result: { status: "completed" },
|
|
165
|
+
work: work
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Error result with context
|
|
169
|
+
Fractor::WorkResult.new(
|
|
170
|
+
error: "Connection timeout",
|
|
171
|
+
error_code: :timeout,
|
|
172
|
+
error_context: {
|
|
173
|
+
endpoint: "https://api.example.com",
|
|
174
|
+
attempt: 3,
|
|
175
|
+
duration: 30.5
|
|
176
|
+
},
|
|
177
|
+
work: work
|
|
178
|
+
)
|
|
179
|
+
----
|
|
180
|
+
|
|
181
|
+
==== Instance Methods
|
|
182
|
+
|
|
183
|
+
===== `success?`
|
|
184
|
+
|
|
185
|
+
Returns whether the work completed successfully.
|
|
186
|
+
|
|
187
|
+
**Returns:** Boolean
|
|
188
|
+
|
|
189
|
+
**Example:**
|
|
190
|
+
|
|
191
|
+
[source,ruby]
|
|
192
|
+
----
|
|
193
|
+
result.success? # => true or false
|
|
194
|
+
----
|
|
195
|
+
|
|
196
|
+
===== `error?`
|
|
197
|
+
|
|
198
|
+
Returns whether the work encountered an error.
|
|
199
|
+
|
|
200
|
+
**Returns:** Boolean
|
|
201
|
+
|
|
202
|
+
**Example:**
|
|
203
|
+
|
|
204
|
+
[source,ruby]
|
|
205
|
+
----
|
|
206
|
+
result.error? # => true or false
|
|
207
|
+
----
|
|
208
|
+
|
|
209
|
+
===== `result`
|
|
210
|
+
|
|
211
|
+
Returns the result value.
|
|
212
|
+
|
|
213
|
+
**Returns:** The result value or nil
|
|
214
|
+
|
|
215
|
+
===== `error`
|
|
216
|
+
|
|
217
|
+
Returns the error message or exception.
|
|
218
|
+
|
|
219
|
+
**Returns:** String, Exception, or nil
|
|
220
|
+
|
|
221
|
+
===== `work`
|
|
222
|
+
|
|
223
|
+
Returns the original work item.
|
|
224
|
+
|
|
225
|
+
**Returns:** Fractor::Work or nil
|
|
226
|
+
|
|
227
|
+
===== `error_code`
|
|
228
|
+
|
|
229
|
+
Returns the categorized error code.
|
|
230
|
+
|
|
231
|
+
**Returns:** Symbol or nil
|
|
232
|
+
|
|
233
|
+
===== `error_context`
|
|
234
|
+
|
|
235
|
+
Returns additional error context.
|
|
236
|
+
|
|
237
|
+
**Returns:** Hash
|
|
238
|
+
|
|
239
|
+
=== Fractor::Supervisor
|
|
240
|
+
|
|
241
|
+
Manages worker pools and distributes work across workers.
|
|
242
|
+
|
|
243
|
+
==== Class Methods
|
|
244
|
+
|
|
245
|
+
===== `new(worker_pools: [], work_queue: nil, max_queue_size: nil)`
|
|
246
|
+
|
|
247
|
+
Creates a new Supervisor instance.
|
|
248
|
+
|
|
249
|
+
[cols="1,1,3"]
|
|
250
|
+
|===
|
|
251
|
+
|Parameter |Type |Description
|
|
252
|
+
|
|
253
|
+
|`worker_pools`
|
|
254
|
+
|Array<Hash>
|
|
255
|
+
|Array of worker pool configurations
|
|
256
|
+
|
|
257
|
+
|`work_queue`
|
|
258
|
+
|Fractor::WorkQueue
|
|
259
|
+
|Custom work queue (optional)
|
|
260
|
+
|
|
261
|
+
|`max_queue_size`
|
|
262
|
+
|Integer
|
|
263
|
+
|Maximum queue size (optional)
|
|
264
|
+
|===
|
|
265
|
+
|
|
266
|
+
**Worker Pool Configuration:**
|
|
267
|
+
|
|
268
|
+
[cols="1,1,3"]
|
|
269
|
+
|===
|
|
270
|
+
|Key |Type |Description
|
|
271
|
+
|
|
272
|
+
|`worker_class`
|
|
273
|
+
|Class
|
|
274
|
+
|The worker class to instantiate
|
|
275
|
+
|
|
276
|
+
|`num_workers`
|
|
277
|
+
|Integer
|
|
278
|
+
|Number of workers in the pool (default: 1)
|
|
279
|
+
|===
|
|
280
|
+
|
|
281
|
+
**Example:**
|
|
282
|
+
|
|
283
|
+
[source,ruby]
|
|
284
|
+
----
|
|
285
|
+
supervisor = Fractor::Supervisor.new(
|
|
286
|
+
worker_pools: [
|
|
287
|
+
{ worker_class: FastWorker, num_workers: 4 },
|
|
288
|
+
{ worker_class: SlowWorker, num_workers: 2 }
|
|
289
|
+
],
|
|
290
|
+
max_queue_size: 1000
|
|
291
|
+
)
|
|
292
|
+
----
|
|
293
|
+
|
|
294
|
+
==== Instance Methods
|
|
295
|
+
|
|
296
|
+
===== `add_work(work)`
|
|
297
|
+
|
|
298
|
+
Adds a single work item to the queue.
|
|
299
|
+
|
|
300
|
+
[cols="1,1,3"]
|
|
301
|
+
|===
|
|
302
|
+
|Parameter |Type |Description
|
|
303
|
+
|
|
304
|
+
|`work`
|
|
305
|
+
|Fractor::Work
|
|
306
|
+
|Work item to add
|
|
307
|
+
|===
|
|
308
|
+
|
|
309
|
+
**Example:**
|
|
310
|
+
|
|
311
|
+
[source,ruby]
|
|
312
|
+
----
|
|
313
|
+
supervisor.add_work(Fractor::Work.new(value: 42))
|
|
314
|
+
----
|
|
315
|
+
|
|
316
|
+
===== `add_work_items(work_items)`
|
|
317
|
+
|
|
318
|
+
Adds multiple work items to the queue.
|
|
319
|
+
|
|
320
|
+
[cols="1,1,3"]
|
|
321
|
+
|===
|
|
322
|
+
|Parameter |Type |Description
|
|
323
|
+
|
|
324
|
+
|`work_items`
|
|
325
|
+
|Array<Fractor::Work>
|
|
326
|
+
|Array of work items to add
|
|
327
|
+
|===
|
|
328
|
+
|
|
329
|
+
**Example:**
|
|
330
|
+
|
|
331
|
+
[source,ruby]
|
|
332
|
+
----
|
|
333
|
+
items = (1..100).map { |i| Fractor::Work.new(value: i) }
|
|
334
|
+
supervisor.add_work_items(items)
|
|
335
|
+
----
|
|
336
|
+
|
|
337
|
+
===== `run`
|
|
338
|
+
|
|
339
|
+
Starts processing work. Blocks until all work is complete.
|
|
340
|
+
|
|
341
|
+
**Example:**
|
|
342
|
+
|
|
343
|
+
[source,ruby]
|
|
344
|
+
----
|
|
345
|
+
supervisor.add_work_items(work_items)
|
|
346
|
+
supervisor.run
|
|
347
|
+
puts "All work completed!"
|
|
348
|
+
----
|
|
349
|
+
|
|
350
|
+
===== `stop`
|
|
351
|
+
|
|
352
|
+
Stops the supervisor and all workers gracefully.
|
|
353
|
+
|
|
354
|
+
**Example:**
|
|
355
|
+
|
|
356
|
+
[source,ruby]
|
|
357
|
+
----
|
|
358
|
+
supervisor.stop
|
|
359
|
+
----
|
|
360
|
+
|
|
361
|
+
===== `results`
|
|
362
|
+
|
|
363
|
+
Returns the result aggregator containing all work results.
|
|
364
|
+
|
|
365
|
+
**Returns:** Fractor::ResultAggregator
|
|
366
|
+
|
|
367
|
+
**Example:**
|
|
368
|
+
|
|
369
|
+
[source,ruby]
|
|
370
|
+
----
|
|
371
|
+
supervisor.run
|
|
372
|
+
aggregator = supervisor.results
|
|
373
|
+
puts "Success: #{aggregator.results.size}"
|
|
374
|
+
puts "Errors: #{aggregator.errors.size}"
|
|
375
|
+
----
|
|
376
|
+
|
|
377
|
+
=== Fractor::WorkQueue
|
|
378
|
+
|
|
379
|
+
Thread-safe queue for managing work items.
|
|
380
|
+
|
|
381
|
+
==== Class Methods
|
|
382
|
+
|
|
383
|
+
===== `new(max_size: nil)`
|
|
384
|
+
|
|
385
|
+
Creates a new WorkQueue instance.
|
|
386
|
+
|
|
387
|
+
[cols="1,1,3"]
|
|
388
|
+
|===
|
|
389
|
+
|Parameter |Type |Description
|
|
390
|
+
|
|
391
|
+
|`max_size`
|
|
392
|
+
|Integer
|
|
393
|
+
|Maximum queue size (optional, unlimited by default)
|
|
394
|
+
|===
|
|
395
|
+
|
|
396
|
+
**Example:**
|
|
397
|
+
|
|
398
|
+
[source,ruby]
|
|
399
|
+
----
|
|
400
|
+
queue = Fractor::WorkQueue.new(max_size: 100)
|
|
401
|
+
----
|
|
402
|
+
|
|
403
|
+
==== Instance Methods
|
|
404
|
+
|
|
405
|
+
===== `<<(work)` or `push(work)`
|
|
406
|
+
|
|
407
|
+
Adds work to the queue.
|
|
408
|
+
|
|
409
|
+
[cols="1,1,3"]
|
|
410
|
+
|===
|
|
411
|
+
|Parameter |Type |Description
|
|
412
|
+
|
|
413
|
+
|`work`
|
|
414
|
+
|Fractor::Work
|
|
415
|
+
|Work item to add
|
|
416
|
+
|===
|
|
417
|
+
|
|
418
|
+
**Example:**
|
|
419
|
+
|
|
420
|
+
[source,ruby]
|
|
421
|
+
----
|
|
422
|
+
queue << Fractor::Work.new(value: 42)
|
|
423
|
+
queue.push(Fractor::Work.new(value: 43))
|
|
424
|
+
----
|
|
425
|
+
|
|
426
|
+
===== `pop`
|
|
427
|
+
|
|
428
|
+
Retrieves and removes the next work item from the queue. Blocks if queue is empty.
|
|
429
|
+
|
|
430
|
+
**Returns:** Fractor::Work
|
|
431
|
+
|
|
432
|
+
**Example:**
|
|
433
|
+
|
|
434
|
+
[source,ruby]
|
|
435
|
+
----
|
|
436
|
+
work = queue.pop
|
|
437
|
+
----
|
|
438
|
+
|
|
439
|
+
===== `size`
|
|
440
|
+
|
|
441
|
+
Returns the current number of items in the queue.
|
|
442
|
+
|
|
443
|
+
**Returns:** Integer
|
|
444
|
+
|
|
445
|
+
**Example:**
|
|
446
|
+
|
|
447
|
+
[source,ruby]
|
|
448
|
+
----
|
|
449
|
+
queue.size # => 5
|
|
450
|
+
----
|
|
451
|
+
|
|
452
|
+
===== `empty?`
|
|
453
|
+
|
|
454
|
+
Returns whether the queue is empty.
|
|
455
|
+
|
|
456
|
+
**Returns:** Boolean
|
|
457
|
+
|
|
458
|
+
**Example:**
|
|
459
|
+
|
|
460
|
+
[source,ruby]
|
|
461
|
+
----
|
|
462
|
+
queue.empty? # => false
|
|
463
|
+
----
|
|
464
|
+
|
|
465
|
+
=== Fractor::ResultAggregator
|
|
466
|
+
|
|
467
|
+
Collects and organizes work results.
|
|
468
|
+
|
|
469
|
+
==== Instance Methods
|
|
470
|
+
|
|
471
|
+
===== `add_result(result)`
|
|
472
|
+
|
|
473
|
+
Adds a work result.
|
|
474
|
+
|
|
475
|
+
[cols="1,1,3"]
|
|
476
|
+
|===
|
|
477
|
+
|Parameter |Type |Description
|
|
478
|
+
|
|
479
|
+
|`result`
|
|
480
|
+
|Fractor::WorkResult
|
|
481
|
+
|Result to add
|
|
482
|
+
|===
|
|
483
|
+
|
|
484
|
+
===== `results`
|
|
485
|
+
|
|
486
|
+
Returns all successful results.
|
|
487
|
+
|
|
488
|
+
**Returns:** Array<Fractor::WorkResult>
|
|
489
|
+
|
|
490
|
+
**Example:**
|
|
491
|
+
|
|
492
|
+
[source,ruby]
|
|
493
|
+
----
|
|
494
|
+
aggregator.results.each do |result|
|
|
495
|
+
puts result.result
|
|
496
|
+
end
|
|
497
|
+
----
|
|
498
|
+
|
|
499
|
+
===== `errors`
|
|
500
|
+
|
|
501
|
+
Returns all error results.
|
|
502
|
+
|
|
503
|
+
**Returns:** Array<Fractor::WorkResult>
|
|
504
|
+
|
|
505
|
+
**Example:**
|
|
506
|
+
|
|
507
|
+
[source,ruby]
|
|
508
|
+
----
|
|
509
|
+
aggregator.errors.each do |result|
|
|
510
|
+
puts "Error: #{result.error}"
|
|
511
|
+
end
|
|
512
|
+
----
|
|
513
|
+
|
|
514
|
+
===== `all`
|
|
515
|
+
|
|
516
|
+
Returns all results (both successful and failed).
|
|
517
|
+
|
|
518
|
+
**Returns:** Array<Fractor::WorkResult>
|
|
519
|
+
|
|
520
|
+
=== Fractor::PerformanceMonitor
|
|
521
|
+
|
|
522
|
+
Monitors and tracks performance metrics for supervisors and workers.
|
|
523
|
+
|
|
524
|
+
==== Class Methods
|
|
525
|
+
|
|
526
|
+
===== `new(supervisor, sample_interval: 1.0)`
|
|
527
|
+
|
|
528
|
+
Creates a new PerformanceMonitor instance.
|
|
529
|
+
|
|
530
|
+
[cols="1,1,3"]
|
|
531
|
+
|===
|
|
532
|
+
|Parameter |Type |Description
|
|
533
|
+
|
|
534
|
+
|`supervisor`
|
|
535
|
+
|Fractor::Supervisor
|
|
536
|
+
|The supervisor to monitor
|
|
537
|
+
|
|
538
|
+
|`sample_interval`
|
|
539
|
+
|Float
|
|
540
|
+
|Sampling interval in seconds (default: 1.0)
|
|
541
|
+
|===
|
|
542
|
+
|
|
543
|
+
**Example:**
|
|
544
|
+
|
|
545
|
+
[source,ruby]
|
|
546
|
+
----
|
|
547
|
+
monitor = Fractor::PerformanceMonitor.new(
|
|
548
|
+
supervisor,
|
|
549
|
+
sample_interval: 0.5
|
|
550
|
+
)
|
|
551
|
+
----
|
|
552
|
+
|
|
553
|
+
==== Instance Methods
|
|
554
|
+
|
|
555
|
+
===== `start`
|
|
556
|
+
|
|
557
|
+
Starts monitoring in a background thread.
|
|
558
|
+
|
|
559
|
+
**Example:**
|
|
560
|
+
|
|
561
|
+
[source,ruby]
|
|
562
|
+
----
|
|
563
|
+
monitor.start
|
|
564
|
+
----
|
|
565
|
+
|
|
566
|
+
===== `stop`
|
|
567
|
+
|
|
568
|
+
Stops monitoring and waits for background thread to finish.
|
|
569
|
+
|
|
570
|
+
**Example:**
|
|
571
|
+
|
|
572
|
+
[source,ruby]
|
|
573
|
+
----
|
|
574
|
+
monitor.stop
|
|
575
|
+
----
|
|
576
|
+
|
|
577
|
+
===== `snapshot`
|
|
578
|
+
|
|
579
|
+
Returns current metrics snapshot.
|
|
580
|
+
|
|
581
|
+
**Returns:** Hash containing all current metrics
|
|
582
|
+
|
|
583
|
+
**Example:**
|
|
584
|
+
|
|
585
|
+
[source,ruby]
|
|
586
|
+
----
|
|
587
|
+
stats = monitor.snapshot
|
|
588
|
+
puts "Jobs processed: #{stats[:jobs_processed]}"
|
|
589
|
+
puts "Throughput: #{stats[:throughput]} jobs/sec"
|
|
590
|
+
puts "Queue depth: #{stats[:queue_depth]}"
|
|
591
|
+
----
|
|
592
|
+
|
|
593
|
+
===== `report`
|
|
594
|
+
|
|
595
|
+
Generates a human-readable performance report.
|
|
596
|
+
|
|
597
|
+
**Returns:** String
|
|
598
|
+
|
|
599
|
+
**Example:**
|
|
600
|
+
|
|
601
|
+
[source,ruby]
|
|
602
|
+
----
|
|
603
|
+
puts monitor.report
|
|
604
|
+
----
|
|
605
|
+
|
|
606
|
+
===== `to_json`
|
|
607
|
+
|
|
608
|
+
Exports metrics in JSON format.
|
|
609
|
+
|
|
610
|
+
**Returns:** String (JSON)
|
|
611
|
+
|
|
612
|
+
**Example:**
|
|
613
|
+
|
|
614
|
+
[source,ruby]
|
|
615
|
+
----
|
|
616
|
+
json_metrics = monitor.to_json
|
|
617
|
+
----
|
|
618
|
+
|
|
619
|
+
===== `to_prometheus`
|
|
620
|
+
|
|
621
|
+
Exports metrics in Prometheus text format.
|
|
622
|
+
|
|
623
|
+
**Returns:** String (Prometheus format)
|
|
624
|
+
|
|
625
|
+
**Example:**
|
|
626
|
+
|
|
627
|
+
[source,ruby]
|
|
628
|
+
----
|
|
629
|
+
prometheus_metrics = monitor.to_prometheus
|
|
630
|
+
----
|
|
631
|
+
|
|
632
|
+
===== `record_job(latency, success: true)`
|
|
633
|
+
|
|
634
|
+
Manually records a job completion.
|
|
635
|
+
|
|
636
|
+
[cols="1,1,3"]
|
|
637
|
+
|===
|
|
638
|
+
|Parameter |Type |Description
|
|
639
|
+
|
|
640
|
+
|`latency`
|
|
641
|
+
|Float
|
|
642
|
+
|Job latency in seconds
|
|
643
|
+
|
|
644
|
+
|`success`
|
|
645
|
+
|Boolean
|
|
646
|
+
|Whether job succeeded (default: true)
|
|
647
|
+
|===
|
|
648
|
+
|
|
649
|
+
**Example:**
|
|
650
|
+
|
|
651
|
+
[source,ruby]
|
|
652
|
+
----
|
|
653
|
+
start_time = Time.now
|
|
654
|
+
# ... perform work ...
|
|
655
|
+
latency = Time.now - start_time
|
|
656
|
+
monitor.record_job(latency, success: true)
|
|
657
|
+
----
|
|
658
|
+
|
|
659
|
+
== Workflow Classes
|
|
660
|
+
|
|
661
|
+
=== Fractor::Workflow
|
|
662
|
+
|
|
663
|
+
Base class for declarative workflows.
|
|
664
|
+
|
|
665
|
+
==== Class Methods
|
|
666
|
+
|
|
667
|
+
===== `workflow(name, &block)`
|
|
668
|
+
|
|
669
|
+
Defines a workflow with the given name.
|
|
670
|
+
|
|
671
|
+
[cols="1,1,3"]
|
|
672
|
+
|===
|
|
673
|
+
|Parameter |Type |Description
|
|
674
|
+
|
|
675
|
+
|`name`
|
|
676
|
+
|String
|
|
677
|
+
|Workflow name
|
|
678
|
+
|
|
679
|
+
|`block`
|
|
680
|
+
|Proc
|
|
681
|
+
|Workflow definition block
|
|
682
|
+
|===
|
|
683
|
+
|
|
684
|
+
**Example:**
|
|
685
|
+
|
|
686
|
+
[source,ruby]
|
|
687
|
+
----
|
|
688
|
+
class MyWorkflow < Fractor::Workflow
|
|
689
|
+
workflow "data-pipeline" do
|
|
690
|
+
input_type InputData
|
|
691
|
+
output_type OutputData
|
|
692
|
+
|
|
693
|
+
job "extract" do
|
|
694
|
+
runs_with ExtractWorker
|
|
695
|
+
inputs_from_workflow
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
job "transform" do
|
|
699
|
+
needs "extract"
|
|
700
|
+
runs_with TransformWorker
|
|
701
|
+
inputs_from_job "extract"
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
job "load" do
|
|
705
|
+
needs "transform"
|
|
706
|
+
runs_with LoadWorker
|
|
707
|
+
inputs_from_job "transform"
|
|
708
|
+
outputs_to_workflow
|
|
709
|
+
terminates_workflow
|
|
710
|
+
end
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
----
|
|
714
|
+
|
|
715
|
+
===== `define(name, &block)`
|
|
716
|
+
|
|
717
|
+
Creates a workflow class dynamically.
|
|
718
|
+
|
|
719
|
+
[cols="1,1,3"]
|
|
720
|
+
|===
|
|
721
|
+
|Parameter |Type |Description
|
|
722
|
+
|
|
723
|
+
|`name`
|
|
724
|
+
|String
|
|
725
|
+
|Workflow name
|
|
726
|
+
|
|
727
|
+
|`block`
|
|
728
|
+
|Proc
|
|
729
|
+
|Workflow definition block
|
|
730
|
+
|===
|
|
731
|
+
|
|
732
|
+
**Returns:** Class (anonymous workflow class)
|
|
733
|
+
|
|
734
|
+
**Example:**
|
|
735
|
+
|
|
736
|
+
[source,ruby]
|
|
737
|
+
----
|
|
738
|
+
workflow_class = Fractor::Workflow.define("simple") do
|
|
739
|
+
job :process, ProcessWorker
|
|
740
|
+
job :finalize, FinalizeWorker, needs: :process
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
workflow = workflow_class.new
|
|
744
|
+
result = workflow.execute(input_data)
|
|
745
|
+
----
|
|
746
|
+
|
|
747
|
+
===== `chain(name)`
|
|
748
|
+
|
|
749
|
+
Creates a linear workflow builder.
|
|
750
|
+
|
|
751
|
+
[cols="1,1,3"]
|
|
752
|
+
|===
|
|
753
|
+
|Parameter |Type |Description
|
|
754
|
+
|
|
755
|
+
|`name`
|
|
756
|
+
|String
|
|
757
|
+
|Workflow name
|
|
758
|
+
|===
|
|
759
|
+
|
|
760
|
+
**Returns:** Fractor::Workflow::ChainBuilder
|
|
761
|
+
|
|
762
|
+
**Example:**
|
|
763
|
+
|
|
764
|
+
[source,ruby]
|
|
765
|
+
----
|
|
766
|
+
workflow = Fractor::Workflow.chain("linear")
|
|
767
|
+
.step(:extract, ExtractWorker)
|
|
768
|
+
.step(:transform, TransformWorker)
|
|
769
|
+
.step(:load, LoadWorker)
|
|
770
|
+
.build
|
|
771
|
+
|
|
772
|
+
result = workflow.new.execute(input_data)
|
|
773
|
+
----
|
|
774
|
+
|
|
775
|
+
==== Instance Methods
|
|
776
|
+
|
|
777
|
+
===== `execute(input)`
|
|
778
|
+
|
|
779
|
+
Executes the workflow with the given input.
|
|
780
|
+
|
|
781
|
+
[cols="1,1,3"]
|
|
782
|
+
|===
|
|
783
|
+
|Parameter |Type |Description
|
|
784
|
+
|
|
785
|
+
|`input`
|
|
786
|
+
|Any
|
|
787
|
+
|Workflow input data
|
|
788
|
+
|===
|
|
789
|
+
|
|
790
|
+
**Returns:** Workflow output data
|
|
791
|
+
|
|
792
|
+
**Raises:** Fractor::Workflow::WorkflowExecutionError on failure
|
|
793
|
+
|
|
794
|
+
**Example:**
|
|
795
|
+
|
|
796
|
+
[source,ruby]
|
|
797
|
+
----
|
|
798
|
+
workflow = MyWorkflow.new
|
|
799
|
+
begin
|
|
800
|
+
result = workflow.execute(input_data)
|
|
801
|
+
puts "Success: #{result}"
|
|
802
|
+
rescue Fractor::Workflow::WorkflowExecutionError => e
|
|
803
|
+
puts "Failed: #{e.message}"
|
|
804
|
+
end
|
|
805
|
+
----
|
|
806
|
+
|
|
807
|
+
===== `dead_letter_queue`
|
|
808
|
+
|
|
809
|
+
Returns the workflow's dead letter queue.
|
|
810
|
+
|
|
811
|
+
**Returns:** Fractor::Workflow::DeadLetterQueue or nil
|
|
812
|
+
|
|
813
|
+
**Example:**
|
|
814
|
+
|
|
815
|
+
[source,ruby]
|
|
816
|
+
----
|
|
817
|
+
dlq = workflow.dead_letter_queue
|
|
818
|
+
if dlq
|
|
819
|
+
puts "Failed items: #{dlq.size}"
|
|
820
|
+
end
|
|
821
|
+
----
|
|
822
|
+
|
|
823
|
+
== Workflow DSL Reference
|
|
824
|
+
|
|
825
|
+
=== Job Configuration
|
|
826
|
+
|
|
827
|
+
==== `runs_with(worker_class)`
|
|
828
|
+
|
|
829
|
+
Specifies the worker class for the job.
|
|
830
|
+
|
|
831
|
+
**Example:**
|
|
832
|
+
|
|
833
|
+
[source,ruby]
|
|
834
|
+
----
|
|
835
|
+
job "process" do
|
|
836
|
+
runs_with ProcessWorker
|
|
837
|
+
end
|
|
838
|
+
----
|
|
839
|
+
|
|
840
|
+
==== `needs(job_ids...)`
|
|
841
|
+
|
|
842
|
+
Specifies job dependencies.
|
|
843
|
+
|
|
844
|
+
**Example:**
|
|
845
|
+
|
|
846
|
+
[source,ruby]
|
|
847
|
+
----
|
|
848
|
+
job "aggregate" do
|
|
849
|
+
needs "job1", "job2", "job3"
|
|
850
|
+
runs_with AggregateWorker
|
|
851
|
+
end
|
|
852
|
+
----
|
|
853
|
+
|
|
854
|
+
==== `inputs_from_workflow`
|
|
855
|
+
|
|
856
|
+
Job receives input from workflow input.
|
|
857
|
+
|
|
858
|
+
**Example:**
|
|
859
|
+
|
|
860
|
+
[source,ruby]
|
|
861
|
+
----
|
|
862
|
+
job "first" do
|
|
863
|
+
runs_with FirstWorker
|
|
864
|
+
inputs_from_workflow
|
|
865
|
+
end
|
|
866
|
+
----
|
|
867
|
+
|
|
868
|
+
==== `inputs_from_job(job_id)`
|
|
869
|
+
|
|
870
|
+
Job receives input from another job's output.
|
|
871
|
+
|
|
872
|
+
**Example:**
|
|
873
|
+
|
|
874
|
+
[source,ruby]
|
|
875
|
+
----
|
|
876
|
+
job "second" do
|
|
877
|
+
needs "first"
|
|
878
|
+
runs_with SecondWorker
|
|
879
|
+
inputs_from_job "first"
|
|
880
|
+
end
|
|
881
|
+
----
|
|
882
|
+
|
|
883
|
+
==== `outputs_to_workflow`
|
|
884
|
+
|
|
885
|
+
Job's output becomes the workflow output.
|
|
886
|
+
|
|
887
|
+
**Example:**
|
|
888
|
+
|
|
889
|
+
[source,ruby]
|
|
890
|
+
----
|
|
891
|
+
job "final" do
|
|
892
|
+
runs_with FinalWorker
|
|
893
|
+
outputs_to_workflow
|
|
894
|
+
end
|
|
895
|
+
----
|
|
896
|
+
|
|
897
|
+
==== `terminates_workflow`
|
|
898
|
+
|
|
899
|
+
Job completion terminates the workflow.
|
|
900
|
+
|
|
901
|
+
**Example:**
|
|
902
|
+
|
|
903
|
+
[source,ruby]
|
|
904
|
+
----
|
|
905
|
+
job "final" do
|
|
906
|
+
runs_with FinalWorker
|
|
907
|
+
outputs_to_workflow
|
|
908
|
+
terminates_workflow
|
|
909
|
+
end
|
|
910
|
+
----
|
|
911
|
+
|
|
912
|
+
=== Error Handling
|
|
913
|
+
|
|
914
|
+
==== `retry_on_error(options)`
|
|
915
|
+
|
|
916
|
+
Configures automatic retry for the job.
|
|
917
|
+
|
|
918
|
+
[cols="1,1,3"]
|
|
919
|
+
|===
|
|
920
|
+
|Option |Type |Description
|
|
921
|
+
|
|
922
|
+
|`max_attempts`
|
|
923
|
+
|Integer
|
|
924
|
+
|Maximum retry attempts (default: 3)
|
|
925
|
+
|
|
926
|
+
|`backoff`
|
|
927
|
+
|Symbol
|
|
928
|
+
|Backoff strategy: `:exponential`, `:linear`, `:constant`, `:none`
|
|
929
|
+
|
|
930
|
+
|`initial_delay`
|
|
931
|
+
|Float
|
|
932
|
+
|Initial delay in seconds (default: 1)
|
|
933
|
+
|
|
934
|
+
|`max_delay`
|
|
935
|
+
|Float
|
|
936
|
+
|Maximum delay cap in seconds
|
|
937
|
+
|
|
938
|
+
|`multiplier`
|
|
939
|
+
|Float
|
|
940
|
+
|Exponential backoff multiplier (default: 2)
|
|
941
|
+
|
|
942
|
+
|`increment`
|
|
943
|
+
|Float
|
|
944
|
+
|Linear backoff increment (default: 1)
|
|
945
|
+
|
|
946
|
+
|`delay`
|
|
947
|
+
|Float
|
|
948
|
+
|Constant backoff delay (default: 1)
|
|
949
|
+
|
|
950
|
+
|`retryable_errors`
|
|
951
|
+
|Array<Class>
|
|
952
|
+
|Error classes to retry (default: [StandardError])
|
|
953
|
+
|===
|
|
954
|
+
|
|
955
|
+
**Example:**
|
|
956
|
+
|
|
957
|
+
[source,ruby]
|
|
958
|
+
----
|
|
959
|
+
job "fetch_data" do
|
|
960
|
+
runs_with ApiWorker
|
|
961
|
+
retry_on_error max_attempts: 5,
|
|
962
|
+
backoff: :exponential,
|
|
963
|
+
initial_delay: 1,
|
|
964
|
+
max_delay: 60
|
|
965
|
+
end
|
|
966
|
+
----
|
|
967
|
+
|
|
968
|
+
==== `on_error(&block)`
|
|
969
|
+
|
|
970
|
+
Registers an error handler for the job.
|
|
971
|
+
|
|
972
|
+
**Example:**
|
|
973
|
+
|
|
974
|
+
[source,ruby]
|
|
975
|
+
----
|
|
976
|
+
job "process" do
|
|
977
|
+
runs_with ProcessWorker
|
|
978
|
+
on_error do |error, context|
|
|
979
|
+
Logger.error("Job failed: #{error.message}")
|
|
980
|
+
AlertService.notify(error)
|
|
981
|
+
end
|
|
982
|
+
end
|
|
983
|
+
----
|
|
984
|
+
|
|
985
|
+
==== `fallback_to(job_id)`
|
|
986
|
+
|
|
987
|
+
Specifies a fallback job if this job fails.
|
|
988
|
+
|
|
989
|
+
**Example:**
|
|
990
|
+
|
|
991
|
+
[source,ruby]
|
|
992
|
+
----
|
|
993
|
+
job "fetch_live" do
|
|
994
|
+
runs_with LiveDataWorker
|
|
995
|
+
retry_on_error max_attempts: 3
|
|
996
|
+
fallback_to "fetch_cached"
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
job "fetch_cached" do
|
|
1000
|
+
runs_with CachedDataWorker
|
|
1001
|
+
end
|
|
1002
|
+
----
|
|
1003
|
+
|
|
1004
|
+
==== `circuit_breaker(options)`
|
|
1005
|
+
|
|
1006
|
+
Configures circuit breaker for the job.
|
|
1007
|
+
|
|
1008
|
+
[cols="1,1,3"]
|
|
1009
|
+
|===
|
|
1010
|
+
|Option |Type |Description
|
|
1011
|
+
|
|
1012
|
+
|`threshold`
|
|
1013
|
+
|Integer
|
|
1014
|
+
|Failure threshold to open circuit (default: 5)
|
|
1015
|
+
|
|
1016
|
+
|`timeout`
|
|
1017
|
+
|Integer
|
|
1018
|
+
|Seconds to wait before half-open (default: 60)
|
|
1019
|
+
|
|
1020
|
+
|`half_open_calls`
|
|
1021
|
+
|Integer
|
|
1022
|
+
|Test calls in half-open state (default: 3)
|
|
1023
|
+
|
|
1024
|
+
|`shared_key`
|
|
1025
|
+
|String
|
|
1026
|
+
|Key for sharing circuit across jobs
|
|
1027
|
+
|===
|
|
1028
|
+
|
|
1029
|
+
**Example:**
|
|
1030
|
+
|
|
1031
|
+
[source,ruby]
|
|
1032
|
+
----
|
|
1033
|
+
job "external_api" do
|
|
1034
|
+
runs_with ApiWorker
|
|
1035
|
+
circuit_breaker threshold: 5,
|
|
1036
|
+
timeout: 60,
|
|
1037
|
+
half_open_calls: 3
|
|
1038
|
+
end
|
|
1039
|
+
----
|
|
1040
|
+
|
|
1041
|
+
=== Dead Letter Queue
|
|
1042
|
+
|
|
1043
|
+
==== `configure_dead_letter_queue(options)`
|
|
1044
|
+
|
|
1045
|
+
Configures the dead letter queue for the workflow.
|
|
1046
|
+
|
|
1047
|
+
[cols="1,1,3"]
|
|
1048
|
+
|===
|
|
1049
|
+
|Option |Type |Description
|
|
1050
|
+
|
|
1051
|
+
|`max_size`
|
|
1052
|
+
|Integer
|
|
1053
|
+
|Maximum DLQ size (default: 1000)
|
|
1054
|
+
|
|
1055
|
+
|`persister`
|
|
1056
|
+
|Object
|
|
1057
|
+
|Persistence strategy (optional)
|
|
1058
|
+
|
|
1059
|
+
|`on_add`
|
|
1060
|
+
|Proc
|
|
1061
|
+
|Callback when item added (optional)
|
|
1062
|
+
|===
|
|
1063
|
+
|
|
1064
|
+
**Example:**
|
|
1065
|
+
|
|
1066
|
+
[source,ruby]
|
|
1067
|
+
----
|
|
1068
|
+
workflow "my-workflow" do
|
|
1069
|
+
configure_dead_letter_queue(
|
|
1070
|
+
max_size: 500,
|
|
1071
|
+
on_add: ->(entry) { Logger.error("DLQ: #{entry.error}") }
|
|
1072
|
+
)
|
|
1073
|
+
end
|
|
1074
|
+
----
|
|
1075
|
+
|
|
1076
|
+
== See Also
|
|
1077
|
+
|
|
1078
|
+
* link:../pages/core-concepts/[Core Concepts] - Understanding Fractor components
|
|
1079
|
+
* link:../features/workflows/[Workflows] - Complete workflow documentation
|
|
1080
|
+
* link:../examples/[Examples] - Usage examples
|