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,358 @@
1
+ ---
2
+ layout: default
3
+ title: Troubleshooting Guide
4
+ nav_order: 5
5
+ ---
6
+ == Troubleshooting Guide
7
+
8
+ This guide helps you diagnose and resolve common issues when using Fractor.
9
+
10
+ == General
11
+
12
+ === My application hangs and doesn't respond
13
+
14
+ This is commonly caused by one of these issues:
15
+
16
+ * **No work being processed**: Workers are waiting for work but the queue is empty
17
+ * **Worker not returning results**: A worker's `process` method is stuck in an infinite loop
18
+ * **Deadlock**: Multiple workers are waiting on each other
19
+
20
+ .Diagnosing the issue
21
+ ----
22
+ # Enable debug mode to see what's happening
23
+ supervisor.debug!
24
+
25
+ # Check the current state
26
+ queue_info = supervisor.inspect_queue
27
+ puts "Queue size: #{queue_info[:size]}"
28
+ puts "Total added: #{queue_info[:total_added]}"
29
+
30
+ # Check worker status
31
+ status = supervisor.workers_status
32
+ puts "Workers: #{status[:total]} total, #{status[:idle]} idle, #{status[:busy]} busy"
33
+ ----
34
+
35
+ .Solutions
36
+
37
+ * If queue size is 0 but total_added > 0, work was already processed
38
+ * If workers are all idle but queue has items, check for worker errors
39
+ * Use `supervisor.results.errors` to see any errors that occurred
40
+ * Use `supervisor.error_reporter.summary` to get error statistics
41
+
42
+ === Workers are not processing all work items
43
+
44
+ This usually happens when:
45
+
46
+ * Workers encounter errors and don't continue
47
+ * The work queue contains invalid work items
48
+ * Worker pool configuration is incorrect
49
+
50
+ .Check error results
51
+ ----
52
+ # After supervisor.run finishes
53
+ errors = supervisor.results.errors
54
+ puts "Failed jobs: #{errors.size}"
55
+
56
+ errors.each do |error_result|
57
+ puts "Work: #{error_result.work.inspect}"
58
+ puts "Error: #{error_result.error}"
59
+ puts "---"
60
+ end
61
+ ----
62
+
63
+ .Check error statistics
64
+ ----
65
+ report = supervisor.error_reporter.summary
66
+ puts report
67
+ ----
68
+
69
+ === I'm getting "worker_class must be a Class" error
70
+
71
+ This error occurs when you pass a symbol or string instead of the actual class.
72
+
73
+ .Wrong
74
+ ----
75
+ supervisor = Fractor::Supervisor.new(
76
+ worker_pools: [
77
+ { worker_class: :MyWorker } # Wrong - symbol
78
+ # or
79
+ { worker_class: "MyWorker" } # Wrong - string
80
+ ]
81
+ )
82
+ ----
83
+
84
+ .Correct
85
+ ----
86
+ supervisor = Fractor::Supervisor.new(
87
+ worker_pools: [
88
+ { worker_class: MyWorker } # Correct - actual class
89
+ ]
90
+ )
91
+ ----
92
+
93
+ === I'm getting "must inherit from Fractor::Worker" error
94
+
95
+ Your worker class must inherit from `Fractor::Worker`.
96
+
97
+ .Wrong
98
+ ----
99
+ class MyWorker
100
+ def process(work)
101
+ Fractor::WorkResult.new(result: work.input * 2, work: work)
102
+ end
103
+ end
104
+ ----
105
+
106
+ .Correct
107
+ ----
108
+ class MyWorker < Fractor::Worker
109
+ def process(work)
110
+ Fractor::WorkResult.new(result: work.input * 2, work: work)
111
+ end
112
+ end
113
+ ----
114
+
115
+ === I'm getting "must be an instance of Fractor::Work" error
116
+
117
+ Work items must inherit from `Fractor::Work`.
118
+
119
+ .Wrong
120
+ ----
121
+ supervisor.add_work_item(42) # Wrong - raw value
122
+ supervisor.add_work_item({ value: 42 }) # Wrong - hash
123
+ ----
124
+
125
+ .Correct
126
+ ----
127
+ class MyWork < Fractor::Work
128
+ def initialize(value)
129
+ super({ value: value })
130
+ end
131
+ end
132
+
133
+ supervisor.add_work_item(MyWork.new(42)) # Correct
134
+ ----
135
+
136
+ == Performance Issues
137
+
138
+ === Processing is slower than expected
139
+
140
+ Performance issues can be caused by:
141
+
142
+ * **Too few workers**: Increase worker count
143
+ * **Heavy work items**: Break work into smaller chunks
144
+ * **Worker bottleneck**: Profile worker code for inefficiencies
145
+
146
+ .Enable performance monitoring
147
+ ----
148
+ supervisor = Fractor::Supervisor.new(
149
+ worker_pools: [
150
+ { worker_class: MyWorker, num_workers: 4 }
151
+ ],
152
+ enable_performance_monitoring: true
153
+ )
154
+
155
+ # Run workload...
156
+
157
+ # Get performance metrics
158
+ metrics = supervisor.performance_metrics
159
+ puts "Throughput: #{metrics[:throughput]} jobs/sec"
160
+ puts "Average latency: #{metrics[:average_latency]}s"
161
+ puts "P95 latency: #{metrics[:p95_latency]}s"
162
+ puts "Worker utilization: #{metrics[:worker_utilization]}"
163
+ ----
164
+
165
+ === Workers are underutilized
166
+
167
+ If worker utilization is low, consider:
168
+
169
+ * **Increasing batch size**: Send more work items
170
+ * **Reducing worker count**: Match workers to workload
171
+ * **Checking I/O**: Workers may be waiting on external resources
172
+
173
+ .Check worker utilization
174
+ ----
175
+ status = supervisor.workers_status
176
+ busy_ratio = status[:busy].to_f / status[:total]
177
+ puts "Worker utilization: #{(busy_ratio * 100).round(2)}%"
178
+ ----
179
+
180
+ == Memory Issues
181
+
182
+ === Memory usage grows over time
183
+
184
+ This can be caused by:
185
+
186
+ * **Large work items**: Work items are retained in results
187
+ * **Result accumulation**: Clear results periodically
188
+ * **Memory leaks in workers**: Profile worker code
189
+
190
+ .Monitor memory usage
191
+ ----
192
+ supervisor = Fractor::Supervisor.new(
193
+ worker_pools: [
194
+ { worker_class: MyWorker }
195
+ ],
196
+ enable_performance_monitoring: true
197
+ )
198
+
199
+ # Check metrics
200
+ metrics = supervisor.performance_metrics
201
+ puts "Memory: #{metrics[:memory_mb]} MB"
202
+ ----
203
+
204
+ .Clear accumulated results
205
+ ----
206
+ # Periodically clear results if you don't need them
207
+ supervisor.results.clear
208
+ supervisor.error_reporter.reset
209
+ ----
210
+
211
+ == Continuous Mode Issues
212
+
213
+ === Continuous server doesn't process new work
214
+
215
+ In continuous mode, work sources must be registered.
216
+
217
+ .Register a work source
218
+ ----
219
+ server = Fractor::ContinuousServer.new(
220
+ worker_pools: [
221
+ { worker_class: MyWorker }
222
+ ],
223
+ continuous_mode: true
224
+ )
225
+
226
+ # Register a callback that provides new work
227
+ server.register_work_source do
228
+ # Return new work items or nil/empty array when no work available
229
+ get_work_from_external_source
230
+ end
231
+
232
+ server.run
233
+ ----
234
+
235
+ === Signals aren't being handled correctly
236
+
237
+ Signal handling varies by platform.
238
+
239
+ .Platform-specific signals
240
+ ----
241
+ # Unix/Linux/macOS: Send SIGUSR1 for status
242
+ kill -USR1 <pid>
243
+
244
+ # Windows: Try SIGBREAK (Ctrl+Break)
245
+
246
+ # Both: SIGINT (Ctrl+C) and SIGTERM work everywhere
247
+ ----
248
+
249
+ == Workflow Issues
250
+
251
+ === Workflow validation fails
252
+
253
+ Workflow validation catches configuration errors early.
254
+
255
+ .Common workflow errors
256
+ ----
257
+ # Missing worker class
258
+ Fractor::Workflow.define("my-workflow") do
259
+ job "process" do
260
+ # Missing: runs MyWorker
261
+ end
262
+ end
263
+
264
+ # Missing job dependencies
265
+ Fractor::Workflow.define("my-workflow") do
266
+ job "transform" do
267
+ runs TransformWorker
268
+ needs "prepare" # "prepare" job doesn't exist
269
+ end
270
+ end
271
+ ----
272
+
273
+ .Use the validation error message
274
+ ----
275
+ The error message includes:
276
+ * Which job has the problem
277
+ * What's missing or misconfigured
278
+ * Example of correct usage
279
+ ----
280
+
281
+ === Jobs run in wrong order
282
+
283
+ Workflow jobs run in dependency order, not declaration order.
284
+
285
+ .Specify dependencies explicitly
286
+ ----
287
+ Fractor::Workflow.define("my-workflow") do
288
+ job "transform" do
289
+ runs TransformWorker
290
+ needs "prepare" # Will run after "prepare" job
291
+ end
292
+
293
+ job "prepare" do
294
+ runs PrepareWorker
295
+ end
296
+
297
+ job "save" do
298
+ runs SaveWorker
299
+ needs "transform" # Will run last
300
+ end
301
+ end
302
+ ----
303
+
304
+ == Error Handling
305
+
306
+ === Errors are silently ignored
307
+
308
+ By default, errors are recorded but don't stop processing.
309
+
310
+ .Enable error callbacks
311
+ ----
312
+ supervisor = Fractor::Supervisor.new(
313
+ worker_pools: [
314
+ { worker_class: MyWorker }
315
+ ]
316
+ )
317
+
318
+ # Register error callback
319
+ supervisor.on_error do |error_result, worker_name, worker_class|
320
+ puts "Error in #{worker_class}: #{error_result.error}"
321
+ puts "Work item: #{error_result.work.inspect}"
322
+ end
323
+
324
+ supervisor.run
325
+ ----
326
+
327
+ === Retry logic isn't working
328
+
329
+ Retry strategies must be configured correctly.
330
+
331
+ .Configure retry strategy
332
+ ----
333
+ Fractor::Workflow.define("my-workflow") do
334
+ job "flaky-job" do
335
+ runs FlakyWorker
336
+ retry_strategy :exponential_backoff, max_attempts: 3, base_delay: 0.1
337
+ end
338
+ end
339
+ ----
340
+
341
+ == Getting Help
342
+
343
+ If you're still stuck:
344
+
345
+ * **Enable debug mode**: `supervisor.debug!` or `FRACTOR_DEBUG=1`
346
+ * **Check error reports**: `supervisor.error_reporter.summary`
347
+ * **Inspect the queue**: `supervisor.inspect_queue`
348
+ * **Check worker status**: `supervisor.workers_status`
349
+ * **Enable performance monitoring**: `enable_performance_monitoring: true`
350
+ * **Review logs**: Check for warning messages and error context
351
+
352
+ .Report issues with:
353
+
354
+ * Error messages and stack traces
355
+ * Worker and Work class definitions
356
+ * Supervisor configuration
357
+ * Debug output with `FRACTOR_DEBUG=1`
358
+ * Performance metrics if relevant