dynflow 1.4.3 → 1.4.4
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/dynflow/connectors/abstract.rb +4 -0
- data/lib/dynflow/connectors/database.rb +4 -0
- data/lib/dynflow/connectors/direct.rb +5 -0
- data/lib/dynflow/executors.rb +32 -10
- data/lib/dynflow/persistence.rb +8 -0
- data/lib/dynflow/persistence_adapters/abstract.rb +16 -0
- data/lib/dynflow/persistence_adapters/sequel.rb +23 -7
- data/lib/dynflow/persistence_adapters/sequel_migrations/020_drop_duplicate_indices.rb +30 -0
- data/lib/dynflow/rails/configuration.rb +11 -4
- data/lib/dynflow/version.rb +1 -1
- data/lib/dynflow/world/invalidation.rb +4 -0
- data/test/persistence_test.rb +36 -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: 541dbb9bd3cd2b7e0deb00b940ac1d387518d2a884263646e6bcc7666ad9a934
|
4
|
+
data.tar.gz: 7741c155b2538ab52c47fd21a2a56bd019bdfd15f5c69e48b4e37044d941bc5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1965d10b547c1fb091c7dbabeaef50f7825d3b472dc911b5f17d6ee8366c401577234bc6836c85e932c58548bfc6cac82bbce4719ea637793b9815a4feebcb5f
|
7
|
+
data.tar.gz: 66ee5081a627072548a30dedebce8cac285bd6bf0630aa3ddf93305852f6d3daded8d05260c5a896db45345913cb1a0a7a2ef4fa6dbde45aa29b3159ff61a595
|
@@ -25,6 +25,10 @@ module Dynflow
|
|
25
25
|
raise NotImplementedError
|
26
26
|
end
|
27
27
|
|
28
|
+
def prune_undeliverable_envelopes(world)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
28
32
|
# we need to pass the world, as the connector can be shared
|
29
33
|
# between words: we need to know the one to send the message to
|
30
34
|
def receive(world, envelope)
|
@@ -172,6 +172,10 @@ module Dynflow
|
|
172
172
|
Telemetry.with_instance { |t| t.increment_counter(:dynflow_connector_envelopes, 1, :world => envelope.sender_id, :direction => 'outgoing') }
|
173
173
|
@core.ask([:handle_envelope, envelope])
|
174
174
|
end
|
175
|
+
|
176
|
+
def prune_undeliverable_envelopes(world)
|
177
|
+
world.persistence.prune_undeliverable_envelopes
|
178
|
+
end
|
175
179
|
end
|
176
180
|
end
|
177
181
|
end
|
@@ -68,6 +68,11 @@ module Dynflow
|
|
68
68
|
Telemetry.with_instance { |t| t.increment_counter(:dynflow_connector_envelopes, 1, :world => envelope.sender_id) }
|
69
69
|
@core.ask([:handle_envelope, envelope])
|
70
70
|
end
|
71
|
+
|
72
|
+
def prune_undeliverable_envelopes(_world)
|
73
|
+
# This is a noop
|
74
|
+
0
|
75
|
+
end
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
data/lib/dynflow/executors.rb
CHANGED
@@ -4,16 +4,38 @@ module Dynflow
|
|
4
4
|
|
5
5
|
require 'dynflow/executors/parallel'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
class << self
|
8
|
+
# Every time we run a code that can be defined outside of Dynflow,
|
9
|
+
# we should wrap it with this method, and we can ensure here to do
|
10
|
+
# necessary cleanup, such as cleaning ActiveRecord connections
|
11
|
+
def run_user_code
|
12
|
+
# Here we cover a case where the connection was already checked out from
|
13
|
+
# the pool and had opened transactions. In that case, we should leave the
|
14
|
+
# cleanup to the other runtime unit which opened the transaction. If the
|
15
|
+
# connection was checked out or there are no opened transactions, we can
|
16
|
+
# safely perform the cleanup.
|
17
|
+
no_previously_opened_transactions = active_record_open_transactions.zero?
|
18
|
+
yield
|
19
|
+
ensure
|
20
|
+
::ActiveRecord::Base.clear_active_connections! if no_previously_opened_transactions && active_record_connected?
|
21
|
+
::Logging.mdc.clear if defined? ::Logging
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def active_record_open_transactions
|
27
|
+
active_record_active_connection&.open_transactions || 0
|
28
|
+
end
|
17
29
|
|
30
|
+
def active_record_active_connection
|
31
|
+
return unless defined?(::ActiveRecord) && ::ActiveRecord::Base.connected?
|
32
|
+
# #active_connection? returns the connection if already established or nil
|
33
|
+
::ActiveRecord::Base.connection_pool.active_connection?
|
34
|
+
end
|
35
|
+
|
36
|
+
def active_record_connected?
|
37
|
+
!!active_record_active_connection
|
38
|
+
end
|
39
|
+
end
|
18
40
|
end
|
19
41
|
end
|
data/lib/dynflow/persistence.rb
CHANGED
@@ -134,5 +134,13 @@ module Dynflow
|
|
134
134
|
envelope
|
135
135
|
end
|
136
136
|
end
|
137
|
+
|
138
|
+
def prune_envelopes(receiver_ids)
|
139
|
+
adapter.prune_envelopes(receiver_ids)
|
140
|
+
end
|
141
|
+
|
142
|
+
def prune_undeliverable_envelopes
|
143
|
+
adapter.prune_undeliverable_envelopes
|
144
|
+
end
|
137
145
|
end
|
138
146
|
end
|
@@ -116,6 +116,22 @@ module Dynflow
|
|
116
116
|
def push_envelope(envelope)
|
117
117
|
raise NotImplementedError
|
118
118
|
end
|
119
|
+
|
120
|
+
def prune_envelopes(receiver_ids)
|
121
|
+
raise NotImplementedError
|
122
|
+
end
|
123
|
+
|
124
|
+
def prune_undeliverable_envelopes
|
125
|
+
raise NotImplementedError
|
126
|
+
end
|
127
|
+
|
128
|
+
def migrate_db
|
129
|
+
raise NotImplementedError
|
130
|
+
end
|
131
|
+
|
132
|
+
def abort_if_pending_migrations!
|
133
|
+
raise NotImplementedError
|
134
|
+
end
|
119
135
|
end
|
120
136
|
end
|
121
137
|
end
|
@@ -45,13 +45,15 @@ module Dynflow
|
|
45
45
|
step: %w(error children) }
|
46
46
|
|
47
47
|
def initialize(config)
|
48
|
+
migrate = true
|
48
49
|
config = config.dup
|
49
50
|
@additional_responsibilities = { coordinator: true, connector: true }
|
50
|
-
if config.is_a?(Hash)
|
51
|
-
@additional_responsibilities.merge!(config.delete(:additional_responsibilities))
|
51
|
+
if config.is_a?(Hash)
|
52
|
+
@additional_responsibilities.merge!(config.delete(:additional_responsibilities)) if config.key?(:additional_responsibilities)
|
53
|
+
migrate = config.fetch(:migrate, true)
|
52
54
|
end
|
53
55
|
@db = initialize_db config
|
54
|
-
migrate_db
|
56
|
+
migrate_db if migrate
|
55
57
|
end
|
56
58
|
|
57
59
|
def transaction(&block)
|
@@ -198,6 +200,16 @@ module Dynflow
|
|
198
200
|
table(:envelope).insert(prepare_record(:envelope, envelope))
|
199
201
|
end
|
200
202
|
|
203
|
+
def prune_envelopes(receiver_ids)
|
204
|
+
connector_feature!
|
205
|
+
table(:envelope).where(receiver_id: receiver_ids).delete
|
206
|
+
end
|
207
|
+
|
208
|
+
def prune_undeliverable_envelopes
|
209
|
+
connector_feature!
|
210
|
+
table(:envelope).where(receiver_id: table(:coordinator_record).select(:id)).invert.delete
|
211
|
+
end
|
212
|
+
|
201
213
|
def coordinator_feature!
|
202
214
|
unless @additional_responsibilities[:coordinator]
|
203
215
|
raise "The sequel persistence adapter coordinator feature used but not enabled in additional_features"
|
@@ -238,6 +250,14 @@ module Dynflow
|
|
238
250
|
envelopes: table(:envelope).all.to_a }
|
239
251
|
end
|
240
252
|
|
253
|
+
def migrate_db
|
254
|
+
::Sequel::Migrator.run(db, self.class.migrations_path, table: 'dynflow_schema_info')
|
255
|
+
end
|
256
|
+
|
257
|
+
def abort_if_pending_migrations!
|
258
|
+
::Sequel::Migrator.check_current(db, self.class.migrations_path, table: 'dynflow_schema_info')
|
259
|
+
end
|
260
|
+
|
241
261
|
private
|
242
262
|
|
243
263
|
TABLES = { execution_plan: :dynflow_execution_plans,
|
@@ -259,10 +279,6 @@ module Dynflow
|
|
259
279
|
File.expand_path('../sequel_migrations', __FILE__)
|
260
280
|
end
|
261
281
|
|
262
|
-
def migrate_db
|
263
|
-
::Sequel::Migrator.run(db, self.class.migrations_path, table: 'dynflow_schema_info')
|
264
|
-
end
|
265
|
-
|
266
282
|
def prepare_record(table_name, value, base = {}, with_data = true)
|
267
283
|
record = base.dup
|
268
284
|
if with_data && table(table_name).columns.include?(:data)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
Sequel.migration do
|
3
|
+
up do
|
4
|
+
alter_table(:dynflow_actions) do
|
5
|
+
drop_index [:execution_plan_uuid, :id]
|
6
|
+
end
|
7
|
+
|
8
|
+
alter_table(:dynflow_execution_plans) do
|
9
|
+
drop_index :uuid
|
10
|
+
end
|
11
|
+
|
12
|
+
alter_table(:dynflow_steps) do
|
13
|
+
drop_index [:execution_plan_uuid, :id]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
down do
|
18
|
+
alter_table(:dynflow_actions) do
|
19
|
+
add_index [:execution_plan_uuid, :id], :unique => true
|
20
|
+
end
|
21
|
+
|
22
|
+
alter_table(:dynflow_execution_plans) do
|
23
|
+
add_index :uuid, :unique => true
|
24
|
+
end
|
25
|
+
|
26
|
+
alter_table(:dynflow_steps) do
|
27
|
+
add_index [:execution_plan_uuid, :id], :unique => true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -100,8 +100,15 @@ module Dynflow
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def calculate_db_pool_size(world)
|
103
|
-
self.db_pool_size
|
104
|
-
|
103
|
+
return self.db_pool_size if self.db_pool_size
|
104
|
+
|
105
|
+
base_value = 5
|
106
|
+
if defined?(::Sidekiq)
|
107
|
+
Sidekiq.options[:concurrency] + base_value
|
108
|
+
else
|
109
|
+
world.config.queues.values.inject(base_value) do |pool_size, pool_options|
|
110
|
+
pool_size += pool_options[:pool_size]
|
111
|
+
end
|
105
112
|
end
|
106
113
|
end
|
107
114
|
|
@@ -185,8 +192,8 @@ module Dynflow
|
|
185
192
|
end
|
186
193
|
|
187
194
|
# Sequel adapter based on Rails app database.yml configuration
|
188
|
-
def initialize_persistence(world)
|
189
|
-
persistence_class.new(default_sequel_adapter_options(world))
|
195
|
+
def initialize_persistence(world, options = {})
|
196
|
+
persistence_class.new(default_sequel_adapter_options(world).merge(options))
|
190
197
|
end
|
191
198
|
end
|
192
199
|
end
|
data/lib/dynflow/version.rb
CHANGED
@@ -28,6 +28,8 @@ module Dynflow
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
pruned = persistence.prune_envelopes(world.id)
|
32
|
+
logger.error("Pruned #{pruned} envelopes for invalidated world #{world.id}") unless pruned.zero?
|
31
33
|
coordinator.delete_world(world)
|
32
34
|
end
|
33
35
|
end
|
@@ -115,6 +117,8 @@ module Dynflow
|
|
115
117
|
def perform_validity_checks
|
116
118
|
world_invalidation_result = worlds_validity_check
|
117
119
|
locks_validity_check
|
120
|
+
pruned = connector.prune_undeliverable_envelopes(self)
|
121
|
+
logger.error("Pruned #{pruned} undeliverable envelopes") unless pruned.zero?
|
118
122
|
world_invalidation_result.values.select { |result| result == :invalidated }.size
|
119
123
|
end
|
120
124
|
|
data/test/persistence_test.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require_relative 'test_helper'
|
3
3
|
require 'tmpdir'
|
4
|
+
require 'ostruct'
|
4
5
|
|
5
6
|
module Dynflow
|
6
7
|
module PersistenceTest
|
@@ -371,6 +372,41 @@ module Dynflow
|
|
371
372
|
assert_equal [], adapter.pull_envelopes(executor_world_id)
|
372
373
|
end
|
373
374
|
|
375
|
+
it 'supports pruning of envelopes of invalidated worlds' do
|
376
|
+
client_world_id = '5678'
|
377
|
+
executor_world_id = '1234'
|
378
|
+
envelope_hash = ->(envelope) { Dynflow::Utils.indifferent_hash(Dynflow.serializer.dump(envelope)) }
|
379
|
+
executor_envelope = envelope_hash.call(Dispatcher::Envelope['123', client_world_id, executor_world_id, Dispatcher::Execution['111']])
|
380
|
+
client_envelope = envelope_hash.call(Dispatcher::Envelope['123', executor_world_id, client_world_id, Dispatcher::Accepted])
|
381
|
+
envelopes = [client_envelope, executor_envelope]
|
382
|
+
|
383
|
+
envelopes.each { |e| adapter.push_envelope(e) }
|
384
|
+
|
385
|
+
assert_equal 1, adapter.prune_envelopes([executor_world_id])
|
386
|
+
assert_equal 0, adapter.prune_envelopes([executor_world_id])
|
387
|
+
assert_equal [], adapter.pull_envelopes(executor_world_id)
|
388
|
+
assert_equal [client_envelope], adapter.pull_envelopes(client_world_id)
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'supports pruning of orphaned envelopes' do
|
392
|
+
client_world_id = '5678'
|
393
|
+
executor_world_id = '1234'
|
394
|
+
envelope_hash = ->(envelope) { Dynflow::Utils.indifferent_hash(Dynflow.serializer.dump(envelope)) }
|
395
|
+
executor_envelope = envelope_hash.call(Dispatcher::Envelope['123', client_world_id, executor_world_id, Dispatcher::Execution['111']])
|
396
|
+
client_envelope = envelope_hash.call(Dispatcher::Envelope['123', executor_world_id, client_world_id, Dispatcher::Accepted])
|
397
|
+
envelopes = [client_envelope, executor_envelope]
|
398
|
+
|
399
|
+
envelopes.each { |e| adapter.push_envelope(e) }
|
400
|
+
adapter.insert_coordinator_record({"class"=>"Dynflow::Coordinator::ExecutorWorld",
|
401
|
+
"id" => executor_world_id, "meta" => {}, "active" => true })
|
402
|
+
|
403
|
+
assert_equal 1, adapter.prune_undeliverable_envelopes
|
404
|
+
assert_equal 0, adapter.prune_undeliverable_envelopes
|
405
|
+
assert_equal [], adapter.pull_envelopes(client_world_id)
|
406
|
+
assert_equal [executor_envelope], adapter.pull_envelopes(executor_world_id)
|
407
|
+
assert_equal [], adapter.pull_envelopes(executor_world_id)
|
408
|
+
end
|
409
|
+
|
374
410
|
it 'supports reading data saved prior to normalization' do
|
375
411
|
db = adapter.send(:db)
|
376
412
|
# Prepare records for saving
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Necas
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-
|
12
|
+
date: 2020-05-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_json
|
@@ -512,6 +512,7 @@ files:
|
|
512
512
|
- lib/dynflow/persistence_adapters/sequel_migrations/017_add_delayed_plan_frozen.rb
|
513
513
|
- lib/dynflow/persistence_adapters/sequel_migrations/018_add_uuid_column.rb
|
514
514
|
- lib/dynflow/persistence_adapters/sequel_migrations/019_update_mysql_time_precision.rb
|
515
|
+
- lib/dynflow/persistence_adapters/sequel_migrations/020_drop_duplicate_indices.rb
|
515
516
|
- lib/dynflow/rails.rb
|
516
517
|
- lib/dynflow/rails/configuration.rb
|
517
518
|
- lib/dynflow/rails/daemon.rb
|