dirty_pipeline 0.2.0 → 0.3.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/lib/dirty_pipeline/base.rb +50 -90
- data/lib/dirty_pipeline/locker.rb +0 -3
- data/lib/dirty_pipeline/status.rb +71 -0
- data/lib/dirty_pipeline/storage.rb +3 -1
- data/lib/dirty_pipeline/transition.rb +18 -4
- data/lib/dirty_pipeline/version.rb +1 -1
- data/lib/dirty_pipeline.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b8f5e951d183bae44a012daeccb345d84b0ccc0dd26edeb1006cfe67cca27ba
|
|
4
|
+
data.tar.gz: 2b166ca245ea06ede75099e5c511749e9b9af04414446948e81db9ad5ce2b485
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ca3237ae6a6f282040216add989af7a84201d9d1a09be280d177c83de6ff752d55e7d5b3394c1f1e887677ec3e6280feff90f450df5ae18a550873f49bd80b9
|
|
7
|
+
data.tar.gz: 0c45a1200ffa8074fda41c1cf7839444d2f72d74a3bf1d4959f8f2a599ffac6aff0eed0a00f973799e4a99f4ef7e4f0fc983301bd39eba0306386592f651aace
|
data/lib/dirty_pipeline/base.rb
CHANGED
|
@@ -76,105 +76,53 @@ module DirtyPipeline
|
|
|
76
76
|
storage.reset_pipeline_status!
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
def clear!
|
|
80
|
+
storage.clear!
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def cache
|
|
84
|
+
storage.store["cache"]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
attr_reader :subject, :error, :storage, :status
|
|
80
88
|
def initialize(subject)
|
|
81
89
|
@subject = subject
|
|
82
90
|
@storage = Storage.new(subject, self.class.pipeline_storage)
|
|
83
91
|
@locker = Locker.new(@subject, @storage)
|
|
92
|
+
@status = Status.new(self)
|
|
84
93
|
end
|
|
85
94
|
|
|
86
95
|
def call(*args)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
throw :success, action.(subject, *transition_args)
|
|
96
|
+
Result() do
|
|
97
|
+
after_commit = nil
|
|
98
|
+
# transaction with support of external calls
|
|
99
|
+
transaction(*args) do |destination, action, *targs|
|
|
100
|
+
output = {}
|
|
101
|
+
fail_cause = nil
|
|
102
|
+
|
|
103
|
+
output, *after_commit = catch(:success) do
|
|
104
|
+
fail_cause = catch(:fail_with_error) do
|
|
105
|
+
Abort() if catch(:abort) do
|
|
106
|
+
throw :success, action.(self, *targs)
|
|
107
|
+
end
|
|
100
108
|
end
|
|
109
|
+
nil
|
|
101
110
|
end
|
|
102
|
-
nil
|
|
103
|
-
end
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
if fail_cause
|
|
113
|
+
ExpectedError(fail_cause)
|
|
114
|
+
else
|
|
115
|
+
Success(destination, output)
|
|
116
|
+
end
|
|
109
117
|
end
|
|
110
|
-
end
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
self
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def clear!
|
|
117
|
-
storage.clear!
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def success?
|
|
121
|
-
succeeded
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def when_success(callback = nil)
|
|
125
|
-
return self unless success?
|
|
126
|
-
if block_given?
|
|
127
|
-
yield(self)
|
|
128
|
-
else
|
|
129
|
-
callback.call(self)
|
|
130
|
-
end
|
|
131
|
-
self
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def when_failed(callback = nil)
|
|
135
|
-
return self unless storage.failed?
|
|
136
|
-
if block_given?
|
|
137
|
-
yield(self)
|
|
138
|
-
else
|
|
139
|
-
callback.call(self)
|
|
140
|
-
end
|
|
141
|
-
self
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def errored?
|
|
145
|
-
return if succeeded.nil?
|
|
146
|
-
ready? && !succeeded
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def when_error(callback = nil)
|
|
150
|
-
return self unless errored?
|
|
151
|
-
if block_given?
|
|
152
|
-
yield(self)
|
|
153
|
-
else
|
|
154
|
-
callback.call(self)
|
|
119
|
+
Array(after_commit).each { |cb| cb.call(subject) } if after_commit
|
|
155
120
|
end
|
|
156
|
-
self
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def ready?
|
|
160
|
-
storage.pipeline_status.nil?
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def when_processing(callback = nil)
|
|
164
|
-
return self unless storage.processing?
|
|
165
|
-
if block_given?
|
|
166
|
-
yield(self)
|
|
167
|
-
else
|
|
168
|
-
callback.call(self)
|
|
169
|
-
end
|
|
170
|
-
self
|
|
171
121
|
end
|
|
172
122
|
|
|
173
123
|
private
|
|
174
124
|
|
|
175
|
-
attr_writer :error
|
|
176
125
|
attr_reader :locker
|
|
177
|
-
attr_accessor :succeeded, :previous_status
|
|
178
126
|
|
|
179
127
|
def find_subject_args
|
|
180
128
|
subject.id
|
|
@@ -188,6 +136,10 @@ module DirtyPipeline
|
|
|
188
136
|
self.class.cleanup_delay || DEFAULT_CLEANUP_DELAY
|
|
189
137
|
end
|
|
190
138
|
|
|
139
|
+
def Result()
|
|
140
|
+
status.wrap { yield }
|
|
141
|
+
end
|
|
142
|
+
|
|
191
143
|
def Retry(error, *args)
|
|
192
144
|
storage.save_retry!(error)
|
|
193
145
|
Shipping::PipelineWorker.perform_in(
|
|
@@ -199,25 +151,26 @@ module DirtyPipeline
|
|
|
199
151
|
end
|
|
200
152
|
|
|
201
153
|
def ExpectedError(cause)
|
|
202
|
-
|
|
154
|
+
status.error = cause
|
|
203
155
|
storage.fail_event!
|
|
204
|
-
|
|
156
|
+
status.succeeded = false
|
|
205
157
|
end
|
|
206
158
|
|
|
207
|
-
def
|
|
159
|
+
def Exception(error)
|
|
208
160
|
storage.save_exception!(error)
|
|
209
|
-
|
|
210
|
-
|
|
161
|
+
status.error = error
|
|
162
|
+
status.succeeded = false
|
|
211
163
|
end
|
|
212
164
|
|
|
213
165
|
def Abort()
|
|
214
|
-
|
|
166
|
+
status.succeeded = false
|
|
215
167
|
throw :abort_transaction, true
|
|
216
168
|
end
|
|
217
169
|
|
|
218
170
|
def Success(destination, output)
|
|
171
|
+
cache.clear
|
|
219
172
|
storage.complete!(output, destination)
|
|
220
|
-
|
|
173
|
+
status.succeeded = true
|
|
221
174
|
end
|
|
222
175
|
|
|
223
176
|
def try_again?(max_attempts_count)
|
|
@@ -252,6 +205,7 @@ module DirtyPipeline
|
|
|
252
205
|
destination, action, max_attempts_count =
|
|
253
206
|
find_transition(transition).values_at(:to, :action, :attempts)
|
|
254
207
|
|
|
208
|
+
status.action_pool.unshift(action)
|
|
255
209
|
subject.transaction(requires_new: true) do
|
|
256
210
|
raise ActiveRecord::Rollback if catch(:abort_transaction) do
|
|
257
211
|
yield(destination, action, *transition_args); nil
|
|
@@ -261,10 +215,16 @@ module DirtyPipeline
|
|
|
261
215
|
if try_again?(max_attempts_count)
|
|
262
216
|
Retry(error)
|
|
263
217
|
else
|
|
264
|
-
|
|
265
|
-
Error(error)
|
|
218
|
+
Exception(error)
|
|
266
219
|
end
|
|
267
220
|
raise
|
|
221
|
+
ensure
|
|
222
|
+
if status.succeeded == false
|
|
223
|
+
status.action_pool.each do |reversable_action|
|
|
224
|
+
next unless reversable_action.respond_to?(:undo)
|
|
225
|
+
reversable_action.undo(self, *transition_args)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
268
228
|
end
|
|
269
229
|
end
|
|
270
230
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module DirtyPipeline
|
|
2
|
+
class Status < SimpleDelegator
|
|
3
|
+
attr_accessor :error, :succeeded, :action_pool
|
|
4
|
+
attr_reader :storage, :pipeline
|
|
5
|
+
def initialize(*)
|
|
6
|
+
super
|
|
7
|
+
@storage = __getobj__.storage
|
|
8
|
+
@action_pool = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def wrap
|
|
12
|
+
return self if succeeded == false
|
|
13
|
+
self.succeeded = nil
|
|
14
|
+
yield
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def success?
|
|
19
|
+
succeeded
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def when_success(callback = nil)
|
|
23
|
+
return self unless success?
|
|
24
|
+
if block_given?
|
|
25
|
+
yield(self)
|
|
26
|
+
else
|
|
27
|
+
callback.call(self)
|
|
28
|
+
end
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def when_failed(callback = nil)
|
|
33
|
+
return self unless storage.failed?
|
|
34
|
+
if block_given?
|
|
35
|
+
yield(self)
|
|
36
|
+
else
|
|
37
|
+
callback.call(self)
|
|
38
|
+
end
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def errored?
|
|
43
|
+
return if succeeded.nil?
|
|
44
|
+
ready? && !succeeded
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def when_error(callback = nil)
|
|
48
|
+
return self unless errored?
|
|
49
|
+
if block_given?
|
|
50
|
+
yield(self)
|
|
51
|
+
else
|
|
52
|
+
callback.call(self)
|
|
53
|
+
end
|
|
54
|
+
self
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def ready?
|
|
58
|
+
storage.pipeline_status.nil?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def when_processing(callback = nil)
|
|
62
|
+
return self unless storage.processing?
|
|
63
|
+
if block_given?
|
|
64
|
+
yield(self)
|
|
65
|
+
else
|
|
66
|
+
callback.call(self)
|
|
67
|
+
end
|
|
68
|
+
self
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -17,7 +17,7 @@ module DirtyPipeline
|
|
|
17
17
|
def init_store(store_field)
|
|
18
18
|
self.store = subject.send(store_field).to_h
|
|
19
19
|
clear! if store.empty?
|
|
20
|
-
return if (store.keys & %w(status events errors state)).size ==
|
|
20
|
+
return if (store.keys & %w(cache status events errors state)).size == 5
|
|
21
21
|
raise InvalidPipelineStorage, store
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -27,6 +27,7 @@ module DirtyPipeline
|
|
|
27
27
|
"status" => nil,
|
|
28
28
|
"pipeline_status" => nil,
|
|
29
29
|
"state" => {},
|
|
30
|
+
"cache" => {},
|
|
30
31
|
"events" => [],
|
|
31
32
|
"errors" => [],
|
|
32
33
|
)
|
|
@@ -99,6 +100,7 @@ module DirtyPipeline
|
|
|
99
100
|
|
|
100
101
|
def commit_pipeline_status!(value = nil)
|
|
101
102
|
self.pipeline_status = value
|
|
103
|
+
store["cache"].clear
|
|
102
104
|
commit!
|
|
103
105
|
end
|
|
104
106
|
alias :reset_pipeline_status! :commit_pipeline_status!
|
|
@@ -15,11 +15,25 @@ module DirtyPipeline
|
|
|
15
15
|
throw :success, result
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def self.undo(*args, **kwargs)
|
|
19
|
+
pipeline = args.shift
|
|
20
|
+
instance = new(pipeline, *args, **kwargs)
|
|
21
|
+
return unless instance.respond_to?(:undo)
|
|
22
|
+
instance.undo(pipeline.subject)
|
|
23
|
+
end
|
|
24
|
+
|
|
18
25
|
def self.call(*args, **kwargs)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
pipeline = args.shift
|
|
27
|
+
new(pipeline, *args, **kwargs).call(pipeline.subject)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :pipeline
|
|
31
|
+
def initialize(pipeline, *, **)
|
|
32
|
+
@pipeline = pipeline
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fetch(key)
|
|
36
|
+
pipeline.cache.fetch(key) { pipeline.cache[key] = yield }
|
|
23
37
|
end
|
|
24
38
|
end
|
|
25
39
|
end
|
data/lib/dirty_pipeline.rb
CHANGED
|
@@ -4,6 +4,7 @@ module DirtyPipeline
|
|
|
4
4
|
require_relative "dirty_pipeline/locker.rb"
|
|
5
5
|
require_relative "dirty_pipeline/storage.rb"
|
|
6
6
|
require_relative "dirty_pipeline/transition.rb"
|
|
7
|
+
require_relative "dirty_pipeline/status.rb"
|
|
7
8
|
require_relative "dirty_pipeline/base.rb"
|
|
8
9
|
|
|
9
10
|
# This method should yield raw Redis connection
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dirty_pipeline
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergey Dolganov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-08-
|
|
11
|
+
date: 2018-08-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -73,6 +73,7 @@ files:
|
|
|
73
73
|
- lib/dirty_pipeline.rb
|
|
74
74
|
- lib/dirty_pipeline/base.rb
|
|
75
75
|
- lib/dirty_pipeline/locker.rb
|
|
76
|
+
- lib/dirty_pipeline/status.rb
|
|
76
77
|
- lib/dirty_pipeline/storage.rb
|
|
77
78
|
- lib/dirty_pipeline/transition.rb
|
|
78
79
|
- lib/dirty_pipeline/version.rb
|