ruby_reactor 0.4.1 → 0.5.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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +8 -2
- data/lib/ruby_reactor/configuration.rb +6 -1
- data/lib/ruby_reactor/context.rb +2 -1
- data/lib/ruby_reactor/context_serializer.rb +1 -3
- data/lib/ruby_reactor/dsl/reactor.rb +6 -2
- data/lib/ruby_reactor/executor/compensation_manager.rb +75 -47
- data/lib/ruby_reactor/executor/retry_manager.rb +15 -5
- data/lib/ruby_reactor/executor/step_executor.rb +36 -18
- data/lib/ruby_reactor/executor.rb +112 -36
- data/lib/ruby_reactor/map/collector.rb +4 -4
- data/lib/ruby_reactor/map/element_executor.rb +15 -1
- data/lib/ruby_reactor/map/helpers.rb +17 -4
- data/lib/ruby_reactor/middleware.rb +13 -0
- data/lib/ruby_reactor/middleware_runner.rb +29 -0
- data/lib/ruby_reactor/open_telemetry.rb +647 -0
- data/lib/ruby_reactor/reactor.rb +1 -0
- data/lib/ruby_reactor/rspec/test_subject.rb +0 -1
- data/lib/ruby_reactor/sidekiq_adapter.rb +7 -21
- data/lib/ruby_reactor/step/map_step.rb +25 -33
- data/lib/ruby_reactor/version.rb +1 -1
- data/lib/ruby_reactor/web/coordination_serializer.rb +12 -18
- data/teley/Dockerfile +60 -0
- metadata +5 -3
- data/lib/ruby_reactor/map/execution.rb +0 -101
- data/lib/ruby_reactor/sidekiq_workers/map_execution_worker.rb +0 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 134fc0713b22bad85e193592696b24417cb3c30afe8e1e7cec41328df4dafeb5
|
|
4
|
+
data.tar.gz: 469b9dc316a61f0814b964ff1afd9292bf4a98790807e367ffda3c575c62e4f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c28f483542f87de327e23554783dfffc1d92b89c181c85d1317763aaa0a4f824f07cfbc8dbc9589f9b814bae2d4debe11acaf87c84a9b9f6b7a501110a71ff4
|
|
7
|
+
data.tar.gz: b856bce9fb30b8e2e8e1b27d01b4801ffa87e95b2214cccddd73d1b0447b5801dd3f90a722977e6955786b81e70b60ac2339450552df216df37fb334a05735b5
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.0](https://github.com/arturictus/ruby_reactor/compare/v0.4.1...v0.5.0) (2026-06-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Middlewares & OpenTelemetry ([#32](https://github.com/arturictus/ruby_reactor/issues/32)) ([a9e10ce](https://github.com/arturictus/ruby_reactor/commit/a9e10ceb6fa6381ead57a5905931343f8d1182d1))
|
|
9
|
+
|
|
3
10
|
## [0.4.1](https://github.com/arturictus/ruby_reactor/compare/v0.4.0...v0.4.1) (2026-05-25)
|
|
4
11
|
|
|
5
12
|
|
data/README.md
CHANGED
|
@@ -475,6 +475,8 @@ end
|
|
|
475
475
|
|
|
476
476
|
By using `async true` with `batch_size`, the system applies **Back Pressure** to efficiently manage resources. [Read more about Back Pressure & Resource Management](documentation/data_pipelines.md#back-pressure--resource-management).
|
|
477
477
|
|
|
478
|
+
`batch_size` is optional: with `async true` alone, RubyReactor fans out one worker per element (defaulting the batch size to the full source size) and aggregates the outcomes into a `ResultEnumerator` — convenient for small collections, but with no back pressure. See [Async Without `batch_size`](documentation/data_pipelines.md#async-without-batch_size).
|
|
479
|
+
|
|
478
480
|
#### Map with Dynamic Source (ActiveRecord)
|
|
479
481
|
|
|
480
482
|
You can use a block for `source` to dynamically fetch data, such as from ActiveRecord queries. The result is wrapped in a `ResultEnumerator` for easy access to successes and failures.
|
|
@@ -859,6 +861,10 @@ Comprehensive guide to testing reactors with RubyReactor's testing utilities. Le
|
|
|
859
861
|
|
|
860
862
|
Coordinate access to shared resources across processes with Redis-backed primitives: exclusive locks (`with_lock`), concurrency-limiting semaphores (`with_semaphore`), fixed-window rate limits with multi-window quotas (`with_rate_limit`), and calendar-bucketed dedup (`with_period`, returning `Skipped` results). Covers re-entrancy across composed reactors, TTL auto-extend, inline-vs-async contention behavior, smart `retry_after` snoozes for rate limits, snooze tuning, the token-based semaphore safety model, and once-per-day/month/year scheduling patterns.
|
|
861
863
|
|
|
864
|
+
### [Middlewares & OpenTelemetry](documentation/middlewares.md)
|
|
865
|
+
|
|
866
|
+
Hook into the execution lifecycle with observer middlewares. Covers the full set of lifecycle events (reactor, step, retry, compensation/undo, async hand-off, locks/semaphores), writing and registering custom middlewares (global and per-reactor), and the built-in `RubyReactor::OpenTelemetry` tracing middleware — span structure, input/argument redaction, distributed trace propagation across async/retry boundaries, and custom exporters.
|
|
867
|
+
|
|
862
868
|
### Examples
|
|
863
869
|
- [Order Processing](documentation/examples/order_processing.md) - Complete order processing workflow example
|
|
864
870
|
- [Payment Processing](documentation/examples/payment_processing.md) - Payment handling with compensation
|
|
@@ -871,7 +877,7 @@ Coordinate access to shared resources across processes with Redis-backed primiti
|
|
|
871
877
|
- [X] `map` step to iterate over arrays in parallel
|
|
872
878
|
- [X] `compose` special step to execute reactors as step
|
|
873
879
|
- [X] `interrupt` to pause and resume reactors
|
|
874
|
-
- [
|
|
880
|
+
- [X] Middlewares
|
|
875
881
|
- [ ] Async ruby to parallelize same level steps
|
|
876
882
|
- [x] Web dashboard to inspect reactor results and errors
|
|
877
883
|
- [ ] Multiple storage adapters
|
|
@@ -880,7 +886,7 @@ Coordinate access to shared resources across processes with Redis-backed primiti
|
|
|
880
886
|
- [ ] Multiple Async adapters
|
|
881
887
|
- [X] Sidekiq
|
|
882
888
|
- [ ] ActiveJob
|
|
883
|
-
- [
|
|
889
|
+
- [X] OpenTelemetry support
|
|
884
890
|
- [X] locks
|
|
885
891
|
|
|
886
892
|
## Development
|
|
@@ -8,7 +8,8 @@ module RubyReactor
|
|
|
8
8
|
include Singleton
|
|
9
9
|
|
|
10
10
|
attr_writer :sidekiq_queue, :sidekiq_retry_count, :logger, :async_router,
|
|
11
|
-
:lock_snooze_base_delay, :lock_snooze_jitter, :lock_snooze_max_attempts
|
|
11
|
+
:lock_snooze_base_delay, :lock_snooze_jitter, :lock_snooze_max_attempts,
|
|
12
|
+
:middlewares
|
|
12
13
|
|
|
13
14
|
def sidekiq_queue
|
|
14
15
|
@sidekiq_queue ||= :default
|
|
@@ -54,5 +55,9 @@ module RubyReactor
|
|
|
54
55
|
raise "Unknown storage adapter: #{storage.adapter}"
|
|
55
56
|
end
|
|
56
57
|
end
|
|
58
|
+
|
|
59
|
+
def middlewares
|
|
60
|
+
@middlewares ||= []
|
|
61
|
+
end
|
|
57
62
|
end
|
|
58
63
|
end
|
data/lib/ruby_reactor/context.rb
CHANGED
|
@@ -5,7 +5,8 @@ module RubyReactor
|
|
|
5
5
|
attr_accessor :inputs, :intermediate_results, :private_data, :current_step, :retry_count, :concurrency_key,
|
|
6
6
|
:retry_context, :reactor_class, :execution_trace, :inline_async_execution, :undo_stack,
|
|
7
7
|
:parent_context, :root_context, :composed_contexts, :context_id, :map_operations, :map_metadata,
|
|
8
|
-
:cancelled, :cancellation_reason, :parent_context_id, :retried_from_id, :status, :failure_reason
|
|
8
|
+
:cancelled, :cancellation_reason, :parent_context_id, :retried_from_id, :status, :failure_reason,
|
|
9
|
+
:middlewares
|
|
9
10
|
|
|
10
11
|
def initialize(inputs = {}, reactor_class = nil)
|
|
11
12
|
@context_id = SecureRandom.uuid
|
|
@@ -202,9 +202,7 @@ module RubyReactor
|
|
|
202
202
|
|
|
203
203
|
hash = flatten_typed_failure(hash) if hash["_type"] == "Failure"
|
|
204
204
|
|
|
205
|
-
if hash["code_snippet"].is_a?(Array) && !hash["code_snippet"].empty?
|
|
206
|
-
return hash
|
|
207
|
-
end
|
|
205
|
+
return hash if hash["code_snippet"].is_a?(Array) && !hash["code_snippet"].empty?
|
|
208
206
|
|
|
209
207
|
file_path, line_number = resolve_failure_location(hash)
|
|
210
208
|
return hash unless file_path && line_number
|
|
@@ -123,8 +123,12 @@ module RubyReactor
|
|
|
123
123
|
@return_step
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
-
def middleware(middleware_class)
|
|
127
|
-
middlewares <<
|
|
126
|
+
def middleware(middleware_class, **options)
|
|
127
|
+
middlewares << if options.empty?
|
|
128
|
+
middleware_class
|
|
129
|
+
else
|
|
130
|
+
[middleware_class, options]
|
|
131
|
+
end
|
|
128
132
|
end
|
|
129
133
|
|
|
130
134
|
def validate_inputs(inputs_hash)
|
|
@@ -51,61 +51,89 @@ module RubyReactor
|
|
|
51
51
|
|
|
52
52
|
private
|
|
53
53
|
|
|
54
|
+
def middlewares
|
|
55
|
+
@context.middlewares || RubyReactor::MiddlewareRunner.new([])
|
|
56
|
+
end
|
|
57
|
+
|
|
54
58
|
def compensate_step(step_config, error, arguments)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
logged_result = if compensate_result.respond_to?(:value)
|
|
65
|
-
compensate_result.value
|
|
66
|
-
elsif compensate_result.respond_to?(:error)
|
|
67
|
-
compensate_result.error
|
|
68
|
-
else
|
|
69
|
-
compensate_result
|
|
70
|
-
end
|
|
59
|
+
middlewares.on(:start_compensation, step_config.name, error, arguments, @context)
|
|
60
|
+
begin
|
|
61
|
+
compensate_result = if step_config.compensate_block
|
|
62
|
+
step_config.compensate_block.call(error, arguments, @context)
|
|
63
|
+
elsif step_config.has_impl?
|
|
64
|
+
step_config.impl.compensate(error, arguments, @context)
|
|
65
|
+
else
|
|
66
|
+
RubyReactor.Success() # Default compensation
|
|
67
|
+
end
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
# Ensure we have a value to log
|
|
70
|
+
logged_result = if compensate_result.respond_to?(:value)
|
|
71
|
+
compensate_result.value
|
|
72
|
+
elsif compensate_result.respond_to?(:error)
|
|
73
|
+
compensate_result.error
|
|
74
|
+
else
|
|
75
|
+
compensate_result
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@context.execution_trace << {
|
|
79
|
+
type: :compensate,
|
|
80
|
+
step: step_config.name,
|
|
81
|
+
timestamp: Time.now,
|
|
82
|
+
result: logged_result,
|
|
83
|
+
arguments: arguments
|
|
84
|
+
}
|
|
85
|
+
@undo_trace << { type: :compensation, step: step_config.name, error: error, arguments: arguments }
|
|
86
|
+
|
|
87
|
+
if compensate_result.is_a?(RubyReactor::Failure)
|
|
88
|
+
middlewares.on(:failed_compensation, step_config.name, compensate_result, @context)
|
|
89
|
+
else
|
|
90
|
+
middlewares.on(:complete_compensation, step_config.name, compensate_result, @context)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
compensate_result
|
|
94
|
+
rescue StandardError => e
|
|
95
|
+
middlewares.on(:failed_compensation, step_config.name, e, @context)
|
|
96
|
+
raise e
|
|
97
|
+
end
|
|
81
98
|
end
|
|
82
99
|
|
|
83
100
|
def undo_step(step_config, result, arguments)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Ensure we have a value to log (if it's a Success/Failure object, get the value or error)
|
|
93
|
-
logged_result = if undo_result.respond_to?(:value)
|
|
94
|
-
undo_result.value
|
|
95
|
-
elsif undo_result.respond_to?(:error)
|
|
96
|
-
undo_result.error
|
|
101
|
+
middlewares.on(:start_undo, step_config.name, result, arguments, @context)
|
|
102
|
+
begin
|
|
103
|
+
undo_result = if step_config.undo_block
|
|
104
|
+
step_config.undo_block.call(result.value, arguments, @context)
|
|
105
|
+
elsif step_config.has_impl?
|
|
106
|
+
step_config.impl.undo(result.value, arguments, @context)
|
|
97
107
|
else
|
|
98
|
-
|
|
108
|
+
RubyReactor.Success()
|
|
99
109
|
end
|
|
100
110
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
# Ensure we have a value to log (if it's a Success/Failure object, get the value or error)
|
|
112
|
+
logged_result = if undo_result.respond_to?(:value)
|
|
113
|
+
undo_result.value
|
|
114
|
+
elsif undo_result.respond_to?(:error)
|
|
115
|
+
undo_result.error
|
|
116
|
+
else
|
|
117
|
+
undo_result
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
@context.execution_trace << { type: :undo, step: step_config.name, timestamp: Time.now, result: logged_result,
|
|
121
|
+
arguments: arguments }
|
|
122
|
+
|
|
123
|
+
if undo_result.is_a?(RubyReactor::Failure)
|
|
124
|
+
middlewares.on(:failed_undo, step_config.name, undo_result, @context)
|
|
125
|
+
else
|
|
126
|
+
middlewares.on(:complete_undo, step_config.name, undo_result, @context)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
undo_result
|
|
130
|
+
rescue StandardError => e
|
|
131
|
+
middlewares.on(:failed_undo, step_config.name, e, @context)
|
|
132
|
+
# Log undo failure but don't halt the rollback process
|
|
133
|
+
@context.execution_trace << { type: :undo_failure, step: step_config.name, timestamp: Time.now,
|
|
134
|
+
error: e.message }
|
|
135
|
+
RubyReactor.Failure(e)
|
|
136
|
+
end
|
|
109
137
|
end
|
|
110
138
|
end
|
|
111
139
|
end
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
module RubyReactor
|
|
4
4
|
class Executor
|
|
5
5
|
class RetryManager
|
|
6
|
-
def initialize(context)
|
|
6
|
+
def initialize(context, middlewares = nil)
|
|
7
7
|
@context = context
|
|
8
|
+
@middlewares = middlewares || context.middlewares || Executor.middlewares_for(context.reactor_class)
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def execute_with_retry(step_config, reactor_class)
|
|
@@ -47,11 +48,10 @@ module RubyReactor
|
|
|
47
48
|
@context.root_context || @context
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
puts "SERIALIZING CONTEXT: #{context_to_serialize.reactor_class.name}"
|
|
51
|
-
puts "INPUTS KEYS: #{context_to_serialize.inputs.keys}" if context_to_serialize.respond_to?(:inputs)
|
|
52
|
-
|
|
53
51
|
reactor_class_name = context_to_serialize.reactor_class.name
|
|
54
52
|
|
|
53
|
+
@middlewares.on(:before_async_enqueue, context_to_serialize)
|
|
54
|
+
|
|
55
55
|
serialized_context = ContextSerializer.serialize(context_to_serialize)
|
|
56
56
|
|
|
57
57
|
if @context.map_metadata
|
|
@@ -68,7 +68,8 @@ module RubyReactor
|
|
|
68
68
|
parent_reactor_class_name: map_args[:parent_reactor_class_name],
|
|
69
69
|
step_name: map_args[:step_name],
|
|
70
70
|
batch_size: map_args[:batch_size],
|
|
71
|
-
serialized_context: serialized_context
|
|
71
|
+
serialized_context: serialized_context,
|
|
72
|
+
fail_fast: map_args[:fail_fast]
|
|
72
73
|
)
|
|
73
74
|
else
|
|
74
75
|
configuration.async_router.perform_in(delay, serialized_context, reactor_class_name)
|
|
@@ -111,6 +112,15 @@ module RubyReactor
|
|
|
111
112
|
end
|
|
112
113
|
|
|
113
114
|
def handle_retryable_failure(step_config, reactor_class, result)
|
|
115
|
+
attempt_number = @context.retry_context.attempts_for_step(step_config.name)
|
|
116
|
+
@middlewares.on(
|
|
117
|
+
:retry_attempt,
|
|
118
|
+
step_config.name,
|
|
119
|
+
attempt_number,
|
|
120
|
+
result.error,
|
|
121
|
+
@context
|
|
122
|
+
)
|
|
123
|
+
|
|
114
124
|
# Check if we should requeue (async retry)
|
|
115
125
|
is_async = reactor_class.async? || step_config.async? ||
|
|
116
126
|
@context.root_context&.reactor_class&.async? ||
|
|
@@ -10,6 +10,7 @@ module RubyReactor
|
|
|
10
10
|
@retry_manager = managers[:retry_manager]
|
|
11
11
|
@result_handler = managers[:result_handler]
|
|
12
12
|
@compensation_manager = managers[:compensation_manager]
|
|
13
|
+
@middlewares = managers[:middlewares] || context.middlewares || Executor.middlewares_for(reactor_class)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def execute_all_steps
|
|
@@ -61,12 +62,28 @@ module RubyReactor
|
|
|
61
62
|
return RubyReactor.Success(@context.get_result(step_config.name))
|
|
62
63
|
end
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
resolved_arguments = resolve_arguments(step_config)
|
|
66
|
+
|
|
67
|
+
@middlewares.on(:start_step, step_config.name, resolved_arguments, @context)
|
|
68
|
+
completed = false
|
|
69
|
+
begin
|
|
70
|
+
result = if step_config.interrupt?
|
|
71
|
+
handle_interrupt_step(step_config)
|
|
72
|
+
elsif step_config.async? && !@context.inline_async_execution
|
|
73
|
+
handle_async_step(step_config)
|
|
74
|
+
else
|
|
75
|
+
execute_step_with_retry(step_config, resolved_arguments)
|
|
76
|
+
end
|
|
77
|
+
completed = true
|
|
78
|
+
if result.is_a?(RubyReactor::Failure)
|
|
79
|
+
@middlewares.on(:failed_step, step_config.name, result, @context)
|
|
80
|
+
else
|
|
81
|
+
@middlewares.on(:complete_step, step_config.name, result, @context)
|
|
82
|
+
end
|
|
83
|
+
result
|
|
84
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
85
|
+
@middlewares.on(:failed_step, step_config.name, e, @context) unless completed
|
|
86
|
+
raise
|
|
70
87
|
end
|
|
71
88
|
end
|
|
72
89
|
|
|
@@ -95,24 +112,22 @@ module RubyReactor
|
|
|
95
112
|
)
|
|
96
113
|
end
|
|
97
114
|
|
|
98
|
-
def execute_step_with_retry(step_config)
|
|
115
|
+
def execute_step_with_retry(step_config, resolved_arguments = nil)
|
|
116
|
+
resolved_arguments ||= resolve_arguments(step_config)
|
|
99
117
|
result = @retry_manager.execute_with_retry(step_config, @reactor_class) do
|
|
100
|
-
safe_execute_step_sync(step_config)
|
|
118
|
+
safe_execute_step_sync(step_config, resolved_arguments)
|
|
101
119
|
end
|
|
102
120
|
|
|
103
121
|
unless result.is_a?(RetryQueuedResult) || result.is_a?(RubyReactor::AsyncResult)
|
|
104
|
-
resolved_arguments = resolve_arguments(step_config)
|
|
105
122
|
@result_handler.handle_step_result(step_config, result, resolved_arguments)
|
|
106
123
|
end
|
|
107
124
|
|
|
108
125
|
result
|
|
109
126
|
end
|
|
110
127
|
|
|
111
|
-
def safe_execute_step_sync(step_config)
|
|
112
|
-
resolved_arguments
|
|
113
|
-
execute_step_sync_without_result_handling(step_config)
|
|
114
|
-
resolved_arguments = args
|
|
115
|
-
end
|
|
128
|
+
def safe_execute_step_sync(step_config, resolved_arguments = nil)
|
|
129
|
+
resolved_arguments ||= resolve_arguments(step_config)
|
|
130
|
+
execute_step_sync_without_result_handling(step_config, resolved_arguments)
|
|
116
131
|
rescue StandardError => e
|
|
117
132
|
# Identify redacted inputs
|
|
118
133
|
redact_inputs = @reactor_class.inputs.select { |_, config| config[:redact] }.keys
|
|
@@ -127,7 +142,7 @@ module RubyReactor
|
|
|
127
142
|
)
|
|
128
143
|
end
|
|
129
144
|
|
|
130
|
-
def execute_step_sync(step_config)
|
|
145
|
+
def execute_step_sync(step_config, resolved_arguments = nil)
|
|
131
146
|
@context.with_step(step_config.name) do
|
|
132
147
|
# Check conditions and guards
|
|
133
148
|
unless step_config.should_run?(@context)
|
|
@@ -136,7 +151,7 @@ module RubyReactor
|
|
|
136
151
|
end
|
|
137
152
|
|
|
138
153
|
# Resolve arguments
|
|
139
|
-
resolved_arguments
|
|
154
|
+
resolved_arguments ||= resolve_arguments(step_config)
|
|
140
155
|
|
|
141
156
|
# Validate arguments if validator is defined
|
|
142
157
|
validate_step_arguments(step_config, resolved_arguments)
|
|
@@ -150,7 +165,7 @@ module RubyReactor
|
|
|
150
165
|
end
|
|
151
166
|
|
|
152
167
|
# Execute step without handling the result (used during retries)
|
|
153
|
-
def execute_step_sync_without_result_handling(step_config)
|
|
168
|
+
def execute_step_sync_without_result_handling(step_config, resolved_arguments = nil)
|
|
154
169
|
@context.with_step(step_config.name) do
|
|
155
170
|
# Check conditions and guards
|
|
156
171
|
unless step_config.should_run?(@context)
|
|
@@ -159,7 +174,7 @@ module RubyReactor
|
|
|
159
174
|
end
|
|
160
175
|
|
|
161
176
|
# Resolve arguments
|
|
162
|
-
resolved_arguments
|
|
177
|
+
resolved_arguments ||= resolve_arguments(step_config)
|
|
163
178
|
|
|
164
179
|
yield resolved_arguments if block_given?
|
|
165
180
|
|
|
@@ -181,6 +196,9 @@ module RubyReactor
|
|
|
181
196
|
context_to_serialize = @context.root_context || @context
|
|
182
197
|
reactor_class_name = context_to_serialize.reactor_class.name
|
|
183
198
|
|
|
199
|
+
# Inject OTel context before serialization
|
|
200
|
+
@middlewares.on(:before_async_enqueue, context_to_serialize)
|
|
201
|
+
|
|
184
202
|
serialized_context = ContextSerializer.serialize(context_to_serialize)
|
|
185
203
|
|
|
186
204
|
configuration.async_router.perform_async(
|