pg_eventstore 0.10.2 → 1.0.0.rc1
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/CHANGELOG.md +6 -0
- data/README.md +26 -0
- data/db/migrations/1_create_events.sql +12 -11
- data/db/migrations/2_create_subscriptions.sql +6 -2
- data/db/migrations/3_create_subscription_commands.sql +9 -5
- data/db/migrations/4_create_subscriptions_set_commands.sql +1 -1
- data/db/migrations/5_partitions.sql +1 -0
- data/docs/how_it_works.md +14 -1
- data/lib/pg_eventstore/commands/append.rb +1 -1
- data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +30 -8
- data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +8 -10
- data/lib/pg_eventstore/commands/link_to.rb +14 -7
- data/lib/pg_eventstore/errors.rb +10 -12
- data/lib/pg_eventstore/event.rb +4 -0
- data/lib/pg_eventstore/queries/event_queries.rb +27 -6
- data/lib/pg_eventstore/queries/links_resolver.rb +28 -6
- data/lib/pg_eventstore/queries/partition_queries.rb +8 -0
- data/lib/pg_eventstore/queries/subscription_command_queries.rb +27 -7
- data/lib/pg_eventstore/queries/subscription_queries.rb +58 -28
- data/lib/pg_eventstore/queries/subscriptions_set_command_queries.rb +13 -1
- data/lib/pg_eventstore/queries/subscriptions_set_queries.rb +18 -4
- data/lib/pg_eventstore/query_builders/events_filtering_query.rb +4 -4
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +10 -2
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +9 -7
- data/lib/pg_eventstore/subscriptions/commands_handler.rb +3 -2
- data/lib/pg_eventstore/subscriptions/subscription.rb +28 -12
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +19 -15
- data/lib/pg_eventstore/subscriptions/subscription_runner.rb +1 -1
- data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +1 -1
- data/lib/pg_eventstore/subscriptions/subscriptions_set.rb +22 -1
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +180 -0
- data/lib/pg_eventstore/web/paginator/base_collection.rb +56 -0
- data/lib/pg_eventstore/web/paginator/event_types_collection.rb +50 -0
- data/lib/pg_eventstore/web/paginator/events_collection.rb +105 -0
- data/lib/pg_eventstore/web/paginator/helpers.rb +119 -0
- data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +48 -0
- data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +50 -0
- data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +51 -0
- data/lib/pg_eventstore/web/public/fonts/vendor/FontAwesome.otf +0 -0
- data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.eot +0 -0
- data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.svg +685 -0
- data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.ttf +0 -0
- data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.woff +0 -0
- data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.woff2 +0 -0
- data/lib/pg_eventstore/web/public/images/favicon.ico +0 -0
- data/lib/pg_eventstore/web/public/javascripts/gentelella.js +334 -0
- data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +162 -0
- data/lib/pg_eventstore/web/public/javascripts/vendor/bootstrap.bundle.min.js +7 -0
- data/lib/pg_eventstore/web/public/javascripts/vendor/bootstrap.bundle.min.js.map +1 -0
- data/lib/pg_eventstore/web/public/javascripts/vendor/jquery.autocomplete.min.js +8 -0
- data/lib/pg_eventstore/web/public/javascripts/vendor/jquery.min.js +4 -0
- data/lib/pg_eventstore/web/public/javascripts/vendor/jquery.min.js.map +1 -0
- data/lib/pg_eventstore/web/public/javascripts/vendor/select2.full.min.js +2 -0
- data/lib/pg_eventstore/web/public/stylesheets/pg_eventstore.css +5 -0
- data/lib/pg_eventstore/web/public/stylesheets/vendor/bootstrap.min.css +7 -0
- data/lib/pg_eventstore/web/public/stylesheets/vendor/bootstrap.min.css.map +1 -0
- data/lib/pg_eventstore/web/public/stylesheets/vendor/font-awesome.min.css +4 -0
- data/lib/pg_eventstore/web/public/stylesheets/vendor/font-awesome.min.css.map +7 -0
- data/lib/pg_eventstore/web/public/stylesheets/vendor/gentelella.min.css +13 -0
- data/lib/pg_eventstore/web/public/stylesheets/vendor/select2-bootstrap4.min.css +3 -0
- data/lib/pg_eventstore/web/public/stylesheets/vendor/select2.min.css +2 -0
- data/lib/pg_eventstore/web/subscriptions/helpers.rb +76 -0
- data/lib/pg_eventstore/web/subscriptions/set_collection.rb +34 -0
- data/lib/pg_eventstore/web/subscriptions/subscriptions.rb +33 -0
- data/lib/pg_eventstore/web/subscriptions/subscriptions_set.rb +33 -0
- data/lib/pg_eventstore/web/subscriptions/subscriptions_to_set_association.rb +32 -0
- data/lib/pg_eventstore/web/views/home/dashboard.erb +147 -0
- data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +15 -0
- data/lib/pg_eventstore/web/views/home/partials/events.erb +22 -0
- data/lib/pg_eventstore/web/views/home/partials/pagination_links.erb +3 -0
- data/lib/pg_eventstore/web/views/home/partials/stream_filter.erb +31 -0
- data/lib/pg_eventstore/web/views/layouts/application.erb +116 -0
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +220 -0
- data/lib/pg_eventstore/web.rb +22 -0
- data/lib/pg_eventstore.rb +5 -0
- data/pg_eventstore.gemspec +2 -1
- metadata +60 -2
|
@@ -22,11 +22,7 @@ module PgEventstore
|
|
|
22
22
|
# @param attrs [Hash]
|
|
23
23
|
# @return [Hash, nil]
|
|
24
24
|
def find_by(attrs)
|
|
25
|
-
builder =
|
|
26
|
-
attrs.each do |attr, val|
|
|
27
|
-
builder.where("#{attr} = ?", val)
|
|
28
|
-
end
|
|
29
|
-
|
|
25
|
+
builder = find_by_attrs_builder(attrs).limit(1)
|
|
30
26
|
pg_result = connection.with do |conn|
|
|
31
27
|
conn.exec_params(*builder.to_exec_params)
|
|
32
28
|
end
|
|
@@ -35,6 +31,25 @@ module PgEventstore
|
|
|
35
31
|
deserialize(pg_result.to_a.first)
|
|
36
32
|
end
|
|
37
33
|
|
|
34
|
+
# @param attrs [Hash]
|
|
35
|
+
# @return [Array<Hash>]
|
|
36
|
+
def find_all(attrs)
|
|
37
|
+
builder = find_by_attrs_builder(attrs)
|
|
38
|
+
pg_result = connection.with do |conn|
|
|
39
|
+
conn.exec_params(*builder.to_exec_params)
|
|
40
|
+
end
|
|
41
|
+
return [] if pg_result.ntuples.zero?
|
|
42
|
+
|
|
43
|
+
pg_result.map(&method(:deserialize))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [Array<String>]
|
|
47
|
+
def set_collection
|
|
48
|
+
connection.with do |conn|
|
|
49
|
+
conn.exec('SELECT set FROM subscriptions GROUP BY set ORDER BY set ASC')
|
|
50
|
+
end.map { |attrs| attrs['set'] }
|
|
51
|
+
end
|
|
52
|
+
|
|
38
53
|
# @param id [Integer]
|
|
39
54
|
# @return [Hash]
|
|
40
55
|
# @raise [PgEventstore::RecordNotFound]
|
|
@@ -58,19 +73,32 @@ module PgEventstore
|
|
|
58
73
|
|
|
59
74
|
# @param id [Integer]
|
|
60
75
|
# @param attrs [Hash]
|
|
61
|
-
|
|
76
|
+
# @param locked_by [Integer]
|
|
77
|
+
# @return [Hash]
|
|
78
|
+
# @raise [PgEventstore::RecordNotFound]
|
|
79
|
+
# @raise [PgEventstore::WrongLockIdError]
|
|
80
|
+
def update(id, attrs:, locked_by:)
|
|
62
81
|
attrs = { updated_at: Time.now.utc }.merge(attrs)
|
|
63
82
|
attrs_sql = attrs.keys.map.with_index(1) do |attr, index|
|
|
64
83
|
"#{attr} = $#{index}"
|
|
65
84
|
end.join(', ')
|
|
66
85
|
sql =
|
|
67
86
|
"UPDATE subscriptions SET #{attrs_sql} WHERE id = $#{attrs.keys.size + 1} RETURNING *"
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
updated_attrs = transaction_queries.transaction do
|
|
88
|
+
pg_result = connection.with do |conn|
|
|
89
|
+
conn.exec_params(sql, [*attrs.values, id])
|
|
90
|
+
end
|
|
91
|
+
raise(RecordNotFound.new("subscriptions", id)) if pg_result.ntuples.zero?
|
|
92
|
+
|
|
93
|
+
updated_attrs = pg_result.to_a.first
|
|
94
|
+
unless updated_attrs['locked_by'] == locked_by
|
|
95
|
+
# Subscription is force-locked by someone else. We have to roll back such transaction
|
|
96
|
+
raise(WrongLockIdError.new(updated_attrs['set'], updated_attrs['name'], updated_attrs['locked_by']))
|
|
97
|
+
end
|
|
98
|
+
updated_attrs
|
|
70
99
|
end
|
|
71
|
-
raise(RecordNotFound.new("subscriptions", id)) if pg_result.ntuples.zero?
|
|
72
100
|
|
|
73
|
-
deserialize(
|
|
101
|
+
deserialize(updated_attrs)
|
|
74
102
|
end
|
|
75
103
|
|
|
76
104
|
# @param query_options [Hash{Integer => Hash}] runner_id/query options association
|
|
@@ -90,13 +118,16 @@ module PgEventstore
|
|
|
90
118
|
end
|
|
91
119
|
|
|
92
120
|
# @param id [Integer] subscription's id
|
|
93
|
-
# @param lock_id [
|
|
121
|
+
# @param lock_id [Integer] id of the subscriptions set which reserves the subscription
|
|
94
122
|
# @param force [Boolean] whether to lock the subscription despite on #locked_by value
|
|
95
|
-
# @return [
|
|
123
|
+
# @return [Integer] lock id
|
|
96
124
|
# @raise [SubscriptionAlreadyLockedError] in case the Subscription is already locked
|
|
97
|
-
def lock!(id, lock_id, force
|
|
125
|
+
def lock!(id, lock_id, force: false)
|
|
98
126
|
transaction_queries.transaction do
|
|
99
127
|
attrs = find!(id)
|
|
128
|
+
# We don't care who locked the Subscription - whether it is the same SubscriptionsSet or not - multiple locks
|
|
129
|
+
# must not happen even with the same SubscriptionsSet. We later assume this to reset Subscription's stats, for
|
|
130
|
+
# example.
|
|
100
131
|
if attrs[:locked_by] && !force
|
|
101
132
|
raise SubscriptionAlreadyLockedError.new(attrs[:set], attrs[:name], attrs[:locked_by])
|
|
102
133
|
end
|
|
@@ -107,22 +138,11 @@ module PgEventstore
|
|
|
107
138
|
lock_id
|
|
108
139
|
end
|
|
109
140
|
|
|
110
|
-
# @param id [Integer]
|
|
111
|
-
# @param lock_id [String] UUIDv4 id of the set which reserved the subscription after itself
|
|
141
|
+
# @param id [Integer]
|
|
112
142
|
# @return [void]
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
transaction_queries.transaction do
|
|
117
|
-
attrs = find!(id)
|
|
118
|
-
# Normally this should never happen as locking/unlocking happens within the same process. This is done only for
|
|
119
|
-
# the matter of consistency.
|
|
120
|
-
unless attrs[:locked_by] == lock_id
|
|
121
|
-
raise SubscriptionUnlockError.new(attrs[:set], attrs[:name], lock_id, attrs[:locked_by])
|
|
122
|
-
end
|
|
123
|
-
connection.with do |conn|
|
|
124
|
-
conn.exec_params('UPDATE subscriptions SET locked_by = $1 WHERE id = $2', [nil, id])
|
|
125
|
-
end
|
|
143
|
+
def delete(id)
|
|
144
|
+
connection.with do |conn|
|
|
145
|
+
conn.exec_params('DELETE FROM subscriptions WHERE id = $1', [id])
|
|
126
146
|
end
|
|
127
147
|
end
|
|
128
148
|
|
|
@@ -159,5 +179,15 @@ module PgEventstore
|
|
|
159
179
|
def deserialize(hash)
|
|
160
180
|
hash.transform_keys(&:to_sym)
|
|
161
181
|
end
|
|
182
|
+
|
|
183
|
+
# @param attrs [Hash]
|
|
184
|
+
# @return [PgEventstore::SQLBuilder]
|
|
185
|
+
def find_by_attrs_builder(attrs)
|
|
186
|
+
builder = SQLBuilder.new.select('*').from('subscriptions').order('id ASC')
|
|
187
|
+
attrs.each do |attr, val|
|
|
188
|
+
builder.where("#{attr} = ?", val)
|
|
189
|
+
end
|
|
190
|
+
builder
|
|
191
|
+
end
|
|
162
192
|
end
|
|
163
193
|
end
|
|
@@ -11,6 +11,13 @@ module PgEventstore
|
|
|
11
11
|
@connection = connection
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# @return [Hash]
|
|
15
|
+
def find_or_create_by(...)
|
|
16
|
+
transaction_queries.transaction do
|
|
17
|
+
find_by(...) || create(...)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
14
21
|
# @param subscriptions_set_id [Integer]
|
|
15
22
|
# @param command_name [String]
|
|
16
23
|
# @return [Hash, nil]
|
|
@@ -31,7 +38,7 @@ module PgEventstore
|
|
|
31
38
|
# @param subscriptions_set_id [Integer]
|
|
32
39
|
# @param command_name [String]
|
|
33
40
|
# @return [Hash]
|
|
34
|
-
def
|
|
41
|
+
def create(subscriptions_set_id:, command_name:)
|
|
35
42
|
sql = <<~SQL
|
|
36
43
|
INSERT INTO subscriptions_set_commands (name, subscriptions_set_id)
|
|
37
44
|
VALUES ($1, $2)
|
|
@@ -72,5 +79,10 @@ module PgEventstore
|
|
|
72
79
|
def deserialize(hash)
|
|
73
80
|
hash.transform_keys(&:to_sym)
|
|
74
81
|
end
|
|
82
|
+
|
|
83
|
+
# @return [PgEventstore::TransactionQueries]
|
|
84
|
+
def transaction_queries
|
|
85
|
+
TransactionQueries.new(connection)
|
|
86
|
+
end
|
|
75
87
|
end
|
|
76
88
|
end
|
|
@@ -14,7 +14,7 @@ module PgEventstore
|
|
|
14
14
|
# @param attrs [Hash]
|
|
15
15
|
# @return [Array<Hash>]
|
|
16
16
|
def find_all(attrs)
|
|
17
|
-
builder = SQLBuilder.new.select('*').from('subscriptions_set')
|
|
17
|
+
builder = SQLBuilder.new.select('*').from('subscriptions_set').order('name ASC, id ASC')
|
|
18
18
|
attrs.each do |attr, val|
|
|
19
19
|
builder.where("#{attr} = ?", val)
|
|
20
20
|
end
|
|
@@ -25,13 +25,22 @@ module PgEventstore
|
|
|
25
25
|
pg_result.to_a.map(&method(:deserialize))
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# @return [Array<String>]
|
|
29
|
+
def set_names
|
|
30
|
+
builder = SQLBuilder.new.select('name').from('subscriptions_set').group('name').order('name ASC')
|
|
31
|
+
|
|
32
|
+
connection.with do |conn|
|
|
33
|
+
conn.exec_params(*builder.to_exec_params)
|
|
34
|
+
end.map { |attrs| attrs['name'] }
|
|
35
|
+
end
|
|
36
|
+
|
|
28
37
|
# The same as #find_all, but returns first result
|
|
29
38
|
# @return [Hash, nil]
|
|
30
39
|
def find_by(...)
|
|
31
40
|
find_all(...).first
|
|
32
41
|
end
|
|
33
42
|
|
|
34
|
-
# @param id [
|
|
43
|
+
# @param id [Integer]
|
|
35
44
|
# @return [Hash]
|
|
36
45
|
# @raise [PgEventstore::RecordNotFound]
|
|
37
46
|
def find!(id)
|
|
@@ -52,7 +61,7 @@ module PgEventstore
|
|
|
52
61
|
deserialize(pg_result.to_a.first)
|
|
53
62
|
end
|
|
54
63
|
|
|
55
|
-
# @param id [
|
|
64
|
+
# @param id [Integer]
|
|
56
65
|
# @param attrs [Hash]
|
|
57
66
|
def update(id, attrs)
|
|
58
67
|
attrs = { updated_at: Time.now.utc }.merge(attrs)
|
|
@@ -70,7 +79,7 @@ module PgEventstore
|
|
|
70
79
|
deserialize(pg_result.to_a.first)
|
|
71
80
|
end
|
|
72
81
|
|
|
73
|
-
# @
|
|
82
|
+
# @param id [Integer]
|
|
74
83
|
# @return [void]
|
|
75
84
|
def delete(id)
|
|
76
85
|
connection.with do |conn|
|
|
@@ -85,5 +94,10 @@ module PgEventstore
|
|
|
85
94
|
def deserialize(hash)
|
|
86
95
|
hash.transform_keys(&:to_sym)
|
|
87
96
|
end
|
|
97
|
+
|
|
98
|
+
# @return [PgEventstore::TransactionQueries]
|
|
99
|
+
def transaction_queries
|
|
100
|
+
TransactionQueries.new(connection)
|
|
101
|
+
end
|
|
88
102
|
end
|
|
89
103
|
end
|
|
@@ -28,8 +28,8 @@ module PgEventstore
|
|
|
28
28
|
# @return [PgEventstore::QueryBuilders::EventsFiltering]
|
|
29
29
|
def all_stream_filtering(options)
|
|
30
30
|
event_filter = new
|
|
31
|
-
options in { filter: { event_types: Array =>
|
|
32
|
-
event_filter.add_event_types(
|
|
31
|
+
options in { filter: { event_types: Array => event_types } }
|
|
32
|
+
event_filter.add_event_types(event_types)
|
|
33
33
|
event_filter.add_limit(options[:max_count])
|
|
34
34
|
options in { filter: { streams: Array => streams } }
|
|
35
35
|
streams&.each { |attrs| event_filter.add_stream_attrs(**attrs) }
|
|
@@ -43,8 +43,8 @@ module PgEventstore
|
|
|
43
43
|
# @return [PgEventstore::QueryBuilders::EventsFiltering]
|
|
44
44
|
def specific_stream_filtering(stream, options)
|
|
45
45
|
event_filter = new
|
|
46
|
-
options in { filter: { event_types: Array =>
|
|
47
|
-
event_filter.add_event_types(
|
|
46
|
+
options in { filter: { event_types: Array => event_types } }
|
|
47
|
+
event_filter.add_event_types(event_types)
|
|
48
48
|
event_filter.add_limit(options[:max_count])
|
|
49
49
|
event_filter.add_stream_attrs(**stream.to_hash)
|
|
50
50
|
event_filter.add_revision(options[:from_revision], options[:direction])
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module PgEventstore
|
|
4
4
|
module CommandHandlers
|
|
5
5
|
class SubscriptionFeederCommands
|
|
6
|
-
AVAILABLE_COMMANDS = %
|
|
6
|
+
AVAILABLE_COMMANDS = %i[StopAll StartAll Restore Stop].to_h { [_1, _1.to_s] }.freeze
|
|
7
7
|
|
|
8
8
|
# @param config_name [Symbol]
|
|
9
9
|
# @param subscription_feeder [PgEventstore::SubscriptionFeeder]
|
|
@@ -16,7 +16,7 @@ module PgEventstore
|
|
|
16
16
|
# @return [void]
|
|
17
17
|
def process
|
|
18
18
|
queries.find_commands(@subscription_feeder.id).each do |command|
|
|
19
|
-
unless AVAILABLE_COMMANDS.include?(command[:name])
|
|
19
|
+
unless AVAILABLE_COMMANDS.values.include?(command[:name])
|
|
20
20
|
PgEventstore.logger&.warn(
|
|
21
21
|
"#{self.class.name}: Don't know how to handle #{command[:name].inspect}. Details: #{command.inspect}."
|
|
22
22
|
)
|
|
@@ -47,6 +47,14 @@ module PgEventstore
|
|
|
47
47
|
def start_all
|
|
48
48
|
@subscription_feeder.start_all
|
|
49
49
|
end
|
|
50
|
+
|
|
51
|
+
def restore
|
|
52
|
+
@subscription_feeder.restore
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def stop
|
|
56
|
+
@subscription_feeder.stop
|
|
57
|
+
end
|
|
50
58
|
end
|
|
51
59
|
end
|
|
52
60
|
end
|
|
@@ -3,20 +3,22 @@
|
|
|
3
3
|
module PgEventstore
|
|
4
4
|
module CommandHandlers
|
|
5
5
|
class SubscriptionRunnersCommands
|
|
6
|
-
AVAILABLE_COMMANDS = %
|
|
6
|
+
AVAILABLE_COMMANDS = %i[Start Stop Restore].to_h { [_1, _1.to_s] }.freeze
|
|
7
7
|
|
|
8
8
|
# @param config_name [Symbol]
|
|
9
9
|
# @param runners [Array<PgEventstore::SubscriptionRunner>]
|
|
10
|
-
|
|
10
|
+
# @param subscriptions_set_id [Integer]
|
|
11
|
+
def initialize(config_name, runners, subscriptions_set_id)
|
|
11
12
|
@config_name = config_name
|
|
12
13
|
@runners = runners
|
|
14
|
+
@subscriptions_set_id = subscriptions_set_id
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
# Look up commands for all given SubscriptionRunner-s and execute them
|
|
16
18
|
# @return [void]
|
|
17
19
|
def process
|
|
18
|
-
queries.find_commands(@runners.map(&:id)).each do |command|
|
|
19
|
-
unless AVAILABLE_COMMANDS.include?(command[:name])
|
|
20
|
+
queries.find_commands(@runners.map(&:id), subscriptions_set_id: @subscriptions_set_id).each do |command|
|
|
21
|
+
unless AVAILABLE_COMMANDS.values.include?(command[:name])
|
|
20
22
|
PgEventstore.logger&.warn(
|
|
21
23
|
"#{self.class.name}: Don't know how to handle #{command[:name].inspect}. Details: #{command.inspect}."
|
|
22
24
|
)
|
|
@@ -48,19 +50,19 @@ module PgEventstore
|
|
|
48
50
|
|
|
49
51
|
# @param subscription_id [Integer]
|
|
50
52
|
# @return [void]
|
|
51
|
-
def
|
|
53
|
+
def start(subscription_id)
|
|
52
54
|
find_subscription_runner(subscription_id)&.start
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
# @param subscription_id [Integer]
|
|
56
58
|
# @return [void]
|
|
57
|
-
def
|
|
59
|
+
def restore(subscription_id)
|
|
58
60
|
find_subscription_runner(subscription_id)&.restore
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
# @param subscription_id [Integer]
|
|
62
64
|
# @return [void]
|
|
63
|
-
def
|
|
65
|
+
def stop(subscription_id)
|
|
64
66
|
find_subscription_runner(subscription_id)&.stop_async
|
|
65
67
|
end
|
|
66
68
|
end
|
|
@@ -11,6 +11,7 @@ module PgEventstore
|
|
|
11
11
|
extend Forwardable
|
|
12
12
|
|
|
13
13
|
RESTART_DELAY = 5 # seconds
|
|
14
|
+
PULL_INTERVAL = 1
|
|
14
15
|
|
|
15
16
|
def_delegators :@basic_runner, :start, :stop, :state, :stop_async, :wait_for_finish
|
|
16
17
|
|
|
@@ -21,7 +22,7 @@ module PgEventstore
|
|
|
21
22
|
@config_name = config_name
|
|
22
23
|
@subscription_feeder = subscription_feeder
|
|
23
24
|
@runners = runners
|
|
24
|
-
@basic_runner = BasicRunner.new(
|
|
25
|
+
@basic_runner = BasicRunner.new(PULL_INTERVAL, 0)
|
|
25
26
|
attach_runner_callbacks
|
|
26
27
|
end
|
|
27
28
|
|
|
@@ -56,7 +57,7 @@ module PgEventstore
|
|
|
56
57
|
|
|
57
58
|
# @return [PgEventstore::CommandHandlers::SubscriptionRunnersCommands]
|
|
58
59
|
def subscription_runners_commands
|
|
59
|
-
CommandHandlers::SubscriptionRunnersCommands.new(@config_name, @runners)
|
|
60
|
+
CommandHandlers::SubscriptionRunnersCommands.new(@config_name, @runners, @subscription_feeder.id)
|
|
60
61
|
end
|
|
61
62
|
end
|
|
62
63
|
end
|
|
@@ -63,8 +63,8 @@ module PgEventstore
|
|
|
63
63
|
# processor
|
|
64
64
|
attribute(:last_chunk_greatest_position)
|
|
65
65
|
# @!attribute locked_by
|
|
66
|
-
# @return [
|
|
67
|
-
#
|
|
66
|
+
# @return [Integer, nil] The id of subscription manager which obtained the lock of the Subscription. _nil_ value
|
|
67
|
+
# means that the Subscription isn't locked yet by any subscription manager.
|
|
68
68
|
attribute(:locked_by)
|
|
69
69
|
# @!attribute created_at
|
|
70
70
|
# @return [Time]
|
|
@@ -80,7 +80,7 @@ module PgEventstore
|
|
|
80
80
|
# @param attrs [Hash]
|
|
81
81
|
# @return [Hash]
|
|
82
82
|
def update(attrs)
|
|
83
|
-
assign_attributes(subscription_queries.update(id, attrs))
|
|
83
|
+
assign_attributes(subscription_queries.update(id, attrs: attrs, locked_by: locked_by))
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# @param attrs [Hash]
|
|
@@ -93,20 +93,13 @@ module PgEventstore
|
|
|
93
93
|
|
|
94
94
|
# Locks the Subscription by the given lock id
|
|
95
95
|
# @return [PgEventstore::Subscription]
|
|
96
|
-
def lock!(lock_id, force
|
|
96
|
+
def lock!(lock_id, force: false)
|
|
97
97
|
self.id = subscription_queries.find_or_create_by(set: set, name: name)[:id]
|
|
98
|
-
self.locked_by = subscription_queries.lock!(id, lock_id, force)
|
|
98
|
+
self.locked_by = subscription_queries.lock!(id, lock_id, force: force)
|
|
99
99
|
reset_runtime_attributes
|
|
100
100
|
self
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
# Unlocks the Subscription.
|
|
104
|
-
# @return [void]
|
|
105
|
-
def unlock!
|
|
106
|
-
subscription_queries.unlock!(id, locked_by)
|
|
107
|
-
self.locked_by = nil
|
|
108
|
-
end
|
|
109
|
-
|
|
110
103
|
# Dup the current object without assigned connection
|
|
111
104
|
# @return [PgEventstore::Subscription]
|
|
112
105
|
def dup
|
|
@@ -119,6 +112,27 @@ module PgEventstore
|
|
|
119
112
|
self
|
|
120
113
|
end
|
|
121
114
|
|
|
115
|
+
# @return [Integer]
|
|
116
|
+
def hash
|
|
117
|
+
id.hash
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @param another [Object]
|
|
121
|
+
# @return [Boolean]
|
|
122
|
+
def eql?(another)
|
|
123
|
+
return false unless another.is_a?(Subscription)
|
|
124
|
+
|
|
125
|
+
hash == another.hash
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @param another [PgEventstore::SubscriptionsSet]
|
|
129
|
+
# @return [Boolean]
|
|
130
|
+
def ==(another)
|
|
131
|
+
return false unless another.is_a?(Subscription)
|
|
132
|
+
|
|
133
|
+
id == another.id
|
|
134
|
+
end
|
|
135
|
+
|
|
122
136
|
private
|
|
123
137
|
|
|
124
138
|
def reset_runtime_attributes
|
|
@@ -130,6 +144,8 @@ module PgEventstore
|
|
|
130
144
|
chunk_query_interval: chunk_query_interval,
|
|
131
145
|
last_chunk_fed_at: Time.at(0).utc,
|
|
132
146
|
last_chunk_greatest_position: nil,
|
|
147
|
+
last_error: nil,
|
|
148
|
+
last_error_occurred_at: nil,
|
|
133
149
|
time_between_restarts: time_between_restarts,
|
|
134
150
|
state: RunnerState::STATES[:initial]
|
|
135
151
|
)
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
module PgEventstore
|
|
4
4
|
# This class is responsible for starting/stopping all SubscriptionRunners. The background runner of it is responsible
|
|
5
5
|
# for events pulling and feeding those SubscriptionRunners.
|
|
6
|
-
# @!visibility private
|
|
7
6
|
class SubscriptionFeeder
|
|
8
7
|
extend Forwardable
|
|
9
8
|
|
|
9
|
+
HEARTBEAT_INTERVAL = 10 # seconds
|
|
10
|
+
|
|
10
11
|
def_delegators :subscriptions_set, :id
|
|
11
12
|
def_delegators :@basic_runner, :start, :stop, :restore, :state, :wait_for_finish, :stop_async
|
|
12
13
|
|
|
@@ -17,12 +18,13 @@ module PgEventstore
|
|
|
17
18
|
def initialize(config_name:, set_name:, max_retries:, retries_interval:)
|
|
18
19
|
@config_name = config_name
|
|
19
20
|
@runners = []
|
|
20
|
-
@
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
@subscriptions_set_attrs = {
|
|
22
|
+
name: set_name, max_restarts_number: max_retries, time_between_restarts: retries_interval
|
|
23
|
+
}
|
|
23
24
|
@commands_handler = CommandsHandler.new(@config_name, self, @runners)
|
|
24
25
|
@basic_runner = BasicRunner.new(0.2, 0)
|
|
25
26
|
@force_lock = false
|
|
27
|
+
@refreshed_at = Time.at(0)
|
|
26
28
|
attach_runner_callbacks
|
|
27
29
|
end
|
|
28
30
|
|
|
@@ -77,18 +79,12 @@ module PgEventstore
|
|
|
77
79
|
# Locks all Subscriptions behind the current SubscriptionsSet
|
|
78
80
|
# @return [void]
|
|
79
81
|
def lock_all
|
|
80
|
-
@runners.each { |runner| runner.lock!(subscriptions_set.id, @force_lock) }
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# @return [void]
|
|
84
|
-
def unlock_all
|
|
85
|
-
@runners.each(&:unlock!)
|
|
82
|
+
@runners.each { |runner| runner.lock!(subscriptions_set.id, force: @force_lock) }
|
|
86
83
|
end
|
|
87
84
|
|
|
88
85
|
# @return [PgEventstore::SubscriptionsSet]
|
|
89
86
|
def subscriptions_set
|
|
90
|
-
@subscriptions_set ||= SubscriptionsSet.using_connection(@config_name).
|
|
91
|
-
create(name: @set_name, max_restarts_number: @max_retries, time_between_restarts: @retries_interval)
|
|
87
|
+
@subscriptions_set ||= SubscriptionsSet.using_connection(@config_name).create(**@subscriptions_set_attrs)
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
# @return [PgEventstore::SubscriptionRunnersFeeder]
|
|
@@ -102,6 +98,7 @@ module PgEventstore
|
|
|
102
98
|
@basic_runner.define_callback(:before_runner_started, :before, method(:before_runner_started))
|
|
103
99
|
@basic_runner.define_callback(:after_runner_died, :before, method(:after_runner_died))
|
|
104
100
|
@basic_runner.define_callback(:after_runner_died, :after, method(:restart_runner))
|
|
101
|
+
@basic_runner.define_callback(:process_async, :before, method(:ping_subscriptions_set))
|
|
105
102
|
@basic_runner.define_callback(:process_async, :before, method(:process_async))
|
|
106
103
|
@basic_runner.define_callback(:after_runner_stopped, :before, method(:after_runner_stopped))
|
|
107
104
|
@basic_runner.define_callback(:before_runner_restored, :after, method(:update_runner_restarts))
|
|
@@ -141,13 +138,20 @@ module PgEventstore
|
|
|
141
138
|
feeder.feed(@runners)
|
|
142
139
|
end
|
|
143
140
|
|
|
141
|
+
# @return [void]
|
|
142
|
+
def ping_subscriptions_set
|
|
143
|
+
return unless subscriptions_set.updated_at > Time.now.utc - HEARTBEAT_INTERVAL
|
|
144
|
+
|
|
145
|
+
subscriptions_set.update(updated_at: Time.now.utc)
|
|
146
|
+
@refreshed_at = Time.now.utc
|
|
147
|
+
end
|
|
148
|
+
|
|
144
149
|
# @return [void]
|
|
145
150
|
def after_runner_stopped
|
|
146
|
-
@
|
|
151
|
+
@runners.each(&:stop_async).each(&:wait_for_finish)
|
|
147
152
|
@subscriptions_set&.delete
|
|
148
153
|
@subscriptions_set = nil
|
|
149
|
-
@
|
|
150
|
-
unlock_all
|
|
154
|
+
@commands_handler.stop
|
|
151
155
|
end
|
|
152
156
|
|
|
153
157
|
# @return [void]
|
|
@@ -16,7 +16,7 @@ module PgEventstore
|
|
|
16
16
|
attr_reader :subscription
|
|
17
17
|
|
|
18
18
|
def_delegators :@events_processor, :start, :stop, :stop_async, :feed, :wait_for_finish, :restore, :state, :running?
|
|
19
|
-
def_delegators :@subscription, :lock!, :
|
|
19
|
+
def_delegators :@subscription, :lock!, :id
|
|
20
20
|
|
|
21
21
|
# @param stats [PgEventstore::SubscriptionHandlerPerformance]
|
|
22
22
|
# @param events_processor [PgEventstore::EventsProcessor]
|
|
@@ -18,7 +18,7 @@ module PgEventstore
|
|
|
18
18
|
runners_query_options = runners.to_h { |runner| [runner.id, runner.next_chunk_query_opts] }
|
|
19
19
|
grouped_events = subscription_queries.subscriptions_events(runners_query_options)
|
|
20
20
|
runners.each do |runner|
|
|
21
|
-
runner.feed(grouped_events[runner.id]
|
|
21
|
+
runner.feed(grouped_events[runner.id] || [])
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -22,7 +22,7 @@ module PgEventstore
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# @!attribute id
|
|
25
|
-
# @return [
|
|
25
|
+
# @return [Integer] It is used to lock the Subscription by updating Subscription#locked_by attribute
|
|
26
26
|
attribute(:id)
|
|
27
27
|
# @!attribute name
|
|
28
28
|
# @return [String] name of the set
|
|
@@ -87,6 +87,27 @@ module PgEventstore
|
|
|
87
87
|
self
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
# @return [Integer]
|
|
91
|
+
def hash
|
|
92
|
+
id.hash
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @param another [Object]
|
|
96
|
+
# @return [Boolean]
|
|
97
|
+
def eql?(another)
|
|
98
|
+
return false unless another.is_a?(SubscriptionsSet)
|
|
99
|
+
|
|
100
|
+
hash == another.hash
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @param another [PgEventstore::SubscriptionsSet]
|
|
104
|
+
# @return [Boolean]
|
|
105
|
+
def ==(another)
|
|
106
|
+
return false unless another.is_a?(SubscriptionsSet)
|
|
107
|
+
|
|
108
|
+
id == another.id
|
|
109
|
+
end
|
|
110
|
+
|
|
90
111
|
private
|
|
91
112
|
|
|
92
113
|
# @return [PgEventstore::SubscriptionsSetQueries]
|