fractor 0.1.6 → 0.1.8

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.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +227 -102
  3. data/README.adoc +113 -1940
  4. data/docs/.lycheeignore +16 -0
  5. data/docs/Gemfile +24 -0
  6. data/docs/README.md +157 -0
  7. data/docs/_config.yml +151 -0
  8. data/docs/_features/error-handling.adoc +1192 -0
  9. data/docs/_features/index.adoc +80 -0
  10. data/docs/_features/monitoring.adoc +589 -0
  11. data/docs/_features/signal-handling.adoc +202 -0
  12. data/docs/_features/workflows.adoc +1235 -0
  13. data/docs/_guides/continuous-mode.adoc +736 -0
  14. data/docs/_guides/cookbook.adoc +1133 -0
  15. data/docs/_guides/index.adoc +55 -0
  16. data/docs/_guides/pipeline-mode.adoc +730 -0
  17. data/docs/_guides/troubleshooting.adoc +358 -0
  18. data/docs/_pages/architecture.adoc +1390 -0
  19. data/docs/_pages/core-concepts.adoc +1392 -0
  20. data/docs/_pages/design-principles.adoc +862 -0
  21. data/docs/_pages/getting-started.adoc +290 -0
  22. data/docs/_pages/installation.adoc +143 -0
  23. data/docs/_reference/api.adoc +1080 -0
  24. data/docs/_reference/error-reporting.adoc +670 -0
  25. data/docs/_reference/examples.adoc +181 -0
  26. data/docs/_reference/index.adoc +96 -0
  27. data/docs/_reference/troubleshooting.adoc +862 -0
  28. data/docs/_tutorials/complex-workflows.adoc +1022 -0
  29. data/docs/_tutorials/data-processing-pipeline.adoc +740 -0
  30. data/docs/_tutorials/first-application.adoc +384 -0
  31. data/docs/_tutorials/index.adoc +48 -0
  32. data/docs/_tutorials/long-running-services.adoc +931 -0
  33. data/docs/assets/images/favicon-16.png +0 -0
  34. data/docs/assets/images/favicon-32.png +0 -0
  35. data/docs/assets/images/favicon-48.png +0 -0
  36. data/docs/assets/images/favicon.ico +0 -0
  37. data/docs/assets/images/favicon.png +0 -0
  38. data/docs/assets/images/favicon.svg +45 -0
  39. data/docs/assets/images/fractor-icon.svg +49 -0
  40. data/docs/assets/images/fractor-logo.svg +61 -0
  41. data/docs/index.adoc +131 -0
  42. data/docs/lychee.toml +39 -0
  43. data/examples/api_aggregator/README.adoc +627 -0
  44. data/examples/api_aggregator/api_aggregator.rb +376 -0
  45. data/examples/auto_detection/README.adoc +407 -29
  46. data/examples/continuous_chat_common/message_protocol.rb +1 -1
  47. data/examples/error_reporting.rb +207 -0
  48. data/examples/file_processor/README.adoc +170 -0
  49. data/examples/file_processor/file_processor.rb +615 -0
  50. data/examples/file_processor/sample_files/invalid.csv +1 -0
  51. data/examples/file_processor/sample_files/orders.xml +24 -0
  52. data/examples/file_processor/sample_files/products.json +23 -0
  53. data/examples/file_processor/sample_files/users.csv +6 -0
  54. data/examples/hierarchical_hasher/README.adoc +629 -41
  55. data/examples/image_processor/README.adoc +610 -0
  56. data/examples/image_processor/image_processor.rb +349 -0
  57. data/examples/image_processor/processed_images/sample_10_processed.jpg.json +12 -0
  58. data/examples/image_processor/processed_images/sample_1_processed.jpg.json +12 -0
  59. data/examples/image_processor/processed_images/sample_2_processed.jpg.json +12 -0
  60. data/examples/image_processor/processed_images/sample_3_processed.jpg.json +12 -0
  61. data/examples/image_processor/processed_images/sample_4_processed.jpg.json +12 -0
  62. data/examples/image_processor/processed_images/sample_5_processed.jpg.json +12 -0
  63. data/examples/image_processor/processed_images/sample_6_processed.jpg.json +12 -0
  64. data/examples/image_processor/processed_images/sample_7_processed.jpg.json +12 -0
  65. data/examples/image_processor/processed_images/sample_8_processed.jpg.json +12 -0
  66. data/examples/image_processor/processed_images/sample_9_processed.jpg.json +12 -0
  67. data/examples/image_processor/test_images/sample_1.png +1 -0
  68. data/examples/image_processor/test_images/sample_10.png +1 -0
  69. data/examples/image_processor/test_images/sample_2.png +1 -0
  70. data/examples/image_processor/test_images/sample_3.png +1 -0
  71. data/examples/image_processor/test_images/sample_4.png +1 -0
  72. data/examples/image_processor/test_images/sample_5.png +1 -0
  73. data/examples/image_processor/test_images/sample_6.png +1 -0
  74. data/examples/image_processor/test_images/sample_7.png +1 -0
  75. data/examples/image_processor/test_images/sample_8.png +1 -0
  76. data/examples/image_processor/test_images/sample_9.png +1 -0
  77. data/examples/log_analyzer/README.adoc +662 -0
  78. data/examples/log_analyzer/log_analyzer.rb +579 -0
  79. data/examples/log_analyzer/sample_logs/apache.log +20 -0
  80. data/examples/log_analyzer/sample_logs/json.log +15 -0
  81. data/examples/log_analyzer/sample_logs/nginx.log +15 -0
  82. data/examples/log_analyzer/sample_logs/rails.log +29 -0
  83. data/examples/multi_work_type/README.adoc +576 -26
  84. data/examples/performance_monitoring.rb +120 -0
  85. data/examples/pipeline_processing/README.adoc +740 -26
  86. data/examples/pipeline_processing/pipeline_processing.rb +2 -2
  87. data/examples/priority_work_example.rb +155 -0
  88. data/examples/producer_subscriber/README.adoc +889 -46
  89. data/examples/scatter_gather/README.adoc +829 -27
  90. data/examples/simple/README.adoc +347 -0
  91. data/examples/specialized_workers/README.adoc +622 -26
  92. data/examples/specialized_workers/specialized_workers.rb +44 -8
  93. data/examples/stream_processor/README.adoc +206 -0
  94. data/examples/stream_processor/stream_processor.rb +284 -0
  95. data/examples/web_scraper/README.adoc +625 -0
  96. data/examples/web_scraper/web_scraper.rb +285 -0
  97. data/examples/workflow/README.adoc +406 -0
  98. data/examples/workflow/circuit_breaker/README.adoc +360 -0
  99. data/examples/workflow/circuit_breaker/circuit_breaker_workflow.rb +225 -0
  100. data/examples/workflow/conditional/README.adoc +483 -0
  101. data/examples/workflow/conditional/conditional_workflow.rb +215 -0
  102. data/examples/workflow/dead_letter_queue/README.adoc +374 -0
  103. data/examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb +217 -0
  104. data/examples/workflow/fan_out/README.adoc +381 -0
  105. data/examples/workflow/fan_out/fan_out_workflow.rb +202 -0
  106. data/examples/workflow/retry/README.adoc +248 -0
  107. data/examples/workflow/retry/retry_workflow.rb +195 -0
  108. data/examples/workflow/simple_linear/README.adoc +267 -0
  109. data/examples/workflow/simple_linear/simple_linear_workflow.rb +175 -0
  110. data/examples/workflow/simplified/README.adoc +329 -0
  111. data/examples/workflow/simplified/simplified_workflow.rb +222 -0
  112. data/exe/fractor +10 -0
  113. data/lib/fractor/cli.rb +288 -0
  114. data/lib/fractor/configuration.rb +307 -0
  115. data/lib/fractor/continuous_server.rb +60 -65
  116. data/lib/fractor/error_formatter.rb +72 -0
  117. data/lib/fractor/error_report_generator.rb +152 -0
  118. data/lib/fractor/error_reporter.rb +244 -0
  119. data/lib/fractor/error_statistics.rb +147 -0
  120. data/lib/fractor/execution_tracer.rb +162 -0
  121. data/lib/fractor/logger.rb +230 -0
  122. data/lib/fractor/main_loop_handler.rb +406 -0
  123. data/lib/fractor/main_loop_handler3.rb +135 -0
  124. data/lib/fractor/main_loop_handler4.rb +299 -0
  125. data/lib/fractor/performance_metrics_collector.rb +181 -0
  126. data/lib/fractor/performance_monitor.rb +215 -0
  127. data/lib/fractor/performance_report_generator.rb +202 -0
  128. data/lib/fractor/priority_work.rb +93 -0
  129. data/lib/fractor/priority_work_queue.rb +189 -0
  130. data/lib/fractor/result_aggregator.rb +32 -0
  131. data/lib/fractor/shutdown_handler.rb +168 -0
  132. data/lib/fractor/signal_handler.rb +80 -0
  133. data/lib/fractor/supervisor.rb +382 -269
  134. data/lib/fractor/supervisor_logger.rb +88 -0
  135. data/lib/fractor/version.rb +1 -1
  136. data/lib/fractor/work.rb +12 -0
  137. data/lib/fractor/work_distribution_manager.rb +151 -0
  138. data/lib/fractor/work_queue.rb +20 -0
  139. data/lib/fractor/work_result.rb +181 -9
  140. data/lib/fractor/worker.rb +73 -0
  141. data/lib/fractor/workflow/builder.rb +210 -0
  142. data/lib/fractor/workflow/chain_builder.rb +169 -0
  143. data/lib/fractor/workflow/circuit_breaker.rb +183 -0
  144. data/lib/fractor/workflow/circuit_breaker_orchestrator.rb +208 -0
  145. data/lib/fractor/workflow/circuit_breaker_registry.rb +112 -0
  146. data/lib/fractor/workflow/dead_letter_queue.rb +334 -0
  147. data/lib/fractor/workflow/execution_hooks.rb +39 -0
  148. data/lib/fractor/workflow/execution_strategy.rb +225 -0
  149. data/lib/fractor/workflow/execution_trace.rb +134 -0
  150. data/lib/fractor/workflow/helpers.rb +191 -0
  151. data/lib/fractor/workflow/job.rb +290 -0
  152. data/lib/fractor/workflow/job_dependency_validator.rb +120 -0
  153. data/lib/fractor/workflow/logger.rb +110 -0
  154. data/lib/fractor/workflow/pre_execution_context.rb +193 -0
  155. data/lib/fractor/workflow/retry_config.rb +156 -0
  156. data/lib/fractor/workflow/retry_orchestrator.rb +184 -0
  157. data/lib/fractor/workflow/retry_strategy.rb +93 -0
  158. data/lib/fractor/workflow/structured_logger.rb +30 -0
  159. data/lib/fractor/workflow/type_compatibility_validator.rb +222 -0
  160. data/lib/fractor/workflow/visualizer.rb +211 -0
  161. data/lib/fractor/workflow/workflow_context.rb +132 -0
  162. data/lib/fractor/workflow/workflow_executor.rb +669 -0
  163. data/lib/fractor/workflow/workflow_result.rb +55 -0
  164. data/lib/fractor/workflow/workflow_validator.rb +295 -0
  165. data/lib/fractor/workflow.rb +333 -0
  166. data/lib/fractor/wrapped_ractor.rb +66 -101
  167. data/lib/fractor/wrapped_ractor3.rb +161 -0
  168. data/lib/fractor/wrapped_ractor4.rb +242 -0
  169. data/lib/fractor.rb +92 -4
  170. metadata +179 -6
  171. data/tests/sample.rb.bak +0 -309
  172. data/tests/sample_working.rb.bak +0 -209
@@ -0,0 +1,347 @@
1
+ = Simple Example - Getting Started with Fractor
2
+
3
+ == Purpose
4
+
5
+ Provides the most basic introduction to Fractor, demonstrating fundamental concepts with minimal complexity to help new users get started quickly.
6
+
7
+ == Focus
8
+
9
+ This example focuses on the essential building blocks:
10
+
11
+ * Creating custom `Work` classes to encapsulate work items
12
+ * Creating custom `Worker` classes to process work
13
+ * Setting up a `Supervisor` to manage parallel processing
14
+ * Adding work items and running the supervisor
15
+ * Processing results and handling errors
16
+ * Auto-detection of available processors
17
+
18
+ == Architecture
19
+
20
+ .Basic Fractor Architecture
21
+ [source]
22
+ ----
23
+ Main Program
24
+
25
+ ├─→ Create Work Items (MyWork)
26
+ │ │
27
+ │ └─→ MyWork { value: 1 }
28
+ │ MyWork { value: 2 }
29
+ │ ...
30
+ │ MyWork { value: 10 }
31
+
32
+ ├─→ Create Supervisor
33
+ │ │
34
+ │ └─→ Worker Pool Configuration
35
+ │ │
36
+ │ └─→ MyWorker (auto-detect CPU count)
37
+
38
+ ├─→ Add Work Items
39
+ │ │
40
+ │ └─→ supervisor.add_work_items(work_items)
41
+
42
+ ├─→ Run Supervisor
43
+ │ │
44
+ │ └─→ Parallel Processing
45
+ │ │
46
+ │ ├─→ Ractor 1 (MyWorker) ─→ Process work
47
+ │ ├─→ Ractor 2 (MyWorker) ─→ Process work
48
+ │ ├─→ Ractor 3 (MyWorker) ─→ Process work
49
+ │ └─→ Ractor N (MyWorker) ─→ Process work
50
+ │ │
51
+ │ └─→ Results Aggregator
52
+ │ │
53
+ │ ├─→ Successful Results
54
+ │ └─→ Error Results
55
+
56
+ └─→ Display Results
57
+
58
+ ├─→ supervisor.results (all results)
59
+ └─→ supervisor.results.errors (failed items)
60
+ ----
61
+
62
+ .Data Flow
63
+ [source]
64
+ ----
65
+ Work Items Worker Processing Results
66
+ ──────────────────────────────────────────────────────────────────
67
+
68
+ MyWork(1) ─┐
69
+ MyWork(2) ─┤
70
+ MyWork(3) ─┤
71
+ MyWork(4) ─┼─→ Work Queue ─→ Ractor Pool ─→ WorkResult(2)
72
+ MyWork(5) ─┤ (Supervisor) (Workers) WorkResult(4)
73
+ MyWork(6) ─┤ WorkResult(6)
74
+ MyWork(7) ─┤ WorkResult(8)
75
+ MyWork(8) ─┤ ...
76
+ MyWork(9) ─┤ WorkError(5)
77
+ MyWork(10) ─┘ WorkResult(20)
78
+ ----
79
+
80
+ == Key Components
81
+
82
+ === Work Class
83
+
84
+ The `Work` class encapsulates input data for processing:
85
+
86
+ [source,ruby]
87
+ ----
88
+ class MyWork < Fractor::Work
89
+ def initialize(value)
90
+ super({ value: value }) # <1>
91
+ end
92
+
93
+ def value
94
+ input[:value] # <2>
95
+ end
96
+
97
+ def to_s
98
+ "MyWork: #{value}" # <3>
99
+ end
100
+ end
101
+ ----
102
+ <1> Store data in input hash using `super`
103
+ <2> Access stored data via `input` hash
104
+ <3> Provide string representation for debugging
105
+
106
+ Key points:
107
+
108
+ * Inherit from `Fractor::Work`
109
+ * Store all data in the `input` hash
110
+ * Provide accessor methods for convenience
111
+ * Include `to_s` method for debugging
112
+
113
+ === Worker Class
114
+
115
+ The `Worker` class processes work items:
116
+
117
+ [source,ruby]
118
+ ----
119
+ class MyWorker < Fractor::Worker
120
+ def process(work) # <1>
121
+ # Check work type
122
+ if work.is_a?(MyWork)
123
+ # Handle known error case
124
+ if work.value == 5
125
+ error = StandardError.new('Cannot process value 5')
126
+ return Fractor::WorkResult.new(error: error, work: work) # <2>
127
+ end
128
+
129
+ # Process successfully
130
+ calculated = work.value * 2
131
+ Fractor::WorkResult.new(result: calculated, work: work) # <3>
132
+
133
+ else
134
+ # Handle unexpected work type
135
+ error = TypeError.new("Unsupported work type: #{work.class}")
136
+ Fractor::WorkResult.new(error: error, work: work)
137
+ end
138
+ end
139
+ end
140
+ ----
141
+ <1> Implement `process(work)` method - called by Ractor
142
+ <2> Return `WorkResult` with error for failures
143
+ <3> Return `WorkResult` with result for success
144
+
145
+ Key points:
146
+
147
+ * Inherit from `Fractor::Worker`
148
+ * Implement `process(work)` method
149
+ * Return `Fractor::WorkResult` objects
150
+ * Handle errors gracefully (return error results, don't raise)
151
+ * Support multiple work types if needed
152
+
153
+ === Supervisor Setup
154
+
155
+ The `Supervisor` manages worker Ractors and distributes work:
156
+
157
+ [source,ruby]
158
+ ----
159
+ # Create supervisor with worker pool
160
+ supervisor = Fractor::Supervisor.new(
161
+ worker_pools: [
162
+ { worker_class: MyWorker } # <1>
163
+ ]
164
+ )
165
+
166
+ # Create and add work items
167
+ work_items = (1..10).map { |i| MyWork.new(i) } # <2>
168
+ supervisor.add_work_items(work_items) # <3>
169
+
170
+ # Run the supervisor
171
+ supervisor.run # <4>
172
+
173
+ # Access results
174
+ supervisor.results # <5>
175
+ supervisor.results.errors # <6>
176
+ ----
177
+ <1> Define worker pool (auto-detects CPU count)
178
+ <2> Create array of work items
179
+ <3> Add work items to supervisor
180
+ <4> Start processing (blocks until complete)
181
+ <5> Access all results (successful and failed)
182
+ <6> Access only failed results
183
+
184
+ Key points:
185
+
186
+ * Worker pools default to auto-detected CPU count
187
+ * Can specify `num_workers` explicitly if needed
188
+ * `run` blocks until all work is complete
189
+ * Results are aggregated in `supervisor.results`
190
+
191
+ == Usage
192
+
193
+ Run the example from the project root:
194
+
195
+ [source,shell]
196
+ ----
197
+ ruby examples/simple/sample.rb
198
+
199
+ # With debug output
200
+ FRACTOR_DEBUG=1 ruby examples/simple/sample.rb
201
+ ----
202
+
203
+ == Expected Output
204
+
205
+ [example]
206
+ ====
207
+ [source]
208
+ ----
209
+ Processing complete.
210
+ Final Aggregated Results:
211
+ #<Fractor::ResultAggregator:0x... @results=[
212
+ #<Fractor::WorkResult @result=2, @work=#<MyWork: 1>>,
213
+ #<Fractor::WorkResult @result=4, @work=#<MyWork: 2>>,
214
+ #<Fractor::WorkResult @result=6, @work=#<MyWork: 3>>,
215
+ #<Fractor::WorkResult @result=8, @work=#<MyWork: 4>>,
216
+ #<Fractor::WorkResult @result=12, @work=#<MyWork: 6>>,
217
+ #<Fractor::WorkResult @result=14, @work=#<MyWork: 7>>,
218
+ #<Fractor::WorkResult @result=16, @work=#<MyWork: 8>>,
219
+ #<Fractor::WorkResult @result=18, @work=#<MyWork: 9>>,
220
+ #<Fractor::WorkResult @result=20, @work=#<MyWork: 10>>
221
+ ], @errors=[...]>
222
+
223
+ Failed Work Items (1):
224
+ Work: MyWork: 5
225
+ Error: StandardError: Cannot process value 5
226
+ ----
227
+ ====
228
+
229
+ == Learning Points
230
+
231
+ === Parallel Processing
232
+
233
+ * Fractor automatically distributes work across multiple Ractors
234
+ * Number of Ractors defaults to available CPU cores
235
+ * Work is processed in parallel, improving throughput
236
+ * Order of completion is non-deterministic
237
+
238
+ === Work Encapsulation
239
+
240
+ * Each work item is a separate object with its input data
241
+ * Work items are isolated from each other
242
+ * Workers process one work item at a time
243
+ * Work items can be of different types (polymorphic)
244
+
245
+ === Error Handling
246
+
247
+ * Errors don't stop the entire processing
248
+ * Failed work items are tracked separately
249
+ * Workers return error results, not exceptions
250
+ * System continues processing remaining work
251
+
252
+ === Auto-Detection
253
+
254
+ * When `num_workers` is not specified, Fractor auto-detects CPU count
255
+ * Uses `Etc.nprocessors` to determine available cores
256
+ * Optimal for CPU-bound tasks
257
+ * Can be overridden if needed:
258
+ +
259
+ [source,ruby]
260
+ ----
261
+ supervisor = Fractor::Supervisor.new(
262
+ worker_pools: [
263
+ { worker_class: MyWorker, num_workers: 4 }
264
+ ]
265
+ )
266
+ ----
267
+
268
+ === Result Aggregation
269
+
270
+ * All results are collected in `supervisor.results`
271
+ * Successful results accessible via `results.results`
272
+ * Failed results accessible via `results.errors`
273
+ * Each result contains both the output and original work item
274
+
275
+ == Common Patterns
276
+
277
+ === Multiple Work Types
278
+
279
+ Process different types of work with the same worker:
280
+
281
+ [source,ruby]
282
+ ----
283
+ class MyWorker < Fractor::Worker
284
+ def process(work)
285
+ if work.is_a?(MyWork)
286
+ # Process MyWork
287
+ Fractor::WorkResult.new(result: work.value * 2, work: work)
288
+ elsif work.is_a?(OtherWork)
289
+ # Process OtherWork differently
290
+ Fractor::WorkResult.new(result: "Processed: #{work.value}", work: work)
291
+ else
292
+ # Handle unknown types
293
+ error = TypeError.new("Unsupported work type: #{work.class}")
294
+ Fractor::WorkResult.new(error: error, work: work)
295
+ end
296
+ end
297
+ end
298
+ ----
299
+
300
+ === Conditional Processing
301
+
302
+ Make decisions based on work item data:
303
+
304
+ [source,ruby]
305
+ ----
306
+ def process(work)
307
+ if work.value < 0
308
+ error = ArgumentError.new('Value must be positive')
309
+ return Fractor::WorkResult.new(error: error, work: work)
310
+ end
311
+
312
+ if work.value > 100
313
+ # Heavy processing for large values
314
+ result = complex_calculation(work.value)
315
+ else
316
+ # Simple processing for small values
317
+ result = work.value * 2
318
+ end
319
+
320
+ Fractor::WorkResult.new(result: result, work: work)
321
+ end
322
+ ----
323
+
324
+ === Debugging
325
+
326
+ Use `ENV['FRACTOR_DEBUG']` to enable debug output:
327
+
328
+ [source,ruby]
329
+ ----
330
+ def process(work)
331
+ puts "Working on '#{work.inspect}'" if ENV['FRACTOR_DEBUG']
332
+
333
+ # Processing logic...
334
+
335
+ Fractor::WorkResult.new(result: result, work: work)
336
+ end
337
+ ----
338
+
339
+ == Next Steps
340
+
341
+ After understanding the basics, explore more advanced examples:
342
+
343
+ * link:../auto_detection/README.adoc[Auto Detection] - CPU auto-detection in detail
344
+ * link:../multi_work_type/README.adoc[Multi Work Type] - Handling multiple work types
345
+ * link:../specialized_workers/README.adoc[Specialized Workers] - Worker pools for different tasks
346
+ * link:../pipeline_processing/README.adoc[Pipeline Processing] - Sequential processing stages
347
+ * link:../workflow/README.adoc[Workflow System] - GitHub Actions-style workflows