ruby_reactor 0.3.2 → 0.4.1
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-config.json +18 -0
- data/.release-please-manifest.json +3 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +21 -0
- data/README.md +80 -4
- data/lib/ruby_reactor/context.rb +5 -2
- data/lib/ruby_reactor/context_serializer.rb +56 -3
- data/lib/ruby_reactor/dsl/reactor.rb +10 -1
- data/lib/ruby_reactor/executor/result_handler.rb +1 -12
- data/lib/ruby_reactor/executor.rb +7 -1
- data/lib/ruby_reactor/map/result_enumerator.rb +4 -3
- data/lib/ruby_reactor/rate_limit.rb +2 -2
- data/lib/ruby_reactor/reactor.rb +11 -4
- data/lib/ruby_reactor/sidekiq_workers/worker.rb +58 -1
- data/lib/ruby_reactor/storage/redis_adapter.rb +10 -3
- data/lib/ruby_reactor/storage/redis_locking.rb +17 -0
- data/lib/ruby_reactor/utils/backtrace_location.rb +37 -0
- data/lib/ruby_reactor/version.rb +1 -1
- data/lib/ruby_reactor/web/api.rb +68 -8
- data/lib/ruby_reactor/web/coordination_serializer.rb +180 -0
- data/lib/ruby_reactor/web/public/assets/index-CCnNVQy5.css +1 -0
- data/lib/ruby_reactor/web/public/assets/index-D7IBZvos.js +21 -0
- data/lib/ruby_reactor/web/public/index.html +2 -2
- data/lib/ruby_reactor.rb +7 -2
- metadata +11 -54
- data/documentation/DAG.md +0 -457
- data/documentation/README.md +0 -135
- data/documentation/async_reactors.md +0 -381
- data/documentation/composition.md +0 -199
- data/documentation/core_concepts.md +0 -676
- data/documentation/data_pipelines.md +0 -230
- data/documentation/examples/inventory_management.md +0 -748
- data/documentation/examples/order_processing.md +0 -380
- data/documentation/examples/payment_processing.md +0 -565
- data/documentation/getting_started.md +0 -242
- data/documentation/images/failed_order_processing.png +0 -0
- data/documentation/images/payment_workflow.png +0 -0
- data/documentation/interrupts.md +0 -163
- data/documentation/locks_and_semaphores.md +0 -459
- data/documentation/retry_configuration.md +0 -362
- data/documentation/testing.md +0 -994
- data/gui/.gitignore +0 -24
- data/gui/README.md +0 -73
- data/gui/eslint.config.js +0 -23
- data/gui/index.html +0 -13
- data/gui/package-lock.json +0 -5925
- data/gui/package.json +0 -46
- data/gui/postcss.config.js +0 -6
- data/gui/public/vite.svg +0 -1
- data/gui/src/App.css +0 -42
- data/gui/src/App.tsx +0 -51
- data/gui/src/assets/react.svg +0 -1
- data/gui/src/components/DagVisualizer.tsx +0 -424
- data/gui/src/components/Dashboard.tsx +0 -163
- data/gui/src/components/ErrorBoundary.tsx +0 -47
- data/gui/src/components/ReactorDetail.tsx +0 -135
- data/gui/src/components/StepInspector.tsx +0 -492
- data/gui/src/components/__tests__/DagVisualizer.test.tsx +0 -140
- data/gui/src/components/__tests__/ReactorDetail.test.tsx +0 -111
- data/gui/src/components/__tests__/StepInspector.test.tsx +0 -408
- data/gui/src/globals.d.ts +0 -7
- data/gui/src/index.css +0 -14
- data/gui/src/lib/utils.ts +0 -13
- data/gui/src/main.tsx +0 -14
- data/gui/src/test/setup.ts +0 -11
- data/gui/tailwind.config.js +0 -11
- data/gui/tsconfig.app.json +0 -28
- data/gui/tsconfig.json +0 -7
- data/gui/tsconfig.node.json +0 -26
- data/gui/vite.config.ts +0 -8
- data/gui/vitest.config.ts +0 -13
- data/lib/ruby_reactor/web/public/assets/index-VdeLgH9k.js +0 -19
- data/lib/ruby_reactor/web/public/assets/index-_z-6BvuM.css +0 -1
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>ui</title>
|
|
8
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-D7IBZvos.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CCnNVQy5.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
data/lib/ruby_reactor.rb
CHANGED
|
@@ -238,13 +238,14 @@ module RubyReactor
|
|
|
238
238
|
return backtrace if ENV["RUBY_REACTOR_DEBUG"] == "true"
|
|
239
239
|
return backtrace if backtrace.nil? || backtrace.empty?
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
internal_prefix = RubyReactor.internal_lib_path
|
|
242
242
|
filtered = []
|
|
243
243
|
filtered << backtrace.first
|
|
244
244
|
|
|
245
245
|
internal_block = false
|
|
246
246
|
backtrace[1..]&.each do |line|
|
|
247
|
-
|
|
247
|
+
file_path, = RubyReactor::Utils::BacktraceLocation.parse(line)
|
|
248
|
+
if file_path&.start_with?(internal_prefix)
|
|
248
249
|
unless internal_block
|
|
249
250
|
filtered << "... [ruby-reactor-internals-redacted-trace]"
|
|
250
251
|
internal_block = true
|
|
@@ -331,4 +332,8 @@ module RubyReactor
|
|
|
331
332
|
def self.root
|
|
332
333
|
Pathname.new(File.expand_path("..", __dir__))
|
|
333
334
|
end
|
|
335
|
+
|
|
336
|
+
def self.internal_lib_path
|
|
337
|
+
File.join(root.to_s, "lib")
|
|
338
|
+
end
|
|
334
339
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_reactor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Artur
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: dry-validation
|
|
@@ -89,57 +88,15 @@ executables: []
|
|
|
89
88
|
extensions: []
|
|
90
89
|
extra_rdoc_files: []
|
|
91
90
|
files:
|
|
91
|
+
- ".release-please-config.json"
|
|
92
|
+
- ".release-please-manifest.json"
|
|
92
93
|
- ".rspec"
|
|
93
94
|
- ".rubocop.yml"
|
|
95
|
+
- ".tool-versions"
|
|
96
|
+
- CHANGELOG.md
|
|
94
97
|
- CODE_OF_CONDUCT.md
|
|
95
98
|
- README.md
|
|
96
99
|
- Rakefile
|
|
97
|
-
- documentation/DAG.md
|
|
98
|
-
- documentation/README.md
|
|
99
|
-
- documentation/async_reactors.md
|
|
100
|
-
- documentation/composition.md
|
|
101
|
-
- documentation/core_concepts.md
|
|
102
|
-
- documentation/data_pipelines.md
|
|
103
|
-
- documentation/examples/inventory_management.md
|
|
104
|
-
- documentation/examples/order_processing.md
|
|
105
|
-
- documentation/examples/payment_processing.md
|
|
106
|
-
- documentation/getting_started.md
|
|
107
|
-
- documentation/images/failed_order_processing.png
|
|
108
|
-
- documentation/images/payment_workflow.png
|
|
109
|
-
- documentation/interrupts.md
|
|
110
|
-
- documentation/locks_and_semaphores.md
|
|
111
|
-
- documentation/retry_configuration.md
|
|
112
|
-
- documentation/testing.md
|
|
113
|
-
- gui/.gitignore
|
|
114
|
-
- gui/README.md
|
|
115
|
-
- gui/eslint.config.js
|
|
116
|
-
- gui/index.html
|
|
117
|
-
- gui/package-lock.json
|
|
118
|
-
- gui/package.json
|
|
119
|
-
- gui/postcss.config.js
|
|
120
|
-
- gui/public/vite.svg
|
|
121
|
-
- gui/src/App.css
|
|
122
|
-
- gui/src/App.tsx
|
|
123
|
-
- gui/src/assets/react.svg
|
|
124
|
-
- gui/src/components/DagVisualizer.tsx
|
|
125
|
-
- gui/src/components/Dashboard.tsx
|
|
126
|
-
- gui/src/components/ErrorBoundary.tsx
|
|
127
|
-
- gui/src/components/ReactorDetail.tsx
|
|
128
|
-
- gui/src/components/StepInspector.tsx
|
|
129
|
-
- gui/src/components/__tests__/DagVisualizer.test.tsx
|
|
130
|
-
- gui/src/components/__tests__/ReactorDetail.test.tsx
|
|
131
|
-
- gui/src/components/__tests__/StepInspector.test.tsx
|
|
132
|
-
- gui/src/globals.d.ts
|
|
133
|
-
- gui/src/index.css
|
|
134
|
-
- gui/src/lib/utils.ts
|
|
135
|
-
- gui/src/main.tsx
|
|
136
|
-
- gui/src/test/setup.ts
|
|
137
|
-
- gui/tailwind.config.js
|
|
138
|
-
- gui/tsconfig.app.json
|
|
139
|
-
- gui/tsconfig.json
|
|
140
|
-
- gui/tsconfig.node.json
|
|
141
|
-
- gui/vite.config.ts
|
|
142
|
-
- gui/vitest.config.ts
|
|
143
100
|
- lib/ruby_reactor.rb
|
|
144
101
|
- lib/ruby_reactor/configuration.rb
|
|
145
102
|
- lib/ruby_reactor/context.rb
|
|
@@ -210,6 +167,7 @@ files:
|
|
|
210
167
|
- lib/ruby_reactor/template/input.rb
|
|
211
168
|
- lib/ruby_reactor/template/result.rb
|
|
212
169
|
- lib/ruby_reactor/template/value.rb
|
|
170
|
+
- lib/ruby_reactor/utils/backtrace_location.rb
|
|
213
171
|
- lib/ruby_reactor/utils/code_extractor.rb
|
|
214
172
|
- lib/ruby_reactor/validation/base.rb
|
|
215
173
|
- lib/ruby_reactor/validation/input_validator.rb
|
|
@@ -218,8 +176,9 @@ files:
|
|
|
218
176
|
- lib/ruby_reactor/web/api.rb
|
|
219
177
|
- lib/ruby_reactor/web/application.rb
|
|
220
178
|
- lib/ruby_reactor/web/config.ru
|
|
221
|
-
- lib/ruby_reactor/web/
|
|
222
|
-
- lib/ruby_reactor/web/public/assets/index-
|
|
179
|
+
- lib/ruby_reactor/web/coordination_serializer.rb
|
|
180
|
+
- lib/ruby_reactor/web/public/assets/index-CCnNVQy5.css
|
|
181
|
+
- lib/ruby_reactor/web/public/assets/index-D7IBZvos.js
|
|
223
182
|
- lib/ruby_reactor/web/public/index.html
|
|
224
183
|
- lib/ruby_reactor/web/public/vite.svg
|
|
225
184
|
- llms-full.txt
|
|
@@ -233,7 +192,6 @@ metadata:
|
|
|
233
192
|
homepage_uri: https://github.com/arturictus/ruby_reactor
|
|
234
193
|
source_code_uri: https://github.com/arturictus/ruby_reactor
|
|
235
194
|
changelog_uri: https://github.com/arturictus/ruby_reactor/blob/main/CHANGELOG.md
|
|
236
|
-
post_install_message:
|
|
237
195
|
rdoc_options: []
|
|
238
196
|
require_paths:
|
|
239
197
|
- lib
|
|
@@ -248,8 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
248
206
|
- !ruby/object:Gem::Version
|
|
249
207
|
version: '0'
|
|
250
208
|
requirements: []
|
|
251
|
-
rubygems_version: 3.
|
|
252
|
-
signing_key:
|
|
209
|
+
rubygems_version: 3.6.9
|
|
253
210
|
specification_version: 4
|
|
254
211
|
summary: A dynamic, concurrent, dependency-resolving saga orchestrator for Ruby.
|
|
255
212
|
test_files: []
|
data/documentation/DAG.md
DELETED
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
# DAG Execution and Saga Patterns in RubyReactor
|
|
2
|
-
|
|
3
|
-
RubyReactor uses Directed Acyclic Graphs (DAGs) to manage step dependencies and implements saga patterns for reliable distributed transactions. This document explains how DAG execution works and how saga patterns ensure consistency across complex workflows.
|
|
4
|
-
|
|
5
|
-
## Directed Acyclic Graphs (DAGs)
|
|
6
|
-
|
|
7
|
-
A DAG is a graph with directed edges and no cycles, ensuring that step dependencies can be resolved in a deterministic order.
|
|
8
|
-
|
|
9
|
-
### DAG Structure
|
|
10
|
-
|
|
11
|
-
```mermaid
|
|
12
|
-
graph TD
|
|
13
|
-
A[Step A] --> C[Step C]
|
|
14
|
-
B[Step B] --> C
|
|
15
|
-
C --> D[Step D]
|
|
16
|
-
C --> E[Step E]
|
|
17
|
-
D --> F[Step F]
|
|
18
|
-
E --> F
|
|
19
|
-
|
|
20
|
-
style A fill:#e1f5fe
|
|
21
|
-
style B fill:#e1f5fe
|
|
22
|
-
style C fill:#fff3e0
|
|
23
|
-
style D fill:#e8f5e8
|
|
24
|
-
style E fill:#e8f5e8
|
|
25
|
-
style F fill:#ffebee
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
**Legend:**
|
|
29
|
-
- 🔵 Independent steps (can run in parallel)
|
|
30
|
-
- 🟡 Dependent steps (wait for prerequisites)
|
|
31
|
-
- 🟢 Ready to execute
|
|
32
|
-
- 🔴 Final step
|
|
33
|
-
|
|
34
|
-
### DAG Execution Algorithm
|
|
35
|
-
|
|
36
|
-
```mermaid
|
|
37
|
-
graph TD
|
|
38
|
-
A[Start Execution] --> B[Build Dependency Graph]
|
|
39
|
-
B --> C[Identify Ready Steps<br/>no unmet dependencies]
|
|
40
|
-
C --> D{Ready Steps<br/>Available?}
|
|
41
|
-
D -->|No| E[Execution Complete]
|
|
42
|
-
D -->|Yes| F[Execute Ready Steps<br/>in Parallel]
|
|
43
|
-
F --> G[Update Dependencies<br/>Mark steps complete]
|
|
44
|
-
G --> H[Collect Results]
|
|
45
|
-
H --> I{Execution<br/>Successful?}
|
|
46
|
-
I -->|Yes| C
|
|
47
|
-
I -->|No| J[Initiate Saga Compensation]
|
|
48
|
-
J --> K[Execution Failed]
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Dependency Resolution
|
|
52
|
-
|
|
53
|
-
```mermaid
|
|
54
|
-
graph TD
|
|
55
|
-
A[Step Dependencies] --> B[Topological Sort]
|
|
56
|
-
B --> C[Execution Order<br/>A → B → C → D]
|
|
57
|
-
C --> D[Parallel Execution<br/>Where Possible]
|
|
58
|
-
|
|
59
|
-
subgraph "Level 1"
|
|
60
|
-
A1[Independent Steps]
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
subgraph "Level 2"
|
|
64
|
-
B1[Steps depending on Level 1]
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
subgraph "Level 3"
|
|
68
|
-
C1[Steps depending on Level 2]
|
|
69
|
-
end
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Saga Patterns
|
|
73
|
-
|
|
74
|
-
RubyReactor implements saga patterns to ensure consistency across distributed operations. Sagas provide transactional semantics for long-running business processes.
|
|
75
|
-
|
|
76
|
-
### Saga Execution Flow
|
|
77
|
-
|
|
78
|
-
```mermaid
|
|
79
|
-
graph TD
|
|
80
|
-
A[Saga Initiated] --> B[Execute Step 1]
|
|
81
|
-
B --> C{Step 1<br/>Success?}
|
|
82
|
-
C -->|Yes| D[Execute Step 2]
|
|
83
|
-
C -->|No| E[Compensation Step 1]
|
|
84
|
-
D --> F{Step 2<br/>Success?}
|
|
85
|
-
F -->|Yes| G[Execute Step 3]
|
|
86
|
-
F -->|No| H[Compensate Step 2, Undo Step 1]
|
|
87
|
-
G --> I{Step 3<br/>Success?}
|
|
88
|
-
I -->|Yes| J[Saga Complete]
|
|
89
|
-
I -->|No| K[Compensate Step 3<br/>Then Undo Steps 2 and 1]
|
|
90
|
-
H --> L[Saga Failed]
|
|
91
|
-
K --> L
|
|
92
|
-
E --> L
|
|
93
|
-
|
|
94
|
-
style A fill:#e3f2fd
|
|
95
|
-
style J fill:#e8f5e8
|
|
96
|
-
style L fill:#ffebee
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Compensation Strategies
|
|
100
|
-
|
|
101
|
-
#### Backward Recovery (Rollback)
|
|
102
|
-
|
|
103
|
-
```mermaid
|
|
104
|
-
graph TD
|
|
105
|
-
A[Forward Execution] --> B[Step A Success]
|
|
106
|
-
B --> C[Step B Success]
|
|
107
|
-
C --> D[Step C Fails]
|
|
108
|
-
D --> E[Start Compensation]
|
|
109
|
-
E --> F[Compensate C]
|
|
110
|
-
F --> G[Undo B]
|
|
111
|
-
G --> H[Undo A]
|
|
112
|
-
H --> I[Consistent State Restored]
|
|
113
|
-
|
|
114
|
-
style A fill:#e3f2fd
|
|
115
|
-
style I fill:#e8f5e8
|
|
116
|
-
style D fill:#ffebee
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
#### Forward Recovery (Retry)
|
|
120
|
-
|
|
121
|
-
```mermaid
|
|
122
|
-
graph TD
|
|
123
|
-
A[Step Fails] --> B{Can Retry?}
|
|
124
|
-
B -->|Yes| C[Calculate Backoff]
|
|
125
|
-
C --> D[Schedule Retry]
|
|
126
|
-
D --> E[Retry Execution]
|
|
127
|
-
E --> F{Retry<br/>Success?}
|
|
128
|
-
F -->|Yes| G[Continue Saga]
|
|
129
|
-
F -->|No| B
|
|
130
|
-
B -->|No| H[Compensation Required]
|
|
131
|
-
|
|
132
|
-
style G fill:#e8f5e8
|
|
133
|
-
style H fill:#ffebee
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Complex Saga Scenarios
|
|
137
|
-
|
|
138
|
-
### Order Processing Saga
|
|
139
|
-
|
|
140
|
-
```mermaid
|
|
141
|
-
graph TD
|
|
142
|
-
A[Order Submitted] --> B[Validate Order]
|
|
143
|
-
B --> C{Valid?}
|
|
144
|
-
C -->|No| D[Saga Failed]
|
|
145
|
-
C -->|Yes| E[Reserve Inventory]
|
|
146
|
-
E --> F{Reserved?}
|
|
147
|
-
F -->|No| G[Saga Failed]
|
|
148
|
-
F -->|Yes| H[Process Payment]
|
|
149
|
-
H --> I{Payment OK?}
|
|
150
|
-
I -->|No| J[Release Inventory<br/>Compensation]
|
|
151
|
-
I -->|Yes| K[Update Order Status]
|
|
152
|
-
K --> L[Send Confirmation]
|
|
153
|
-
L --> M[Saga Complete]
|
|
154
|
-
|
|
155
|
-
J --> D
|
|
156
|
-
G --> D
|
|
157
|
-
|
|
158
|
-
style A fill:#e3f2fd
|
|
159
|
-
style M fill:#e8f5e8
|
|
160
|
-
style D fill:#ffebee
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
## DAG + Saga Integration
|
|
165
|
-
|
|
166
|
-
RubyReactor combines DAG execution with saga patterns for complex workflows:
|
|
167
|
-
|
|
168
|
-
```mermaid
|
|
169
|
-
graph TD
|
|
170
|
-
subgraph "DAG Execution"
|
|
171
|
-
A1[Build Graph] --> B1[Resolve Dependencies]
|
|
172
|
-
B1 --> C1[Execute in Order]
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
subgraph "Saga Pattern"
|
|
176
|
-
D1[Monitor Execution] --> E1{Step Fails?}
|
|
177
|
-
E1 -->|No| F1[Continue]
|
|
178
|
-
E1 -->|Yes| G1[Trigger Compensation]
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
subgraph "Retry Mechanism"
|
|
182
|
-
H1[Failed Step] --> I1{Can Retry?}
|
|
183
|
-
I1 -->|Yes| J1[Requeue Job]
|
|
184
|
-
I1 -->|No| K1[Final Compensation]
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
C1 --> D1
|
|
188
|
-
G1 --> H1
|
|
189
|
-
J1 --> A1
|
|
190
|
-
|
|
191
|
-
style F1 fill:#e8f5e8
|
|
192
|
-
style K1 fill:#ffebee
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## Execution States
|
|
196
|
-
|
|
197
|
-
### Step States
|
|
198
|
-
|
|
199
|
-
```mermaid
|
|
200
|
-
stateDiagram-v2
|
|
201
|
-
[*] --> Pending
|
|
202
|
-
Pending --> Running
|
|
203
|
-
Running --> Completed
|
|
204
|
-
Running --> Failed
|
|
205
|
-
Failed --> Compensating
|
|
206
|
-
Compensating --> Compensated
|
|
207
|
-
Compensated --> [*]
|
|
208
|
-
Failed --> Retrying
|
|
209
|
-
Retrying --> Pending
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Saga States
|
|
213
|
-
|
|
214
|
-
```mermaid
|
|
215
|
-
stateDiagram-v2
|
|
216
|
-
[*] --> Initiated
|
|
217
|
-
Initiated --> Executing
|
|
218
|
-
Executing --> Completed
|
|
219
|
-
Executing --> Failing
|
|
220
|
-
Failing --> Compensating
|
|
221
|
-
Compensating --> Failed
|
|
222
|
-
Failed --> [*]
|
|
223
|
-
Completed --> [*]
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Error Handling Patterns
|
|
227
|
-
|
|
228
|
-
### Cascading Compensation
|
|
229
|
-
|
|
230
|
-
```mermaid
|
|
231
|
-
graph TD
|
|
232
|
-
A[Root Cause Failure] --> B[Step N Fails]
|
|
233
|
-
B --> C[Mark Step N for Compensation]
|
|
234
|
-
C --> D[Stop Downstream Steps]
|
|
235
|
-
D --> E[Execute Compensation Chain]
|
|
236
|
-
E --> F[Step N-1 Compensation]
|
|
237
|
-
F --> G[Step N-2 Compensation]
|
|
238
|
-
G --> H[...continue to Step 1]
|
|
239
|
-
H --> I[Consistent State Achieved]
|
|
240
|
-
|
|
241
|
-
style A fill:#ffebee
|
|
242
|
-
style I fill:#e8f5e8
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### Partial Success Handling
|
|
246
|
-
|
|
247
|
-
```mermaid
|
|
248
|
-
graph TD
|
|
249
|
-
A[Multi-Step Process] --> B[Step 1 Success]
|
|
250
|
-
B --> C[Step 2 Success]
|
|
251
|
-
C --> D[Step 3 Fails]
|
|
252
|
-
D --> E{Essential<br/>Step?}
|
|
253
|
-
E -->|Yes| F[Full Compensation Required]
|
|
254
|
-
E -->|No| G[Partial Success Acceptable]
|
|
255
|
-
G --> H[Continue with Successful Steps]
|
|
256
|
-
H --> I[Manual Intervention for Failed Step]
|
|
257
|
-
|
|
258
|
-
style G fill:#fff3e0
|
|
259
|
-
style I fill:#fff3e0
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
## Performance Considerations
|
|
263
|
-
|
|
264
|
-
### Parallel Execution in DAGs
|
|
265
|
-
|
|
266
|
-
```mermaid
|
|
267
|
-
graph TD
|
|
268
|
-
A[Level 1<br/>Parallel Steps] --> B[Level 2<br/>Parallel Steps]
|
|
269
|
-
B --> C[Level 3<br/>Parallel Steps]
|
|
270
|
-
|
|
271
|
-
subgraph "Level 1 (3 steps)"
|
|
272
|
-
A1[Step A] & A2[Step B] & A3[Step C]
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
subgraph "Level 2 (2 steps)"
|
|
276
|
-
B1[Step D] & B2[Step E]
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
subgraph "Level 3 (1 step)"
|
|
280
|
-
C1[Step F]
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
A1 --> B1
|
|
284
|
-
A2 --> B1
|
|
285
|
-
A3 --> B2
|
|
286
|
-
B1 --> C1
|
|
287
|
-
B2 --> C1
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Saga Overhead
|
|
291
|
-
|
|
292
|
-
```mermaid
|
|
293
|
-
graph TD
|
|
294
|
-
A[Normal Execution] --> B[Fast Path<br/>No Compensation]
|
|
295
|
-
C[Saga Execution] --> D[State Tracking<br/>Compensation Logic]
|
|
296
|
-
D --> E[Increased Complexity]
|
|
297
|
-
|
|
298
|
-
A --> F[Better Performance]
|
|
299
|
-
C --> G[Reliability Benefits]
|
|
300
|
-
|
|
301
|
-
style F fill:#e8f5e8
|
|
302
|
-
style G fill:#e8f5e8
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
## Monitoring and Observability
|
|
306
|
-
|
|
307
|
-
### Saga Metrics
|
|
308
|
-
|
|
309
|
-
```mermaid
|
|
310
|
-
graph TD
|
|
311
|
-
A[Saga Metrics] --> B[Completion Rate]
|
|
312
|
-
A --> C[Average Duration]
|
|
313
|
-
A --> D[Compensation Frequency]
|
|
314
|
-
A --> E[Retry Attempts]
|
|
315
|
-
A --> F[Failure Patterns]
|
|
316
|
-
|
|
317
|
-
B --> G[Business Health]
|
|
318
|
-
C --> H[Performance Monitoring]
|
|
319
|
-
D --> I[Reliability Insights]
|
|
320
|
-
E --> J[Resilience Metrics]
|
|
321
|
-
F --> K[Root Cause Analysis]
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
### DAG Execution Monitoring
|
|
325
|
-
|
|
326
|
-
```mermaid
|
|
327
|
-
graph TD
|
|
328
|
-
A[DAG Monitoring] --> B[Step Dependencies]
|
|
329
|
-
A --> C[Execution Order]
|
|
330
|
-
A --> D[Parallel Execution]
|
|
331
|
-
A --> E[Bottleneck Identification]
|
|
332
|
-
|
|
333
|
-
B --> F[Dependency Health]
|
|
334
|
-
C --> G[Execution Flow]
|
|
335
|
-
D --> H[Concurrency Optimization]
|
|
336
|
-
E --> I[Performance Tuning]
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
## Best Practices
|
|
340
|
-
|
|
341
|
-
### DAG Design
|
|
342
|
-
|
|
343
|
-
1. **Keep it Simple**: Minimize dependencies to maximize parallelism
|
|
344
|
-
2. **Clear Dependencies**: Make step relationships explicit
|
|
345
|
-
3. **Avoid Cycles**: Ensure DAG remains acyclic
|
|
346
|
-
4. **Test Execution Order**: Verify topological sort produces expected order
|
|
347
|
-
|
|
348
|
-
### Saga Implementation
|
|
349
|
-
|
|
350
|
-
1. **Idempotent Operations**: Design steps to be safely retryable
|
|
351
|
-
2. **Compensation Logic**: Always implement proper undo operations
|
|
352
|
-
3. **State Tracking**: Maintain sufficient state for compensation
|
|
353
|
-
4. **Timeout Handling**: Set appropriate timeouts for long-running sagas
|
|
354
|
-
|
|
355
|
-
### Error Handling
|
|
356
|
-
|
|
357
|
-
1. **Graceful Degradation**: Handle partial failures appropriately
|
|
358
|
-
2. **Circuit Breakers**: Prevent cascade failures
|
|
359
|
-
3. **Monitoring**: Track saga health and failure patterns
|
|
360
|
-
4. **Recovery Procedures**: Document manual recovery processes
|
|
361
|
-
|
|
362
|
-
## Implementation Examples
|
|
363
|
-
|
|
364
|
-
### Simple DAG with Saga
|
|
365
|
-
|
|
366
|
-
```ruby
|
|
367
|
-
class OrderProcessingReactor < RubyReactor::Reactor
|
|
368
|
-
async true
|
|
369
|
-
|
|
370
|
-
step :validate_order do
|
|
371
|
-
run { validate_order_logic }
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
step :reserve_inventory do
|
|
375
|
-
argument :order, result(:validate_order)
|
|
376
|
-
run do |args, _context|
|
|
377
|
-
reserve_inventory_logic(args[:order])
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
compensate do
|
|
381
|
-
# Release reservation
|
|
382
|
-
release_inventory_logic
|
|
383
|
-
end
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
step :process_payment do
|
|
387
|
-
argument :inventory_result, result(:reserve_inventory)
|
|
388
|
-
run do |args, _context|
|
|
389
|
-
process_payment_logic(args[:inventory_result])
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
compensate do
|
|
393
|
-
# Refund payment
|
|
394
|
-
refund_payment_logic
|
|
395
|
-
end
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
step :confirm_order do
|
|
399
|
-
argument :payment_result, result(:process_payment)
|
|
400
|
-
run do |args, _context|
|
|
401
|
-
confirm_order_logic(args[:payment_result])
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
end
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
### Complex DAG with Parallel Execution
|
|
408
|
-
|
|
409
|
-
```ruby
|
|
410
|
-
class ComplexWorkflowReactor < RubyReactor::Reactor
|
|
411
|
-
async true
|
|
412
|
-
|
|
413
|
-
# Level 1 - Independent steps
|
|
414
|
-
step :validate_input do
|
|
415
|
-
run { validate_input_logic }
|
|
416
|
-
end
|
|
417
|
-
|
|
418
|
-
step :check_permissions do
|
|
419
|
-
run { check_permissions_logic }
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
# Level 2 - Depends on level 1
|
|
423
|
-
step :process_data do
|
|
424
|
-
argument :validation_result, result(:validate_input)
|
|
425
|
-
argument :permissions_result, result(:check_permissions)
|
|
426
|
-
run do |args, _context|
|
|
427
|
-
process_data_logic(args[:validation_result], args[:permissions_result])
|
|
428
|
-
end
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
# Level 3 - Parallel steps depending on level 2
|
|
432
|
-
step :send_notification do
|
|
433
|
-
argument :data_result, result(:process_data)
|
|
434
|
-
run do |args, _context|
|
|
435
|
-
send_notification_logic(args[:data_result])
|
|
436
|
-
end
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
step :update_audit_log do
|
|
440
|
-
argument :data_result, result(:process_data)
|
|
441
|
-
run do |args, _context|
|
|
442
|
-
update_audit_log_logic(args[:data_result])
|
|
443
|
-
end
|
|
444
|
-
end
|
|
445
|
-
|
|
446
|
-
# Level 4 - Depends on level 3
|
|
447
|
-
step :cleanup do
|
|
448
|
-
argument :notification_result, result(:send_notification)
|
|
449
|
-
argument :audit_result, result(:update_audit_log)
|
|
450
|
-
run do |args, _context|
|
|
451
|
-
cleanup_logic(args[:notification_result], args[:audit_result])
|
|
452
|
-
end
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
This DAG allows `send_notification` and `update_audit_log` to execute in parallel after `process_data` completes, demonstrating how RubyReactor maximizes concurrency while maintaining dependency ordering
|