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.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-https---raw-githubusercontent-com-riboseinc-oss-guides-main-ci-rubocop-yml +552 -0
  3. data/.rubocop.yml +14 -8
  4. data/.rubocop_todo.yml +284 -43
  5. data/README.adoc +111 -950
  6. data/docs/.lycheeignore +16 -0
  7. data/docs/Gemfile +24 -0
  8. data/docs/README.md +157 -0
  9. data/docs/_config.yml +151 -0
  10. data/docs/_features/error-handling.adoc +1192 -0
  11. data/docs/_features/index.adoc +80 -0
  12. data/docs/_features/monitoring.adoc +589 -0
  13. data/docs/_features/signal-handling.adoc +202 -0
  14. data/docs/_features/workflows.adoc +1235 -0
  15. data/docs/_guides/continuous-mode.adoc +736 -0
  16. data/docs/_guides/cookbook.adoc +1133 -0
  17. data/docs/_guides/index.adoc +55 -0
  18. data/docs/_guides/pipeline-mode.adoc +730 -0
  19. data/docs/_guides/troubleshooting.adoc +358 -0
  20. data/docs/_pages/architecture.adoc +1390 -0
  21. data/docs/_pages/core-concepts.adoc +1392 -0
  22. data/docs/_pages/design-principles.adoc +862 -0
  23. data/docs/_pages/getting-started.adoc +290 -0
  24. data/docs/_pages/installation.adoc +143 -0
  25. data/docs/_reference/api.adoc +1080 -0
  26. data/docs/_reference/error-reporting.adoc +670 -0
  27. data/docs/_reference/examples.adoc +181 -0
  28. data/docs/_reference/index.adoc +96 -0
  29. data/docs/_reference/troubleshooting.adoc +862 -0
  30. data/docs/_tutorials/complex-workflows.adoc +1022 -0
  31. data/docs/_tutorials/data-processing-pipeline.adoc +740 -0
  32. data/docs/_tutorials/first-application.adoc +384 -0
  33. data/docs/_tutorials/index.adoc +48 -0
  34. data/docs/_tutorials/long-running-services.adoc +931 -0
  35. data/docs/assets/images/favicon-16.png +0 -0
  36. data/docs/assets/images/favicon-32.png +0 -0
  37. data/docs/assets/images/favicon-48.png +0 -0
  38. data/docs/assets/images/favicon.ico +0 -0
  39. data/docs/assets/images/favicon.png +0 -0
  40. data/docs/assets/images/favicon.svg +45 -0
  41. data/docs/assets/images/fractor-icon.svg +49 -0
  42. data/docs/assets/images/fractor-logo.svg +61 -0
  43. data/docs/index.adoc +131 -0
  44. data/docs/lychee.toml +39 -0
  45. data/examples/api_aggregator/README.adoc +627 -0
  46. data/examples/api_aggregator/api_aggregator.rb +376 -0
  47. data/examples/auto_detection/README.adoc +407 -29
  48. data/examples/auto_detection/auto_detection.rb +9 -9
  49. data/examples/continuous_chat_common/message_protocol.rb +53 -0
  50. data/examples/continuous_chat_fractor/README.adoc +217 -0
  51. data/examples/continuous_chat_fractor/chat_client.rb +303 -0
  52. data/examples/continuous_chat_fractor/chat_common.rb +83 -0
  53. data/examples/continuous_chat_fractor/chat_server.rb +167 -0
  54. data/examples/continuous_chat_fractor/simulate.rb +345 -0
  55. data/examples/continuous_chat_server/README.adoc +135 -0
  56. data/examples/continuous_chat_server/chat_client.rb +303 -0
  57. data/examples/continuous_chat_server/chat_server.rb +359 -0
  58. data/examples/continuous_chat_server/simulate.rb +343 -0
  59. data/examples/error_reporting.rb +207 -0
  60. data/examples/file_processor/README.adoc +170 -0
  61. data/examples/file_processor/file_processor.rb +615 -0
  62. data/examples/file_processor/sample_files/invalid.csv +1 -0
  63. data/examples/file_processor/sample_files/orders.xml +24 -0
  64. data/examples/file_processor/sample_files/products.json +23 -0
  65. data/examples/file_processor/sample_files/users.csv +6 -0
  66. data/examples/hierarchical_hasher/README.adoc +629 -41
  67. data/examples/hierarchical_hasher/hierarchical_hasher.rb +12 -8
  68. data/examples/image_processor/README.adoc +610 -0
  69. data/examples/image_processor/image_processor.rb +349 -0
  70. data/examples/image_processor/processed_images/sample_10_processed.jpg.json +12 -0
  71. data/examples/image_processor/processed_images/sample_1_processed.jpg.json +12 -0
  72. data/examples/image_processor/processed_images/sample_2_processed.jpg.json +12 -0
  73. data/examples/image_processor/processed_images/sample_3_processed.jpg.json +12 -0
  74. data/examples/image_processor/processed_images/sample_4_processed.jpg.json +12 -0
  75. data/examples/image_processor/processed_images/sample_5_processed.jpg.json +12 -0
  76. data/examples/image_processor/processed_images/sample_6_processed.jpg.json +12 -0
  77. data/examples/image_processor/processed_images/sample_7_processed.jpg.json +12 -0
  78. data/examples/image_processor/processed_images/sample_8_processed.jpg.json +12 -0
  79. data/examples/image_processor/processed_images/sample_9_processed.jpg.json +12 -0
  80. data/examples/image_processor/test_images/sample_1.png +1 -0
  81. data/examples/image_processor/test_images/sample_10.png +1 -0
  82. data/examples/image_processor/test_images/sample_2.png +1 -0
  83. data/examples/image_processor/test_images/sample_3.png +1 -0
  84. data/examples/image_processor/test_images/sample_4.png +1 -0
  85. data/examples/image_processor/test_images/sample_5.png +1 -0
  86. data/examples/image_processor/test_images/sample_6.png +1 -0
  87. data/examples/image_processor/test_images/sample_7.png +1 -0
  88. data/examples/image_processor/test_images/sample_8.png +1 -0
  89. data/examples/image_processor/test_images/sample_9.png +1 -0
  90. data/examples/log_analyzer/README.adoc +662 -0
  91. data/examples/log_analyzer/log_analyzer.rb +579 -0
  92. data/examples/log_analyzer/sample_logs/apache.log +20 -0
  93. data/examples/log_analyzer/sample_logs/json.log +15 -0
  94. data/examples/log_analyzer/sample_logs/nginx.log +15 -0
  95. data/examples/log_analyzer/sample_logs/rails.log +29 -0
  96. data/examples/multi_work_type/README.adoc +576 -26
  97. data/examples/multi_work_type/multi_work_type.rb +30 -29
  98. data/examples/performance_monitoring.rb +120 -0
  99. data/examples/pipeline_processing/README.adoc +740 -26
  100. data/examples/pipeline_processing/pipeline_processing.rb +16 -16
  101. data/examples/priority_work_example.rb +155 -0
  102. data/examples/producer_subscriber/README.adoc +889 -46
  103. data/examples/producer_subscriber/producer_subscriber.rb +20 -16
  104. data/examples/scatter_gather/README.adoc +829 -27
  105. data/examples/scatter_gather/scatter_gather.rb +29 -28
  106. data/examples/simple/README.adoc +347 -0
  107. data/examples/simple/sample.rb +5 -5
  108. data/examples/specialized_workers/README.adoc +622 -26
  109. data/examples/specialized_workers/specialized_workers.rb +88 -45
  110. data/examples/stream_processor/README.adoc +206 -0
  111. data/examples/stream_processor/stream_processor.rb +284 -0
  112. data/examples/web_scraper/README.adoc +625 -0
  113. data/examples/web_scraper/web_scraper.rb +285 -0
  114. data/examples/workflow/README.adoc +406 -0
  115. data/examples/workflow/circuit_breaker/README.adoc +360 -0
  116. data/examples/workflow/circuit_breaker/circuit_breaker_workflow.rb +225 -0
  117. data/examples/workflow/conditional/README.adoc +483 -0
  118. data/examples/workflow/conditional/conditional_workflow.rb +215 -0
  119. data/examples/workflow/dead_letter_queue/README.adoc +374 -0
  120. data/examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb +217 -0
  121. data/examples/workflow/fan_out/README.adoc +381 -0
  122. data/examples/workflow/fan_out/fan_out_workflow.rb +202 -0
  123. data/examples/workflow/retry/README.adoc +248 -0
  124. data/examples/workflow/retry/retry_workflow.rb +195 -0
  125. data/examples/workflow/simple_linear/README.adoc +267 -0
  126. data/examples/workflow/simple_linear/simple_linear_workflow.rb +175 -0
  127. data/examples/workflow/simplified/README.adoc +329 -0
  128. data/examples/workflow/simplified/simplified_workflow.rb +222 -0
  129. data/exe/fractor +10 -0
  130. data/lib/fractor/cli.rb +288 -0
  131. data/lib/fractor/configuration.rb +307 -0
  132. data/lib/fractor/continuous_server.rb +183 -0
  133. data/lib/fractor/error_formatter.rb +72 -0
  134. data/lib/fractor/error_report_generator.rb +152 -0
  135. data/lib/fractor/error_reporter.rb +244 -0
  136. data/lib/fractor/error_statistics.rb +147 -0
  137. data/lib/fractor/execution_tracer.rb +162 -0
  138. data/lib/fractor/logger.rb +230 -0
  139. data/lib/fractor/main_loop_handler.rb +406 -0
  140. data/lib/fractor/main_loop_handler3.rb +135 -0
  141. data/lib/fractor/main_loop_handler4.rb +299 -0
  142. data/lib/fractor/performance_metrics_collector.rb +181 -0
  143. data/lib/fractor/performance_monitor.rb +215 -0
  144. data/lib/fractor/performance_report_generator.rb +202 -0
  145. data/lib/fractor/priority_work.rb +93 -0
  146. data/lib/fractor/priority_work_queue.rb +189 -0
  147. data/lib/fractor/result_aggregator.rb +33 -1
  148. data/lib/fractor/shutdown_handler.rb +168 -0
  149. data/lib/fractor/signal_handler.rb +80 -0
  150. data/lib/fractor/supervisor.rb +430 -144
  151. data/lib/fractor/supervisor_logger.rb +88 -0
  152. data/lib/fractor/version.rb +1 -1
  153. data/lib/fractor/work.rb +12 -0
  154. data/lib/fractor/work_distribution_manager.rb +151 -0
  155. data/lib/fractor/work_queue.rb +88 -0
  156. data/lib/fractor/work_result.rb +181 -9
  157. data/lib/fractor/worker.rb +75 -1
  158. data/lib/fractor/workflow/builder.rb +210 -0
  159. data/lib/fractor/workflow/chain_builder.rb +169 -0
  160. data/lib/fractor/workflow/circuit_breaker.rb +183 -0
  161. data/lib/fractor/workflow/circuit_breaker_orchestrator.rb +208 -0
  162. data/lib/fractor/workflow/circuit_breaker_registry.rb +112 -0
  163. data/lib/fractor/workflow/dead_letter_queue.rb +334 -0
  164. data/lib/fractor/workflow/execution_hooks.rb +39 -0
  165. data/lib/fractor/workflow/execution_strategy.rb +225 -0
  166. data/lib/fractor/workflow/execution_trace.rb +134 -0
  167. data/lib/fractor/workflow/helpers.rb +191 -0
  168. data/lib/fractor/workflow/job.rb +290 -0
  169. data/lib/fractor/workflow/job_dependency_validator.rb +120 -0
  170. data/lib/fractor/workflow/logger.rb +110 -0
  171. data/lib/fractor/workflow/pre_execution_context.rb +193 -0
  172. data/lib/fractor/workflow/retry_config.rb +156 -0
  173. data/lib/fractor/workflow/retry_orchestrator.rb +184 -0
  174. data/lib/fractor/workflow/retry_strategy.rb +93 -0
  175. data/lib/fractor/workflow/structured_logger.rb +30 -0
  176. data/lib/fractor/workflow/type_compatibility_validator.rb +222 -0
  177. data/lib/fractor/workflow/visualizer.rb +211 -0
  178. data/lib/fractor/workflow/workflow_context.rb +132 -0
  179. data/lib/fractor/workflow/workflow_executor.rb +669 -0
  180. data/lib/fractor/workflow/workflow_result.rb +55 -0
  181. data/lib/fractor/workflow/workflow_validator.rb +295 -0
  182. data/lib/fractor/workflow.rb +333 -0
  183. data/lib/fractor/wrapped_ractor.rb +66 -91
  184. data/lib/fractor/wrapped_ractor3.rb +161 -0
  185. data/lib/fractor/wrapped_ractor4.rb +242 -0
  186. data/lib/fractor.rb +93 -3
  187. metadata +192 -6
  188. data/tests/sample.rb.bak +0 -309
  189. data/tests/sample_working.rb.bak +0 -209
@@ -0,0 +1,627 @@
1
+ = API Data Aggregator Example
2
+ :toc:
3
+ :toclevels: 3
4
+
5
+ High-performance API data aggregator that fetches data from multiple API endpoints in parallel using Fractor::Workflow with circuit breaker pattern for resilience.
6
+
7
+ == Purpose
8
+
9
+ This example demonstrates:
10
+
11
+ * Parallel API fetching using Fractor::Workflow
12
+ * Circuit breaker pattern for fault tolerance
13
+ * Retry logic with exponential backoff
14
+ * Rate limiting to respect API constraints
15
+ * Data aggregation and enrichment from multiple sources
16
+ * Error recovery and graceful degradation
17
+
18
+ == Features
19
+
20
+ === Circuit Breaker Pattern
21
+
22
+ The aggregator implements a circuit breaker to prevent cascading failures:
23
+
24
+ * **Closed State**: Normal operation, requests pass through
25
+ * **Open State**: After 3 failures, circuit opens and fails fast
26
+ * **Half-Open State**: After timeout, allows limited requests to test recovery
27
+
28
+ .Circuit breaker state transitions
29
+ [source]
30
+ ----
31
+ ┌─────────┐
32
+ │ Closed │ ◄──── Success ────┐
33
+ │ (Normal)│ │
34
+ └────┬────┘ │
35
+ │ │
36
+ Failure ×3 Half-Open
37
+ │ Succeeds ×2
38
+ ▼ │
39
+ ┌─────────┐ │
40
+ │ Open │ │
41
+ │(Failing)│ │
42
+ └────┬────┘ │
43
+ │ │
44
+ Timeout │
45
+ │ │
46
+ ▼ │
47
+ ┌──────────┐ │
48
+ │Half-Open │──────────────────┘
49
+ │ (Testing)│
50
+ └──────────┘
51
+
52
+ Failure
53
+
54
+
55
+ Back to Open
56
+ ----
57
+
58
+ === Retry with Exponential Backoff
59
+
60
+ Failed requests are automatically retried with increasing delays:
61
+
62
+ * Attempt 1: Immediate
63
+ * Attempt 2: Wait 0.5 seconds
64
+ * Attempt 3: Wait 1.0 second
65
+ * Attempt 4: Wait 2.0 seconds (max attempts: 3)
66
+
67
+ === Rate Limiting
68
+
69
+ Each endpoint respects configurable rate limits:
70
+
71
+ * Default: 0.1 second delay between requests
72
+ * Prevents overwhelming API servers
73
+ * Maintains good citizenship with external services
74
+
75
+ === Data Enrichment
76
+
77
+ The aggregator combines data from multiple endpoints:
78
+
79
+ * Fetches users, products, orders, analytics
80
+ * Enriches orders with user and product details
81
+ * Calculates derived metrics (total prices, etc.)
82
+
83
+ == Architecture
84
+
85
+ === Component Diagram
86
+
87
+ [source]
88
+ ----
89
+ ┌────────────────────────────────────────┐
90
+ │ APIAggregator (Main) │
91
+ │ - Manages endpoints │
92
+ │ - Orchestrates workflow │
93
+ │ - Aggregates results │
94
+ └────────────────────────────────────────┘
95
+
96
+ │ uses
97
+
98
+ ┌────────────────────────────────────────┐
99
+ │ Fractor::Workflow │
100
+ │ - Circuit breaker │
101
+ │ - Retry logic │
102
+ │ - Parallel execution │
103
+ └────────────────────────────────────────┘
104
+
105
+ ┌───────┼───────┐
106
+ ▼ ▼ ▼
107
+ ┌────────┬────────┬────────┐
108
+ │ Users │Products│ Orders │
109
+ │ API │ API │ API │
110
+ └────────┴────────┴────────┘
111
+
112
+ │ returns
113
+
114
+ ┌────────────────────────────────────────┐
115
+ │ Aggregated Data │
116
+ │ - Combined results │
117
+ │ - Enriched information │
118
+ │ - Summary statistics │
119
+ └────────────────────────────────────────┘
120
+
121
+ │ formatted by
122
+
123
+ ┌────────────────────────────────────────┐
124
+ │ AggregationReport │
125
+ │ - Human-readable report │
126
+ │ - Statistics and summaries │
127
+ └────────────────────────────────────────┘
128
+ ----
129
+
130
+ === Class Structure
131
+
132
+ [source]
133
+ ----
134
+ APIEndpoint
135
+ ├── name: String
136
+ ├── url: String
137
+ ├── timeout: Integer
138
+ └── rate_limit_delay: Float
139
+
140
+ APIAggregator
141
+ ├── endpoints: Array<APIEndpoint>
142
+ ├── results: Hash
143
+ ├── errors: Hash
144
+ ├── add_endpoint(endpoint)
145
+ ├── fetch_all(options)
146
+ └── aggregate_data(results)
147
+
148
+ MockAPIResponses (Module)
149
+ ├── USERS_API: Array
150
+ ├── PRODUCTS_API: Array
151
+ ├── ORDERS_API: Array
152
+ ├── ANALYTICS_API: Hash
153
+ └── get_response(endpoint_name, options)
154
+
155
+ AggregationReport
156
+ └── generate(data, output_file)
157
+ ----
158
+
159
+ == Usage
160
+
161
+ === Basic Usage
162
+
163
+ Run the aggregator with default settings:
164
+
165
+ [source,bash]
166
+ ----
167
+ ruby api_aggregator.rb
168
+ ----
169
+
170
+ Output:
171
+
172
+ [source]
173
+ ----
174
+ === API Data Aggregator with Circuit Breaker ===
175
+
176
+ Fetching data from 4 API endpoints...
177
+ Circuit breaker enabled with 3 failures threshold
178
+ Retry enabled with exponential backoff (max 3 attempts)
179
+
180
+ [users] Fetching from https://api.example.com/users...
181
+ [products] Fetching from https://api.example.com/products...
182
+ [orders] Fetching from https://api.example.com/orders...
183
+ [analytics] Fetching from https://api.example.com/analytics...
184
+ [users] ✓ Success (3 items)
185
+ [products] ✓ Success (3 items)
186
+ [orders] ✓ Success (3 items)
187
+ [analytics] ✓ Success (N/A items)
188
+
189
+ === Aggregation Complete ===
190
+ Successful fetches: 4
191
+ Failed fetches: 0
192
+ Total API requests: 4
193
+ ----
194
+
195
+ === Simulate API Errors
196
+
197
+ Test circuit breaker behavior:
198
+
199
+ [source,bash]
200
+ ----
201
+ ruby api_aggregator.rb --simulate-errors
202
+ ----
203
+
204
+ This triggers API errors to demonstrate:
205
+
206
+ * Automatic retry with backoff
207
+ * Circuit breaker opening after threshold
208
+ * Fast-fail behavior when circuit is open
209
+ * Recovery when circuit enters half-open state
210
+
211
+ === Simulate Slow Responses
212
+
213
+ Test timeout handling:
214
+
215
+ [source,bash]
216
+ ----
217
+ ruby api_aggregator.rb --simulate-slow
218
+ ----
219
+
220
+ === Save Report to File
221
+
222
+ Generate and save aggregation report:
223
+
224
+ [source,bash]
225
+ ----
226
+ ruby api_aggregator.rb -o reports/aggregation.txt
227
+ ----
228
+
229
+ === Command-Line Options
230
+
231
+ [source,bash]
232
+ ----
233
+ Usage: api_aggregator.rb [options]
234
+
235
+ Options:
236
+ --simulate-errors Simulate API errors to test circuit breaker
237
+ --simulate-slow Simulate slow API responses
238
+ -o, --output FILE Output report file
239
+ -h, --help Show this message
240
+ ----
241
+
242
+ == Examples
243
+
244
+ === Example 1: Successful Aggregation
245
+
246
+ [source,bash]
247
+ ----
248
+ $ ruby api_aggregator.rb
249
+
250
+ ================================================================================
251
+ API AGGREGATION REPORT
252
+ ================================================================================
253
+
254
+ SUMMARY
255
+ --------------------------------------------------------------------------------
256
+ Total Users: 3
257
+ Total Products: 3
258
+ Total Orders: 3
259
+ Endpoints Successful: 4
260
+ Endpoints Failed: 0
261
+ Timestamp: 2024-10-25T13:00:00+08:00
262
+
263
+ USERS (3)
264
+ --------------------------------------------------------------------------------
265
+ 1. Alice Johnson <alice@example.com> [admin]
266
+ 2. Bob Smith <bob@example.com> [user]
267
+ 3. Carol Williams <carol@example.com> [user]
268
+
269
+ PRODUCTS (3)
270
+ --------------------------------------------------------------------------------
271
+ 1. Laptop - $999.99 (Stock: 15)
272
+ 2. Mouse - $29.99 (Stock: 50)
273
+ 3. Keyboard - $79.99 (Stock: 30)
274
+
275
+ ENRICHED ORDERS (3)
276
+ --------------------------------------------------------------------------------
277
+ Order #1001:
278
+ User: Alice Johnson <alice@example.com>
279
+ Product: Laptop
280
+ Quantity: 1 × $999.99 = $999.99
281
+ Status: shipped
282
+
283
+ Order #1002:
284
+ User: Bob Smith <bob@example.com>
285
+ Product: Mouse
286
+ Quantity: 2 × $29.99 = $59.98
287
+ Status: pending
288
+
289
+ Order #1003:
290
+ User: Carol Williams <carol@example.com>
291
+ Product: Keyboard
292
+ Quantity: 1 × $79.99 = $79.99
293
+ Status: delivered
294
+
295
+ ANALYTICS
296
+ --------------------------------------------------------------------------------
297
+ Total users: 3
298
+ Total products: 3
299
+ Total orders: 3
300
+ Revenue: 1139.97
301
+ Timestamp: 2024-10-25T13:00:00+08:00
302
+
303
+ ================================================================================
304
+ ----
305
+
306
+ === Example 2: Error Recovery with Circuit Breaker
307
+
308
+ [source,bash]
309
+ ----
310
+ $ ruby api_aggregator.rb --simulate-errors
311
+
312
+ Fetching data from 4 API endpoints...
313
+ Circuit breaker enabled with 3 failures threshold
314
+ Retry enabled with exponential backoff (max 3 attempts)
315
+
316
+ [users] Fetching from https://api.example.com/users...
317
+ [users] ✗ Error: Simulated API error
318
+ [users] Retrying (attempt 2/3) after 0.5s...
319
+ [users] ✗ Error: Simulated API error
320
+ [users] Retrying (attempt 3/3) after 1.0s...
321
+ [users] ✗ Error: Simulated API error
322
+ [users] All retry attempts exhausted
323
+
324
+ Circuit breaker OPEN for workflow after 3 failures
325
+ [products] Fast-failing due to open circuit
326
+ [orders] Fast-failing due to open circuit
327
+ [analytics] Fast-failing due to open circuit
328
+
329
+ === Aggregation Complete ===
330
+ Successful fetches: 0
331
+ Failed fetches: 4
332
+ Total API requests: 3
333
+
334
+ SUMMARY
335
+ --------------------------------------------------------------------------------
336
+ Endpoints Successful: 0
337
+ Endpoints Failed: 4
338
+ ----
339
+
340
+ === Example 3: Partial Success
341
+
342
+ When some endpoints succeed and others fail:
343
+
344
+ [source]
345
+ ----
346
+ === Aggregation Complete ===
347
+ Successful fetches: 2
348
+ Failed fetches: 2
349
+ Total API requests: 8
350
+
351
+ SUMMARY
352
+ --------------------------------------------------------------------------------
353
+ Total Users: 3
354
+ Total Products: 3
355
+ Total Orders: 0
356
+ Endpoints Successful: 2
357
+ Endpoints Failed: 2
358
+ ----
359
+
360
+ The aggregator gracefully handles partial failures and returns available data.
361
+
362
+ == Implementation Details
363
+
364
+ === Workflow Configuration
365
+
366
+ The aggregator uses [`Fractor::Workflow`](../../lib/fractor/workflow.rb:1) with:
367
+
368
+ [source,ruby]
369
+ ----
370
+ workflow = Fractor::Workflow.new(name: "API Aggregation Workflow")
371
+
372
+ # Configure circuit breaker
373
+ workflow.configure_circuit_breaker(
374
+ failure_threshold: 3, # Open after 3 failures
375
+ timeout: 10, # Retest after 10 seconds
376
+ half_open_attempts: 2 # Need 2 successes to close
377
+ )
378
+
379
+ # Add tasks with retry
380
+ workflow.task(:users) do |context|
381
+ fetch_endpoint(users_endpoint, context)
382
+ end.retry_on(
383
+ StandardError,
384
+ max_attempts: 3,
385
+ backoff: :exponential,
386
+ base_delay: 0.5
387
+ )
388
+ ----
389
+
390
+ === Mock API Responses
391
+
392
+ For demonstration, the example uses mock data instead of real HTTP calls:
393
+
394
+ [source,ruby]
395
+ ----
396
+ module MockAPIResponses
397
+ USERS_API = [
398
+ { id: 1, name: "Alice Johnson", ... },
399
+ # ...
400
+ ]
401
+
402
+ def self.get_response(endpoint_name, simulate_error: false, simulate_slow: false)
403
+ sleep(2) if simulate_slow
404
+ raise "Simulated API error" if simulate_error
405
+
406
+ { status: "success", data: USERS_API, timestamp: Time.now.iso8601 }
407
+ end
408
+ end
409
+ ----
410
+
411
+ In production, replace with actual HTTP calls using `Net::HTTP` or `HTTP.rb`.
412
+
413
+ === Data Enrichment Logic
414
+
415
+ Orders are enriched by joining with users and products:
416
+
417
+ [source,ruby]
418
+ ----
419
+ def enrich_orders(orders, users, products)
420
+ orders.map do |order|
421
+ user = users.find { |u| u[:id] == order[:user_id] }
422
+ product = products.find { |p| p[:id] == order[:product_id] }
423
+
424
+ order.merge(
425
+ user_name: user&.dig(:name),
426
+ user_email: user&.dig(:email),
427
+ product_name: product&.dig(:name),
428
+ product_price: product&.dig(:price),
429
+ total_price: (product&.dig(:price) || 0) * order[:quantity]
430
+ )
431
+ end
432
+ end
433
+ ----
434
+
435
+ == Circuit Breaker States
436
+
437
+ === Closed State (Normal Operation)
438
+
439
+ * All requests proceed normally
440
+ * Failures are counted
441
+ * If failures reach threshold (3), circuit opens
442
+
443
+ === Open State (Failing Fast)
444
+
445
+ * All requests fail immediately without trying
446
+ * No actual API calls are made
447
+ * After timeout period (10s), transitions to half-open
448
+
449
+ === Half-Open State (Testing Recovery)
450
+
451
+ * Limited number of requests allowed through (2)
452
+ * If requests succeed, circuit closes
453
+ * If requests fail, circuit reopens
454
+
455
+ == Error Handling
456
+
457
+ === Retry Strategy
458
+
459
+ Each failed request is retried up to 3 times:
460
+
461
+ 1. **First retry**: Wait 0.5 seconds
462
+ 2. **Second retry**: Wait 1.0 second (2× base delay)
463
+ 3. **Third retry**: Wait 2.0 seconds (4× base delay)
464
+
465
+ After 3 failures, the request is marked as failed.
466
+
467
+ === Graceful Degradation
468
+
469
+ When some endpoints fail:
470
+
471
+ * Continue with available data
472
+ * Report partial results
473
+ * Include error information in summary
474
+ * Don't block entire aggregation
475
+
476
+ === Error Types
477
+
478
+ * `StandardError`: Network errors, timeouts, API errors
479
+ * `CircuitBreakerError`: When circuit is open
480
+ * All errors are caught and reported in the errors hash
481
+
482
+ == Performance Considerations
483
+
484
+ === Parallel Execution
485
+
486
+ All API endpoints are fetched in parallel using Fractor's workflow execution:
487
+
488
+ * Endpoints: 4
489
+ * Max parallelism: 4 (limited by worker count)
490
+ * Sequential time: ~0.4s (4 × 0.1s rate limit)
491
+ * Parallel time: ~0.1s (max of all delays)
492
+
493
+ === Rate Limiting
494
+
495
+ Each endpoint has configurable rate limiting:
496
+
497
+ * Prevents overwhelming API servers
498
+ * Respects API provider guidelines
499
+ * Can be adjusted per endpoint
500
+
501
+ === Memory Usage
502
+
503
+ The aggregator is memory-efficient:
504
+
505
+ * Streams results as they arrive
506
+ * No large intermediate buffers
507
+ * Garbage collection friendly
508
+
509
+ == Testing
510
+
511
+ Run the test suite:
512
+
513
+ [source,bash]
514
+ ----
515
+ bundle exec rspec spec/examples/api_aggregator_spec.rb
516
+ ----
517
+
518
+ The test suite covers:
519
+
520
+ * APIEndpoint configuration
521
+ * Mock API responses
522
+ * Workflow execution
523
+ * Circuit breaker behavior
524
+ * Retry logic
525
+ * Data aggregation
526
+ * Error handling
527
+ * Report generation
528
+
529
+ == Best Practices
530
+
531
+ === Production Deployment
532
+
533
+ For production use:
534
+
535
+ 1. **Replace mock responses** with real HTTP client
536
+ 2. **Add authentication** (API keys, OAuth tokens)
537
+ 3. **Configure timeouts** based on SLA requirements
538
+ 4. **Adjust circuit breaker** thresholds for your use case
539
+ 5. **Add monitoring** and alerting for failures
540
+ 6. **Use connection pools** for efficiency
541
+ 7. **Implement caching** for frequently accessed data
542
+
543
+ === Configuration Guidelines
544
+
545
+ * **Circuit Breaker Threshold**: Set based on acceptable failure rate (typically 3-5)
546
+ * **Retry Attempts**: Balance responsiveness vs. resilience (typically 2-3)
547
+ * **Timeouts**: Set based on P99 latency + buffer (typically 5-10s)
548
+ * **Rate Limits**: Respect API provider guidelines (check documentation)
549
+
550
+ === Error Monitoring
551
+
552
+ Monitor these metrics:
553
+
554
+ * Circuit breaker state changes
555
+ * Retry attempt counts
556
+ * Failed endpoint calls
557
+ * Response time percentiles
558
+ * Success/failure rates per endpoint
559
+
560
+ == Troubleshooting
561
+
562
+ === Circuit Breaker Always Open
563
+
564
+ * Check failure threshold is appropriate
565
+ * Verify API endpoints are reachable
566
+ * Check for network connectivity issues
567
+ * Review timeout settings
568
+
569
+ === Slow Performance
570
+
571
+ * Verify rate limit delays aren't excessive
572
+ * Check for slow API endpoints
573
+ * Consider reducing timeout values
574
+ * Review retry backoff configuration
575
+
576
+ === Partial Data
577
+
578
+ * Check which endpoints are failing
579
+ * Review error messages in errors hash
580
+ * Verify API credentials/permissions
581
+ * Test endpoints individually
582
+
583
+ == Extending the Aggregator
584
+
585
+ === Adding New Endpoints
586
+
587
+ [source,ruby]
588
+ ----
589
+ aggregator.add_endpoint(APIEndpoint.new(
590
+ name: "inventory",
591
+ url: "https://api.example.com/inventory",
592
+ timeout: 5,
593
+ rate_limit_delay: 0.2
594
+ ))
595
+ ----
596
+
597
+ === Custom Enrichment Logic
598
+
599
+ [source,ruby]
600
+ ----
601
+ def custom_enrichment(results)
602
+ # Your custom data transformation
603
+ results[:custom_field] = calculate_something(results)
604
+ results
605
+ end
606
+ ----
607
+
608
+ === Alternative Retry Strategies
609
+
610
+ [source,ruby]
611
+ ----
612
+ workflow.task(:endpoint) do
613
+ # Task logic
614
+ end.retry_on(
615
+ NetworkError,
616
+ max_attempts: 5,
617
+ backoff: :linear, # or :constant
618
+ base_delay: 1.0
619
+ )
620
+ ----
621
+
622
+ == See Also
623
+
624
+ * link:../../README.adoc[Fractor Main Documentation]
625
+ * link:../../docs/workflows.adoc[Workflow Guide]
626
+ * link:../log_analyzer/README.adoc[Log Analyzer Example]
627
+ * link:../web_scraper/README.adoc[Web Scraper Example]