ruby_reactor 0.1.0

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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +98 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/README.md +570 -0
  6. data/Rakefile +12 -0
  7. data/documentation/DAG.md +457 -0
  8. data/documentation/README.md +123 -0
  9. data/documentation/async_reactors.md +369 -0
  10. data/documentation/composition.md +199 -0
  11. data/documentation/core_concepts.md +662 -0
  12. data/documentation/data_pipelines.md +224 -0
  13. data/documentation/examples/inventory_management.md +749 -0
  14. data/documentation/examples/order_processing.md +365 -0
  15. data/documentation/examples/payment_processing.md +654 -0
  16. data/documentation/getting_started.md +224 -0
  17. data/documentation/retry_configuration.md +357 -0
  18. data/lib/ruby_reactor/async_router.rb +91 -0
  19. data/lib/ruby_reactor/configuration.rb +41 -0
  20. data/lib/ruby_reactor/context.rb +169 -0
  21. data/lib/ruby_reactor/context_serializer.rb +164 -0
  22. data/lib/ruby_reactor/dependency_graph.rb +126 -0
  23. data/lib/ruby_reactor/dsl/compose_builder.rb +86 -0
  24. data/lib/ruby_reactor/dsl/map_builder.rb +112 -0
  25. data/lib/ruby_reactor/dsl/reactor.rb +151 -0
  26. data/lib/ruby_reactor/dsl/step_builder.rb +177 -0
  27. data/lib/ruby_reactor/dsl/template_helpers.rb +36 -0
  28. data/lib/ruby_reactor/dsl/validation_helpers.rb +35 -0
  29. data/lib/ruby_reactor/error/base.rb +16 -0
  30. data/lib/ruby_reactor/error/compensation_error.rb +8 -0
  31. data/lib/ruby_reactor/error/context_too_large_error.rb +11 -0
  32. data/lib/ruby_reactor/error/dependency_error.rb +8 -0
  33. data/lib/ruby_reactor/error/deserialization_error.rb +11 -0
  34. data/lib/ruby_reactor/error/input_validation_error.rb +29 -0
  35. data/lib/ruby_reactor/error/schema_version_error.rb +11 -0
  36. data/lib/ruby_reactor/error/step_failure_error.rb +18 -0
  37. data/lib/ruby_reactor/error/undo_error.rb +8 -0
  38. data/lib/ruby_reactor/error/validation_error.rb +8 -0
  39. data/lib/ruby_reactor/executor/compensation_manager.rb +79 -0
  40. data/lib/ruby_reactor/executor/graph_manager.rb +41 -0
  41. data/lib/ruby_reactor/executor/input_validator.rb +39 -0
  42. data/lib/ruby_reactor/executor/result_handler.rb +103 -0
  43. data/lib/ruby_reactor/executor/retry_manager.rb +156 -0
  44. data/lib/ruby_reactor/executor/step_executor.rb +319 -0
  45. data/lib/ruby_reactor/executor.rb +123 -0
  46. data/lib/ruby_reactor/map/collector.rb +65 -0
  47. data/lib/ruby_reactor/map/element_executor.rb +154 -0
  48. data/lib/ruby_reactor/map/execution.rb +60 -0
  49. data/lib/ruby_reactor/map/helpers.rb +67 -0
  50. data/lib/ruby_reactor/max_retries_exhausted_failure.rb +19 -0
  51. data/lib/ruby_reactor/reactor.rb +75 -0
  52. data/lib/ruby_reactor/retry_context.rb +92 -0
  53. data/lib/ruby_reactor/retry_queued_result.rb +26 -0
  54. data/lib/ruby_reactor/sidekiq_workers/map_collector_worker.rb +13 -0
  55. data/lib/ruby_reactor/sidekiq_workers/map_element_worker.rb +13 -0
  56. data/lib/ruby_reactor/sidekiq_workers/map_execution_worker.rb +15 -0
  57. data/lib/ruby_reactor/sidekiq_workers/worker.rb +55 -0
  58. data/lib/ruby_reactor/step/compose_step.rb +107 -0
  59. data/lib/ruby_reactor/step/map_step.rb +234 -0
  60. data/lib/ruby_reactor/step.rb +33 -0
  61. data/lib/ruby_reactor/storage/adapter.rb +51 -0
  62. data/lib/ruby_reactor/storage/configuration.rb +15 -0
  63. data/lib/ruby_reactor/storage/redis_adapter.rb +140 -0
  64. data/lib/ruby_reactor/template/base.rb +15 -0
  65. data/lib/ruby_reactor/template/element.rb +25 -0
  66. data/lib/ruby_reactor/template/input.rb +48 -0
  67. data/lib/ruby_reactor/template/result.rb +48 -0
  68. data/lib/ruby_reactor/template/value.rb +22 -0
  69. data/lib/ruby_reactor/validation/base.rb +26 -0
  70. data/lib/ruby_reactor/validation/input_validator.rb +62 -0
  71. data/lib/ruby_reactor/validation/schema_builder.rb +17 -0
  72. data/lib/ruby_reactor/version.rb +5 -0
  73. data/lib/ruby_reactor.rb +159 -0
  74. data/sig/ruby_reactor.rbs +4 -0
  75. metadata +178 -0
@@ -0,0 +1,224 @@
1
+ # Getting Started with RubyReactor
2
+
3
+ This guide will help you get started with RubyReactor, from installation to your first reactor.
4
+
5
+ ## Installation
6
+
7
+ Add RubyReactor to your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ruby_reactor'
11
+ ```
12
+
13
+ Or install directly:
14
+
15
+ ```bash
16
+ gem install ruby_reactor
17
+ ```
18
+
19
+ ## Your First Reactor
20
+
21
+ Let's create a simple order processing reactor:
22
+
23
+ ```ruby
24
+ require 'ruby_reactor'
25
+
26
+ class OrderProcessingReactor < RubyReactor::Reactor
27
+ step :validate_order do
28
+ validate_args do
29
+ required(:order_id).filled(:string)
30
+ end
31
+
32
+ run do |order_id|
33
+ # Validate the order exists and is in correct state
34
+ order = Order.find(order_id)
35
+ raise "Order not found" unless order
36
+ raise "Order already processed" if order.processed?
37
+
38
+ Success({ order: order })
39
+ end
40
+ end
41
+
42
+ step :process_payment do
43
+ run do |order:, **|
44
+ # Process payment for the order
45
+ payment_result = PaymentService.charge(order.total, order.customer.card_token)
46
+ raise "Payment failed" unless payment_result.success?
47
+
48
+ Success({ payment_id: payment_result.id })
49
+ end
50
+ end
51
+
52
+ step :update_inventory do
53
+ run do |order:, **|
54
+ # Update inventory for each item
55
+ order.items.each do |item|
56
+ InventoryService.decrement(item.product_id, item.quantity)
57
+ end
58
+
59
+ Success({ inventory_updated: true })
60
+ end
61
+ end
62
+
63
+ step :send_confirmation do
64
+ run do |order:, payment_id:, **|
65
+ # Send confirmation email
66
+ email_result = EmailService.send_confirmation(
67
+ order.customer.email,
68
+ order_id: order.id,
69
+ payment_id: payment_id
70
+ )
71
+
72
+ Success({ confirmation_sent: email_result.success? })
73
+ end
74
+ end
75
+ end
76
+ ```
77
+
78
+ ## Executing a Reactor
79
+
80
+ ### Synchronous Execution
81
+
82
+ ```ruby
83
+ # Run the reactor synchronously
84
+ result = OrderProcessingReactor.run(order_id: 123)
85
+
86
+ if result.success?
87
+ puts "Order processed successfully!"
88
+ puts "Results: #{result.step_results}"
89
+ else
90
+ puts "Order processing failed: #{result.error}"
91
+ end
92
+ ```
93
+
94
+ ### Asynchronous Execution
95
+
96
+ For async execution, you need Sidekiq configured. Mark the reactor as async:
97
+
98
+ ```ruby
99
+ class OrderProcessingReactor < RubyReactor::Reactor
100
+ async true # Enable full reactor async
101
+
102
+ # ... steps defined above
103
+ end
104
+
105
+ # Run asynchronously
106
+ async_result = OrderProcessingReactor.run(order_id: 123)
107
+ # Returns immediately with AsyncResult
108
+ # Check status later with async_result.status
109
+ ```
110
+
111
+ ## Understanding Results
112
+
113
+ RubyReactor returns detailed execution results:
114
+
115
+ ```ruby
116
+ result = OrderProcessingReactor.run(order_id: 123)
117
+
118
+ # Check overall success
119
+ result.success? # => true/false
120
+
121
+ # Access step results
122
+ result.step_results[:validate_order] # => { order: #<Order> }
123
+ result.step_results[:process_payment] # => { payment_id: "pay_123" }
124
+
125
+ # Access intermediate results
126
+ result.intermediate_results # => Hash of all step outputs
127
+
128
+ # Check completed steps
129
+ result.completed_steps # => Set of completed step names
130
+
131
+ # Error information (if failed)
132
+ result.error # => Exception that caused failure
133
+ ```
134
+
135
+ ## Step Dependencies
136
+
137
+ Steps can depend on each other using the `argument` method with `result()`:
138
+
139
+ ```ruby
140
+ class ComplexReactor < RubyReactor::Reactor
141
+ step :validate_order do
142
+ run { validate_order_logic }
143
+ end
144
+
145
+ step :check_inventory do
146
+ argument :order, result(:validate_order)
147
+
148
+ run do |args, _context|
149
+ check_inventory_for_order(args[:order])
150
+ end
151
+ end
152
+
153
+ step :process_payment do
154
+ argument :order, result(:check_inventory)
155
+
156
+ run do |args, _context|
157
+ process_payment_for_order(args[:order])
158
+ end
159
+ end
160
+ end
161
+ ```
162
+
163
+ ## Error Handling and Compensation
164
+
165
+ RubyReactor automatically handles errors and provides compensation:
166
+
167
+ ```ruby
168
+ class OrderProcessingReactor < RubyReactor::Reactor
169
+ step :validate_order do
170
+ run { validate_order_logic }
171
+ end
172
+
173
+ step :process_payment do
174
+ run { process_payment_logic }
175
+
176
+ undo do |payment_id:, **|
177
+ # Undo the payment if something fails later
178
+ PaymentService.refund(payment_id)
179
+ end
180
+ end
181
+
182
+ step :update_inventory do
183
+ run { update_inventory_logic }
184
+
185
+ compensate do |order:, **|
186
+ # Restore inventory if something fails later
187
+ order.items.each do |item|
188
+ InventoryService.increment(item.product_id, item.quantity)
189
+ end
190
+ end
191
+ end
192
+ end
193
+ ```
194
+
195
+ If `update_inventory` fails, RubyReactor will:
196
+ 1. Run the `update_inventory` compensate block
197
+ 2. Run the `process_payment` undo block
198
+ 3. Return a failure result
199
+
200
+ ## Configuration
201
+
202
+ ### Sidekiq Setup (for Async)
203
+
204
+ Add to your Sidekiq configuration:
205
+
206
+ ```ruby
207
+ # config/sidekiq.rb
208
+ require 'ruby_reactor/worker'
209
+
210
+ # Configure RubyReactor
211
+ RubyReactor.configure do |config|
212
+ config.sidekiq_queue = :default
213
+ config.sidekiq_retry_count = 3
214
+ config.logger = Logger.new('log/ruby_reactor.log')
215
+ end
216
+ ```
217
+
218
+
219
+ ## Next Steps
220
+
221
+ - Learn about [async reactors](async_reactors.md)
222
+ - Configure [retry policies](retry_configuration.md)
223
+ - See [examples](examples/) for more patterns
224
+ - Check the [API reference](api_reference.md) for detailed documentation
@@ -0,0 +1,357 @@
1
+ # Retry Configuration
2
+
3
+ RubyReactor provides flexible, non-blocking retry mechanisms that requeue jobs instead of blocking worker threads. Retry policies can be configured at both reactor and step levels.
4
+
5
+ ## Overview
6
+
7
+ The retry system offers:
8
+
9
+ - **Non-blocking retries on Async**: Jobs are requeued with calculated delays
10
+ - **Multiple backoff strategies**: Exponential, linear, and fixed delays
11
+ - **Step-level control**: Different retry policies for different steps
12
+ - **Full observability**: Complete visibility into retry attempts
13
+
14
+ ### Retry Flow Architecture
15
+
16
+ ```mermaid
17
+ graph TD
18
+ A[Step Execution] --> B{Step<br/>Succeeds?}
19
+ B -->|Yes| C[Continue to Next Step]
20
+ B -->|No| D{Can Retry?<br/>attempts < max_attempts}
21
+ D -->|No| E[Final Failure<br/>Run Compensation]
22
+ D -->|Yes| F[Calculate Backoff Delay<br/>exponential/linear/fixed]
23
+ F --> G[Serialize Context<br/>with Retry State]
24
+ G --> H[Queue Job for Retry<br/>with Calculated Delay]
25
+ H --> I[Worker Freed<br/>No Thread Blocking]
26
+ I --> J[Delay Elapses]
27
+ J --> K[Worker Picks Up<br/>Retry Job]
28
+ K --> L[Deserialize Context]
29
+ L --> M[Resume Execution<br/>from Failed Step]
30
+ M --> A
31
+ ```
32
+
33
+ ## Basic Retry Configuration
34
+
35
+ ### Step-Level Retry
36
+
37
+ ```ruby
38
+ class PaymentReactor < RubyReactor::Reactor
39
+ async true
40
+
41
+ step :charge_card do
42
+ retries max_attempts: 3, backoff: :exponential, base_delay: 5.seconds
43
+ run { PaymentService.charge(card_token, amount) }
44
+ end
45
+ end
46
+ ```
47
+
48
+ ### Reactor-Level Defaults
49
+
50
+ ```ruby
51
+ class PaymentReactor < RubyReactor::Reactor
52
+ async true
53
+
54
+ # All steps inherit these defaults
55
+ retry_defaults max_attempts: 3, backoff: :exponential, base_delay: 2.seconds
56
+
57
+ step :validate_card do
58
+ # Uses reactor defaults
59
+ run { validate_card_details }
60
+ end
61
+
62
+ step :charge_card do
63
+ # Override for this specific step
64
+ retries max_attempts: 5, backoff: :linear, base_delay: 10.seconds
65
+ run { PaymentService.charge(card_token, amount) }
66
+ end
67
+ end
68
+ ```
69
+
70
+ ## Retry Parameters
71
+
72
+ ### max_attempts
73
+ Maximum number of execution attempts (including the initial attempt).
74
+
75
+ ```ruby
76
+ retries max_attempts: 5 # 1 initial + 4 retries = 5 total attempts
77
+ ```
78
+
79
+ ### backoff
80
+ The backoff strategy for calculating delays between retry attempts.
81
+
82
+ **Options:**
83
+ - `:exponential` (default): Delay doubles with each attempt
84
+ - `:linear`: Delay increases linearly
85
+ - `:fixed`: Same delay for each attempt
86
+
87
+ ### base_delay
88
+ The base delay for retry calculations. Can be a number (seconds) or ActiveSupport duration.
89
+
90
+ ```ruby
91
+ retry base_delay: 5.seconds
92
+ retry base_delay: 300 # 5 minutes in seconds
93
+ ```
94
+
95
+ ## Backoff Strategies
96
+
97
+ ### Exponential Backoff
98
+
99
+ Delay doubles with each retry attempt. Best for external services that may be temporarily overloaded.
100
+
101
+ ```ruby
102
+ retries max_attempts: 4, backoff: :exponential, base_delay: 1.second
103
+ # Delays: 1s, 2s, 4s (total: 7 seconds)
104
+ ```
105
+
106
+ **Use cases:**
107
+ - API rate limiting
108
+ - Temporary service unavailability
109
+ - Network timeouts
110
+
111
+ ### Linear Backoff
112
+
113
+ Delay increases linearly with each attempt. Provides predictable, gradually increasing delays.
114
+
115
+ ```ruby
116
+ retries max_attempts: 4, backoff: :linear, base_delay: 5.seconds
117
+ # Delays: 5s, 10s, 15s (total: 30 seconds)
118
+ ```
119
+
120
+ **Use cases:**
121
+ - Database connection issues
122
+ - Resource contention
123
+ - Gradual backpressure
124
+
125
+ ### Fixed Backoff
126
+
127
+ Same delay between each retry attempt. Simplest strategy with predictable timing.
128
+
129
+ ```ruby
130
+ retries max_attempts: 4, backoff: :fixed, base_delay: 10.seconds
131
+ # Delays: 10s, 10s, 10s (total: 30 seconds)
132
+ ```
133
+
134
+ **Use cases:**
135
+ - Simple retry scenarios
136
+ - When timing precision matters
137
+ - Testing environments
138
+
139
+ ## Idempotency
140
+
141
+ ### What is Idempotency?
142
+
143
+ An operation is idempotent if executing it multiple times produces the same result as executing it once.
144
+
145
+ ### Idempotent vs Non-Idempotent Operations
146
+
147
+ **Idempotent operations (safe to retry):**
148
+ - Reading data
149
+ - Updating records with same values
150
+ - Sending notifications (with deduplication)
151
+ - Idempotent API calls
152
+
153
+ **Non-idempotent operations (unsafe to retry):**
154
+ - Creating new records
155
+ - Charging payments (without deduplication)
156
+ - Sending unique messages
157
+ - File system operations
158
+
159
+ ### Best Practices
160
+
161
+ 1. **Design for idempotency**: Structure operations to be safely retryable
162
+ 2. **Use idempotency keys**: For payments, orders, etc.
163
+ 3. **Test thoroughly**: Verify retry behavior doesn't cause issues
164
+
165
+ ## Advanced Configuration
166
+
167
+ ### Complex Retry Scenarios
168
+
169
+ ```ruby
170
+ class OrderProcessingReactor < RubyReactor::Reactor
171
+ async true
172
+
173
+ retry_defaults max_attempts: 3, backoff: :exponential, base_delay: 2.seconds
174
+
175
+ step :validate_order do
176
+ # Quick validation - no retry needed
177
+ run { validate_order_exists(order_id) }
178
+ end
179
+
180
+ step :check_inventory do
181
+ # Inventory checks can be retried
182
+ retries max_attempts: 5, backoff: :linear, base_delay: 1.second
183
+ run { check_inventory_availability(order) }
184
+ end
185
+
186
+ step :reserve_inventory do
187
+ # Inventory reservation - must be idempotent
188
+ retries max_attempts: 3, backoff: :fixed, base_delay: 5.seconds
189
+ run { InventoryService.reserve_items(order.items) }
190
+
191
+ compensate do
192
+ # Release reservation on failure
193
+ InventoryService.release_reservation(order.items)
194
+ end
195
+ end
196
+
197
+ step :process_payment do
198
+ # Payment processing - critical, fewer retries
199
+ retries max_attempts: 2, backoff: :exponential, base_delay: 10.seconds
200
+ run { PaymentService.charge(order.total, order.card_token) }
201
+
202
+ compensate do |payment_id:|
203
+ # Refund on failure
204
+ PaymentService.refund(payment_id) if payment_id
205
+ end
206
+ end
207
+
208
+ step :confirm_order do
209
+ # Final confirmation - must succeed
210
+ run { OrderService.mark_completed(order_id) }
211
+ end
212
+ end
213
+ ```
214
+
215
+ ### Conditional Retry Logic
216
+
217
+ For more complex retry logic, you can implement custom retry handlers:
218
+
219
+ ```ruby
220
+ class CustomRetryReactor < RubyReactor::Reactor
221
+ async true
222
+
223
+ step :call_external_api do
224
+ retries max_attempts: 5, backoff: :exponential, base_delay: 1.second
225
+ run do
226
+ result = ExternalAPI.call
227
+ # Raise specific errors based on response
228
+ case result.status
229
+ when 429 # Rate limited
230
+ Failure(RateLimitError.new(result) retryable: true)
231
+ when 500 # Server error
232
+ Failure(ServerError.new(result) retryable: true)
233
+ when 400 # Bad request
234
+ Failure(ValidationError.new(result) retryable: false)
235
+ else
236
+ result
237
+ end
238
+ end
239
+ end
240
+ end
241
+ ```
242
+
243
+ ## Monitoring and Observability
244
+
245
+ ### Retry Metrics
246
+
247
+ Track these important metrics:
248
+
249
+ ```ruby
250
+ # In your monitoring system
251
+ retry_attempt_count(step_name)
252
+ retry_success_rate(step_name)
253
+ average_retry_delay(step_name)
254
+ retry_timeout_count(step_name)
255
+ ```
256
+
257
+
258
+ ### Sidekiq Web UI
259
+
260
+ Retry jobs are visible in Sidekiq web interface with:
261
+ - Step name and attempt number
262
+ - Failure reason and stack trace
263
+ - Scheduled retry time
264
+ - Job arguments and context
265
+
266
+ ## Performance Considerations
267
+
268
+ ### Retry Storm Prevention
269
+
270
+ Avoid retry storms by:
271
+
272
+ 1. **Reasonable delays**: Don't use very short base delays
273
+ 2. **Limited attempts**: Set appropriate max_attempts limits
274
+ 3. **Circuit breakers**: Implement circuit breaker patterns for external services
275
+ 4. **Rate limiting**: Consider rate limiting at the application level
276
+
277
+ ### Resource Usage
278
+
279
+ - **Worker threads**: Retries don't block workers, improving utilization
280
+ - **Memory**: Context serialization adds memory overhead
281
+ - **Redis**: Job storage and queue management
282
+ - **Database**: Potential increased load from idempotent operations
283
+
284
+ ### Tuning Guidelines
285
+
286
+ ```ruby
287
+ # Fast-retry scenario (API calls)
288
+ retries max_attempts: 3, backoff: :exponential, base_delay: 1.second
289
+
290
+ # Slow-retry scenario (batch processing)
291
+ retries max_attempts: 5, backoff: :linear, base_delay: 5.minutes
292
+
293
+ # Critical operations (payments)
294
+ retries max_attempts: 2, backoff: :fixed, base_delay: 30.seconds
295
+ ```
296
+
297
+ ## Error Types and Handling
298
+
299
+ ### Retryable Errors
300
+
301
+ ```ruby
302
+ class NetworkTimeoutError < StandardError
303
+ def retryable?
304
+ true
305
+ end
306
+ end
307
+
308
+ class ValidationError < StandardError
309
+ def retryable?
310
+ false # Don't retry validation errors
311
+ end
312
+ end
313
+ ```
314
+
315
+ ## Testing Retry Behavior
316
+
317
+ ### Unit Testing
318
+
319
+ ```ruby
320
+ RSpec.describe PaymentReactor do
321
+ it "retries failed payment with exponential backoff" do
322
+ allow(PaymentService).to receive(:charge)
323
+ .and_raise(NetworkError.new("Timeout"))
324
+ .and_raise(NetworkError.new("Timeout"))
325
+ .and_return(payment_result)
326
+
327
+ expect(PaymentService).to receive(:charge).exactly(3).times
328
+
329
+ result = PaymentReactor.run(card_token: "tok_123", amount: 100)
330
+
331
+ expect(result).to be_success
332
+ expect(result.step_results[:charge_card][:payment_id]).to eq("pay_123")
333
+ end
334
+ end
335
+ ```
336
+
337
+ ### Integration Testing
338
+
339
+ ```ruby
340
+ describe "Retry integration" do
341
+ it "handles real Sidekiq retry scenarios" do
342
+ # Test with actual Sidekiq worker
343
+ Sidekiq::Testing.fake! do
344
+ result = FailingReactor.run(input: "test")
345
+
346
+ # Verify job was queued for retry
347
+ expect(RubyReactor::SidekiqWorkers::Worker.jobs.size).to eq(1)
348
+
349
+ # Process the retry
350
+ RubyReactor::SidekiqWorkers::Worker.drain
351
+
352
+ # Verify final success
353
+ expect(result).to be_success
354
+ end
355
+ end
356
+ end
357
+ ```
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyReactor
4
+ class AsyncRouter
5
+ def self.perform_async(serialized_context, reactor_class_name = nil, intermediate_results: {})
6
+ job_id = SidekiqWorkers::Worker.perform_async(serialized_context, reactor_class_name)
7
+ RubyReactor::AsyncResult.new(job_id: job_id, intermediate_results: intermediate_results)
8
+ end
9
+
10
+ def self.perform_in(delay, serialized_context, reactor_class_name = nil, intermediate_results: {})
11
+ job_id = SidekiqWorkers::Worker.perform_in(delay, serialized_context, reactor_class_name)
12
+ RubyReactor::AsyncResult.new(job_id: job_id, intermediate_results: intermediate_results)
13
+ end
14
+
15
+ # rubocop:disable Metrics/ParameterLists
16
+ def self.perform_map_element_async(map_id:, element_id:, index:, serialized_inputs:, reactor_class_info:,
17
+ strict_ordering:, parent_context_id:, parent_reactor_class_name:, step_name:,
18
+ batch_size: nil, serialized_context: nil)
19
+ RubyReactor::SidekiqWorkers::MapElementWorker.perform_async(
20
+ {
21
+ "map_id" => map_id,
22
+ "element_id" => element_id,
23
+ "index" => index,
24
+ "serialized_inputs" => serialized_inputs,
25
+ "reactor_class_info" => reactor_class_info,
26
+ "strict_ordering" => strict_ordering,
27
+ "parent_context_id" => parent_context_id,
28
+ "parent_reactor_class_name" => parent_reactor_class_name,
29
+ "step_name" => step_name,
30
+ "batch_size" => batch_size,
31
+ "serialized_context" => serialized_context
32
+ }
33
+ )
34
+ end
35
+
36
+ def self.perform_map_element_in(delay, map_id:, element_id:, index:, serialized_inputs:, reactor_class_info:,
37
+ strict_ordering:, parent_context_id:, parent_reactor_class_name:, step_name:,
38
+ batch_size: nil, serialized_context: nil)
39
+ RubyReactor::SidekiqWorkers::MapElementWorker.perform_in(
40
+ delay,
41
+ {
42
+ "map_id" => map_id,
43
+ "element_id" => element_id,
44
+ "index" => index,
45
+ "serialized_inputs" => serialized_inputs,
46
+ "reactor_class_info" => reactor_class_info,
47
+ "strict_ordering" => strict_ordering,
48
+ "parent_context_id" => parent_context_id,
49
+ "parent_reactor_class_name" => parent_reactor_class_name,
50
+ "step_name" => step_name,
51
+ "batch_size" => batch_size,
52
+ "serialized_context" => serialized_context
53
+ }
54
+ )
55
+ end
56
+ # rubocop:enable Metrics/ParameterLists
57
+
58
+ # rubocop:disable Metrics/ParameterLists
59
+ def self.perform_map_collection_async(parent_context_id:, map_id:, parent_reactor_class_name:, step_name:,
60
+ strict_ordering:, timeout:)
61
+ RubyReactor::SidekiqWorkers::MapCollectorWorker.perform_async(
62
+ {
63
+ "parent_context_id" => parent_context_id,
64
+ "map_id" => map_id,
65
+ "parent_reactor_class_name" => parent_reactor_class_name,
66
+ "step_name" => step_name,
67
+ "strict_ordering" => strict_ordering,
68
+ "timeout" => timeout
69
+ }
70
+ )
71
+ end
72
+ # rubocop:enable Metrics/ParameterLists
73
+
74
+ # rubocop:disable Metrics/ParameterLists
75
+ def self.perform_map_execution_async(map_id:, serialized_inputs:, reactor_class_info:, strict_ordering:,
76
+ parent_context_id:, parent_reactor_class_name:, step_name:)
77
+ RubyReactor::SidekiqWorkers::MapExecutionWorker.perform_async(
78
+ {
79
+ "map_id" => map_id,
80
+ "serialized_inputs" => serialized_inputs,
81
+ "reactor_class_info" => reactor_class_info,
82
+ "strict_ordering" => strict_ordering,
83
+ "parent_context_id" => parent_context_id,
84
+ "parent_reactor_class_name" => parent_reactor_class_name,
85
+ "step_name" => step_name
86
+ }
87
+ )
88
+ end
89
+ # rubocop:enable Metrics/ParameterLists
90
+ end
91
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+
5
+ module RubyReactor
6
+ # Configuration class for RubyReactor settings
7
+ class Configuration
8
+ include Singleton
9
+
10
+ attr_writer :sidekiq_queue, :sidekiq_retry_count, :logger, :async_router
11
+
12
+ def sidekiq_queue
13
+ @sidekiq_queue ||= :default
14
+ end
15
+
16
+ def sidekiq_retry_count
17
+ @sidekiq_retry_count ||= 3
18
+ end
19
+
20
+ def logger
21
+ @logger ||= Logger.new($stderr)
22
+ end
23
+
24
+ def async_router
25
+ @async_router ||= RubyReactor::AsyncRouter
26
+ end
27
+
28
+ def storage
29
+ @storage ||= RubyReactor::Storage::Configuration.new
30
+ end
31
+
32
+ def storage_adapter
33
+ @storage_adapter ||= case storage.adapter
34
+ when :redis
35
+ RubyReactor::Storage::RedisAdapter.new(url: storage.redis_url, **storage.redis_options)
36
+ else
37
+ raise "Unknown storage adapter: #{storage.adapter}"
38
+ end
39
+ end
40
+ end
41
+ end