pg_eventstore 1.13.1 → 1.13.2
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/.rubocop.yml +126 -0
- data/CHANGELOG.md +4 -0
- data/exe/pg-eventstore +6 -6
- data/lib/pg_eventstore/abstract_command.rb +1 -1
- data/lib/pg_eventstore/callbacks.rb +7 -2
- data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +1 -0
- data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +5 -4
- data/lib/pg_eventstore/cli/parser_options/metadata.rb +4 -4
- data/lib/pg_eventstore/cli/parsers/default_parser.rb +8 -8
- data/lib/pg_eventstore/cli/parsers/subscription_parser.rb +2 -2
- data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +4 -1
- data/lib/pg_eventstore/cli.rb +4 -4
- data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +2 -2
- data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +1 -0
- data/lib/pg_eventstore/commands/link_to.rb +4 -5
- data/lib/pg_eventstore/commands/multiple.rb +1 -3
- data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +1 -1
- data/lib/pg_eventstore/config.rb +1 -1
- data/lib/pg_eventstore/connection.rb +4 -1
- data/lib/pg_eventstore/errors.rb +3 -1
- data/lib/pg_eventstore/extensions/callbacks_extension.rb +2 -0
- data/lib/pg_eventstore/extensions/options_extension.rb +10 -10
- data/lib/pg_eventstore/extensions/using_connection_extension.rb +1 -1
- data/lib/pg_eventstore/pg_connection.rb +7 -5
- data/lib/pg_eventstore/queries/event_queries.rb +11 -10
- data/lib/pg_eventstore/queries/partition_queries.rb +4 -4
- data/lib/pg_eventstore/queries/transaction_queries.rb +5 -9
- data/lib/pg_eventstore/query_builders/events_filtering.rb +13 -13
- data/lib/pg_eventstore/query_builders/partitions_filtering.rb +6 -8
- data/lib/pg_eventstore/rspec/has_option_matcher.rb +12 -13
- data/lib/pg_eventstore/rspec/test_helpers.rb +4 -3
- data/lib/pg_eventstore/sql_builder.rb +9 -13
- data/lib/pg_eventstore/stream.rb +8 -8
- data/lib/pg_eventstore/subscriptions/basic_runner.rb +4 -3
- data/lib/pg_eventstore/subscriptions/commands_handler.rb +1 -1
- data/lib/pg_eventstore/subscriptions/events_processor.rb +1 -1
- data/lib/pg_eventstore/subscriptions/extensions/base_command_extension.rb +10 -8
- data/lib/pg_eventstore/subscriptions/queries/subscription_command_queries.rb +23 -21
- data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +11 -8
- data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +8 -13
- data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_queries.rb +7 -6
- data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_connection.rb +3 -1
- data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_runner.rb +1 -0
- data/lib/pg_eventstore/subscriptions/runner_state.rb +3 -1
- data/lib/pg_eventstore/subscriptions/subscription.rb +11 -10
- data/lib/pg_eventstore/subscriptions/subscription_handler_performance.rb +1 -1
- data/lib/pg_eventstore/subscriptions/subscription_runner.rb +1 -1
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +5 -7
- data/lib/pg_eventstore/subscriptions/subscriptions_set.rb +8 -8
- data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +2 -0
- data/lib/pg_eventstore/tasks/setup.rake +7 -6
- data/lib/pg_eventstore/utils.rb +8 -4
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +22 -15
- data/lib/pg_eventstore/web/paginator/event_types_collection.rb +9 -11
- data/lib/pg_eventstore/web/paginator/events_collection.rb +6 -7
- data/lib/pg_eventstore/web/paginator/helpers.rb +19 -19
- data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +9 -9
- data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +9 -10
- data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +9 -11
- data/lib/pg_eventstore/web/subscriptions/set_collection.rb +1 -1
- data/lib/pg_eventstore/web/subscriptions/subscriptions_to_set_association.rb +2 -0
- data/lib/pg_eventstore/web/subscriptions/with_state/set_collection.rb +1 -1
- data/lib/pg_eventstore.rb +3 -11
- data/pg_eventstore.gemspec +19 -19
- data/sig/pg_eventstore/client.rbs +4 -8
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +2 -0
- data/sig/pg_eventstore/web/application.rbs +2 -0
- metadata +8 -7
@@ -7,7 +7,7 @@ module PgEventstore
|
|
7
7
|
ISOLATION_LEVELS = {
|
8
8
|
read_committed: 'READ COMMITTED',
|
9
9
|
repeatable_read: 'REPEATABLE READ',
|
10
|
-
serializable: 'SERIALIZABLE'
|
10
|
+
serializable: 'SERIALIZABLE',
|
11
11
|
}.tap do |h|
|
12
12
|
h.default = h[:serializable]
|
13
13
|
end.freeze
|
@@ -24,16 +24,12 @@ module PgEventstore
|
|
24
24
|
|
25
25
|
# @param level [Symbol] transaction isolation level
|
26
26
|
# @return [void]
|
27
|
-
def transaction(level = :serializable)
|
27
|
+
def transaction(level = :serializable, &blk)
|
28
28
|
connection.with do |conn|
|
29
29
|
# We are inside a transaction already - no need to start another one
|
30
|
-
if [PG::PQTRANS_ACTIVE, PG::PQTRANS_INTRANS].include?(conn.transaction_status)
|
31
|
-
next yield
|
32
|
-
end
|
30
|
+
next yield if [PG::PQTRANS_ACTIVE, PG::PQTRANS_INTRANS].include?(conn.transaction_status)
|
33
31
|
|
34
|
-
pg_transaction(ISOLATION_LEVELS[level], conn)
|
35
|
-
yield
|
36
|
-
end
|
32
|
+
pg_transaction(ISOLATION_LEVELS[level], conn, &blk)
|
37
33
|
end
|
38
34
|
end
|
39
35
|
|
@@ -42,7 +38,7 @@ module PgEventstore
|
|
42
38
|
# @param level [String] PostgreSQL transaction isolation level
|
43
39
|
# @param pg_connection [PG::Connection]
|
44
40
|
# @return [void]
|
45
|
-
def pg_transaction(level, pg_connection)
|
41
|
+
def pg_transaction(level, pg_connection, &_blk)
|
46
42
|
pg_connection.transaction do
|
47
43
|
pg_connection.exec("SET TRANSACTION ISOLATION LEVEL #{level}")
|
48
44
|
yield
|
@@ -17,7 +17,7 @@ module PgEventstore
|
|
17
17
|
:asc => 'ASC',
|
18
18
|
:desc => 'DESC',
|
19
19
|
'Forwards' => 'ASC',
|
20
|
-
'Backwards' => 'DESC'
|
20
|
+
'Backwards' => 'DESC',
|
21
21
|
}.tap do |directions|
|
22
22
|
directions.default = 'ASC'
|
23
23
|
end.freeze
|
@@ -82,9 +82,7 @@ module PgEventstore
|
|
82
82
|
# @return [Array<String>]
|
83
83
|
def extract_event_types_filter(options)
|
84
84
|
options in { filter: { event_types: Array => event_types } }
|
85
|
-
event_types = event_types&.select
|
86
|
-
_1.is_a?(String)
|
87
|
-
end
|
85
|
+
event_types = event_types&.select { _1.is_a?(String) }
|
88
86
|
event_types || []
|
89
87
|
end
|
90
88
|
|
@@ -92,10 +90,10 @@ module PgEventstore
|
|
92
90
|
# @return [Array<Hash[Symbol, String]>]
|
93
91
|
def extract_streams_filter(options)
|
94
92
|
options in { filter: { streams: Array => streams } }
|
95
|
-
streams = streams&.map do
|
96
|
-
|
97
|
-
|
98
|
-
|
93
|
+
streams = streams&.map do |stream_attrs|
|
94
|
+
stream_attrs in { context: String | NilClass => context }
|
95
|
+
stream_attrs in { stream_name: String | NilClass => stream_name }
|
96
|
+
stream_attrs in { stream_id: String | NilClass => stream_id }
|
99
97
|
{ context: context, stream_name: stream_name, stream_id: stream_id }
|
100
98
|
end
|
101
99
|
streams || []
|
@@ -123,7 +121,7 @@ module PgEventstore
|
|
123
121
|
stream_attrs.compact!
|
124
122
|
sql = stream_attrs.map do |attr, _|
|
125
123
|
"#{to_table_name}.#{attr} = ?"
|
126
|
-
end.join(
|
124
|
+
end.join(' AND ')
|
127
125
|
@sql_builder.where_or(sql, *stream_attrs.values)
|
128
126
|
end
|
129
127
|
|
@@ -133,8 +131,8 @@ module PgEventstore
|
|
133
131
|
return if event_types.empty?
|
134
132
|
|
135
133
|
sql = event_types.size.times.map do
|
136
|
-
|
137
|
-
end.join(
|
134
|
+
'?'
|
135
|
+
end.join(', ')
|
138
136
|
@sql_builder.where("#{to_table_name}.type IN (#{sql})", *event_types)
|
139
137
|
end
|
140
138
|
|
@@ -178,9 +176,11 @@ module PgEventstore
|
|
178
176
|
|
179
177
|
# @param table_name [String] system stream view name
|
180
178
|
# @return [void]
|
179
|
+
# rubocop:disable Naming/AccessorMethodName
|
181
180
|
def set_source(table_name)
|
182
|
-
@sql_builder.from(%
|
181
|
+
@sql_builder.from(%( "#{PG::Connection.escape(table_name)}" #{to_table_name} ))
|
183
182
|
end
|
183
|
+
# rubocop:enable Naming/AccessorMethodName
|
184
184
|
|
185
185
|
private
|
186
186
|
|
@@ -192,7 +192,7 @@ module PgEventstore
|
|
192
192
|
{ context: String, stream_name: nil, stream_id: nil })
|
193
193
|
return true if result
|
194
194
|
|
195
|
-
PgEventstore
|
195
|
+
PgEventstore.logger&.debug(<<~TEXT)
|
196
196
|
Ignoring unsupported stream filter format for searching #{stream_attrs.compact.inspect}. \
|
197
197
|
See docs/reading_events.md docs for supported formats.
|
198
198
|
TEXT
|
@@ -13,9 +13,7 @@ module PgEventstore
|
|
13
13
|
# @return [Array<String>]
|
14
14
|
def extract_event_types_filter(options)
|
15
15
|
options in { filter: { event_types: Array => event_types } }
|
16
|
-
event_types = event_types&.select
|
17
|
-
_1.is_a?(String)
|
18
|
-
end
|
16
|
+
event_types = event_types&.select { _1.is_a?(String) }
|
19
17
|
event_types || []
|
20
18
|
end
|
21
19
|
|
@@ -23,9 +21,9 @@ module PgEventstore
|
|
23
21
|
# @return [Array<Hash[Symbol, String]>]
|
24
22
|
def extract_streams_filter(options)
|
25
23
|
options in { filter: { streams: Array => streams } }
|
26
|
-
streams = streams&.map do
|
27
|
-
|
28
|
-
|
24
|
+
streams = streams&.map do |stream_attrs|
|
25
|
+
stream_attrs in { context: String | NilClass => context }
|
26
|
+
stream_attrs in { stream_name: String | NilClass => stream_name }
|
29
27
|
{ context: context, stream_name: stream_name }
|
30
28
|
end
|
31
29
|
streams || []
|
@@ -47,7 +45,7 @@ module PgEventstore
|
|
47
45
|
stream_attrs.compact!
|
48
46
|
sql = stream_attrs.map do |attr, _|
|
49
47
|
"#{to_table_name}.#{attr} = ?"
|
50
|
-
end.join(
|
48
|
+
end.join(' AND ')
|
51
49
|
@sql_builder.where_or(sql, *stream_attrs.values)
|
52
50
|
end
|
53
51
|
|
@@ -72,7 +70,7 @@ module PgEventstore
|
|
72
70
|
result = (stream_attrs in { context: String, stream_name: String } | { context: String, stream_name: nil })
|
73
71
|
return true if result
|
74
72
|
|
75
|
-
PgEventstore
|
73
|
+
PgEventstore.logger&.debug(<<~TEXT)
|
76
74
|
Ignoring unsupported stream filter format for grouped read #{stream_attrs.compact.inspect}. \
|
77
75
|
See docs/reading_events.md docs for supported formats.
|
78
76
|
TEXT
|
@@ -27,9 +27,7 @@ RSpec::Matchers.define :has_option do |option_name|
|
|
27
27
|
is_correct &&=
|
28
28
|
RSpec::Matchers::BuiltIn::Match.new(@default_value).matches?(obj.class.allocate.public_send(option_name))
|
29
29
|
end
|
30
|
-
if defined?(@metadata)
|
31
|
-
is_correct &&= RSpec::Matchers::BuiltIn::Match.new(@metadata).matches?(option.metadata)
|
32
|
-
end
|
30
|
+
is_correct &&= RSpec::Matchers::BuiltIn::Match.new(@metadata).matches?(option.metadata) if defined?(@metadata)
|
33
31
|
is_correct
|
34
32
|
end
|
35
33
|
|
@@ -42,23 +40,24 @@ RSpec::Matchers.define :has_option do |option_name|
|
|
42
40
|
message = "Expected #{obj.class} to have `#{option_name.inspect}' option"
|
43
41
|
message += " #{default_value_message}," if defined?(@default_value)
|
44
42
|
message += " #{metadata_message}," if defined?(@metadata)
|
45
|
-
message +=
|
43
|
+
message += ',' unless defined?(@metadata) || defined?(@default_value)
|
46
44
|
if option_presence
|
47
45
|
actual_default_value = obj.class.allocate.public_send(option_name)
|
48
46
|
actual_metadata = option.metadata
|
49
47
|
default_value_matches = RSpec::Matchers::BuiltIn::Match.new(@default_value).matches?(actual_default_value)
|
50
48
|
metadata_matches = RSpec::Matchers::BuiltIn::Match.new(@metadata).matches?(actual_metadata)
|
51
49
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
50
|
+
message +=
|
51
|
+
case [default_value_matches, metadata_matches]
|
52
|
+
when [false, true]
|
53
|
+
" but default value is #{actual_default_value.inspect}"
|
54
|
+
when [true, false]
|
55
|
+
" but metadata is #{actual_metadata.inspect}"
|
56
|
+
else
|
57
|
+
" but default value is #{actual_default_value.inspect} and metadata is #{actual_metadata.inspect}"
|
58
|
+
end
|
60
59
|
else
|
61
|
-
message +=
|
60
|
+
message += ' but there is no option found with the given name'
|
62
61
|
end
|
63
62
|
message
|
64
63
|
end
|
@@ -20,11 +20,12 @@ module PgEventstore
|
|
20
20
|
def clean_up_data
|
21
21
|
tables_to_purge = PgEventstore.connection.with do |conn|
|
22
22
|
conn.exec(<<~SQL)
|
23
|
-
SELECT tablename
|
24
|
-
FROM pg_catalog.pg_tables
|
23
|
+
SELECT tablename
|
24
|
+
FROM pg_catalog.pg_tables
|
25
25
|
WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND tablename != 'migrations'
|
26
26
|
SQL
|
27
|
-
end
|
27
|
+
end
|
28
|
+
tables_to_purge = tables_to_purge.map { |attrs| attrs['tablename'] }
|
28
29
|
tables_to_purge.each do |table_name|
|
29
30
|
PgEventstore.connection.with { |c| c.exec("DELETE FROM #{table_name}") }
|
30
31
|
end
|
@@ -14,6 +14,13 @@ module PgEventstore
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
# @return [Array<Object>] sql positional values
|
18
|
+
attr_reader :positional_values
|
19
|
+
# @return [Integer]
|
20
|
+
attr_writer :positional_values_size
|
21
|
+
|
22
|
+
protected :positional_values, :positional_values_size=
|
23
|
+
|
17
24
|
def initialize
|
18
25
|
@select_values = []
|
19
26
|
@from_value = nil
|
@@ -134,17 +141,6 @@ module PgEventstore
|
|
134
141
|
|
135
142
|
protected
|
136
143
|
|
137
|
-
# @return [Array<Object>] sql positional values
|
138
|
-
def positional_values
|
139
|
-
@positional_values
|
140
|
-
end
|
141
|
-
|
142
|
-
# @param val [Integer]
|
143
|
-
# @return [Integer]
|
144
|
-
def positional_values_size=(val)
|
145
|
-
@positional_values_size = val
|
146
|
-
end
|
147
|
-
|
148
144
|
# @return [Array<String, Array<Object>>]
|
149
145
|
def _to_exec_params
|
150
146
|
return [single_query_sql, @positional_values] if @union_values.empty?
|
@@ -196,7 +192,7 @@ module PgEventstore
|
|
196
192
|
|
197
193
|
# @return [String]
|
198
194
|
def join_sql
|
199
|
-
@join_values.map { |sql, args| extract_positional_args(sql, *args) }.join(
|
195
|
+
@join_values.map { |sql, args| extract_positional_args(sql, *args) }.join(' ')
|
200
196
|
end
|
201
197
|
|
202
198
|
# @return [String]
|
@@ -208,7 +204,7 @@ module PgEventstore
|
|
208
204
|
# @param sql [String]
|
209
205
|
# @return [String]
|
210
206
|
def extract_positional_args(sql, *arguments)
|
211
|
-
sql.gsub(
|
207
|
+
sql.gsub('?').each_with_index do |_, index|
|
212
208
|
@positional_values.push(arguments[index])
|
213
209
|
@positional_values_size += 1
|
214
210
|
"$#{@positional_values_size}"
|
data/lib/pg_eventstore/stream.rb
CHANGED
@@ -83,20 +83,20 @@ module PgEventstore
|
|
83
83
|
to_hash.hash
|
84
84
|
end
|
85
85
|
|
86
|
-
# @param
|
86
|
+
# @param other [Object]
|
87
87
|
# @return [Boolean]
|
88
|
-
def eql?(
|
89
|
-
return false unless
|
88
|
+
def eql?(other)
|
89
|
+
return false unless other.is_a?(Stream)
|
90
90
|
|
91
|
-
hash ==
|
91
|
+
hash == other.hash
|
92
92
|
end
|
93
93
|
|
94
|
-
# @param
|
94
|
+
# @param other [Object]
|
95
95
|
# @return [Boolean]
|
96
|
-
def ==(
|
97
|
-
return false unless
|
96
|
+
def ==(other)
|
97
|
+
return false unless other.is_a?(Stream)
|
98
98
|
|
99
|
-
to_hash ==
|
99
|
+
to_hash == other.to_hash
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -184,6 +184,7 @@ module PgEventstore
|
|
184
184
|
halt = true
|
185
185
|
end
|
186
186
|
break if halt
|
187
|
+
|
187
188
|
sleep 0.1
|
188
189
|
end
|
189
190
|
end
|
@@ -223,7 +224,7 @@ module PgEventstore
|
|
223
224
|
|
224
225
|
# @param state [Symbol]
|
225
226
|
# @return [Object, nil] a result of evaluating of passed block
|
226
|
-
def within_state(state, &
|
227
|
+
def within_state(state, &_blk)
|
227
228
|
synchronize do
|
228
229
|
return unless @state.public_send("#{RunnerState::STATES.fetch(state)}?")
|
229
230
|
|
@@ -247,8 +248,8 @@ module PgEventstore
|
|
247
248
|
|
248
249
|
private
|
249
250
|
|
250
|
-
def synchronize
|
251
|
-
@mutex.synchronize
|
251
|
+
def synchronize(&blk)
|
252
|
+
@mutex.synchronize(&blk)
|
252
253
|
end
|
253
254
|
|
254
255
|
# @return [void]
|
@@ -35,7 +35,7 @@ module PgEventstore
|
|
35
35
|
def attach_runner_callbacks
|
36
36
|
@basic_runner.define_callback(
|
37
37
|
:process_async, :before,
|
38
|
-
|
38
|
+
CommandsHandlerHandlers.setup_handler(:process_feeder_commands, @config_name, @subscription_feeder)
|
39
39
|
)
|
40
40
|
@basic_runner.define_callback(
|
41
41
|
:process_async, :before,
|
@@ -28,7 +28,7 @@ module PgEventstore
|
|
28
28
|
# @param raw_events [Array<Hash>]
|
29
29
|
# @return [void]
|
30
30
|
def feed(raw_events)
|
31
|
-
raise EmptyChunkFedError.new(
|
31
|
+
raise EmptyChunkFedError.new('Empty chunk was fed!') if raw_events.empty?
|
32
32
|
|
33
33
|
within_state(:running) do
|
34
34
|
callbacks.run_callbacks(:feed, Utils.original_global_position(raw_events.last))
|
@@ -18,9 +18,11 @@ module PgEventstore
|
|
18
18
|
module ClassMethods
|
19
19
|
# @param data [Hash]
|
20
20
|
# @return [Hash]
|
21
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
21
22
|
def parse_data(data)
|
22
23
|
{}
|
23
24
|
end
|
25
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
24
26
|
end
|
25
27
|
|
26
28
|
# @return [Integer]
|
@@ -28,20 +30,20 @@ module PgEventstore
|
|
28
30
|
options_hash.hash
|
29
31
|
end
|
30
32
|
|
31
|
-
# @param
|
33
|
+
# @param other [Object]
|
32
34
|
# @return [Boolean]
|
33
|
-
def eql?(
|
34
|
-
return false unless
|
35
|
+
def eql?(other)
|
36
|
+
return false unless other.is_a?(self.class)
|
35
37
|
|
36
|
-
hash ==
|
38
|
+
hash == other.hash
|
37
39
|
end
|
38
40
|
|
39
|
-
# @param
|
41
|
+
# @param other [Object]
|
40
42
|
# @return [Boolean]
|
41
|
-
def ==(
|
42
|
-
return false unless
|
43
|
+
def ==(other)
|
44
|
+
return false unless other.is_a?(self.class)
|
43
45
|
|
44
|
-
options_hash ==
|
46
|
+
options_hash == other.options_hash
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
@@ -20,8 +20,17 @@ module PgEventstore
|
|
20
20
|
# @return [PgEventstore::SubscriptionRunnerCommands::Base]
|
21
21
|
def find_or_create_by(subscription_id:, subscriptions_set_id:, command_name:, data:)
|
22
22
|
transaction_queries.transaction do
|
23
|
-
find_by(
|
24
|
-
|
23
|
+
existing = find_by(
|
24
|
+
subscription_id: subscription_id, subscriptions_set_id: subscriptions_set_id, command_name: command_name
|
25
|
+
)
|
26
|
+
next existing if existing
|
27
|
+
|
28
|
+
create(
|
29
|
+
subscription_id: subscription_id,
|
30
|
+
subscriptions_set_id: subscriptions_set_id,
|
31
|
+
command_name: command_name,
|
32
|
+
data: data
|
33
|
+
)
|
25
34
|
end
|
26
35
|
end
|
27
36
|
|
@@ -30,18 +39,15 @@ module PgEventstore
|
|
30
39
|
# @param command_name [String]
|
31
40
|
# @return [PgEventstore::SubscriptionRunnerCommands::Base, nil]
|
32
41
|
def find_by(subscription_id:, subscriptions_set_id:, command_name:)
|
33
|
-
sql_builder =
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
'subscription_id = ? AND subscriptions_set_id = ? AND name = ?',
|
39
|
-
subscription_id, subscriptions_set_id, command_name
|
40
|
-
)
|
42
|
+
sql_builder = SQLBuilder.new.select('*').from('subscription_commands')
|
43
|
+
sql_builder.where(
|
44
|
+
'subscription_id = ? AND subscriptions_set_id = ? AND name = ?',
|
45
|
+
subscription_id, subscriptions_set_id, command_name
|
46
|
+
)
|
41
47
|
pg_result = connection.with do |conn|
|
42
48
|
conn.exec_params(*sql_builder.to_exec_params)
|
43
49
|
end
|
44
|
-
return if pg_result.ntuples
|
50
|
+
return if pg_result.ntuples == 0
|
45
51
|
|
46
52
|
deserialize(pg_result.to_a.first)
|
47
53
|
end
|
@@ -53,7 +59,7 @@ module PgEventstore
|
|
53
59
|
# @return [PgEventstore::SubscriptionRunnerCommands::Base]
|
54
60
|
def create(subscription_id:, subscriptions_set_id:, command_name:, data:)
|
55
61
|
sql = <<~SQL
|
56
|
-
INSERT INTO subscription_commands (name, subscription_id, subscriptions_set_id, data)
|
62
|
+
INSERT INTO subscription_commands (name, subscription_id, subscriptions_set_id, data)
|
57
63
|
VALUES ($1, $2, $3, $4)
|
58
64
|
RETURNING *
|
59
65
|
SQL
|
@@ -69,15 +75,11 @@ module PgEventstore
|
|
69
75
|
def find_commands(subscription_ids, subscriptions_set_id:)
|
70
76
|
return [] if subscription_ids.empty?
|
71
77
|
|
72
|
-
sql = subscription_ids.size.times.map
|
73
|
-
|
74
|
-
|
75
|
-
sql_builder =
|
76
|
-
|
77
|
-
from('subscription_commands').
|
78
|
-
where("subscription_id IN (#{sql})", *subscription_ids).
|
79
|
-
where("subscriptions_set_id = ?", subscriptions_set_id).
|
80
|
-
order('id ASC')
|
78
|
+
sql = subscription_ids.size.times.map { '?' }.join(', ')
|
79
|
+
sql_builder = SQLBuilder.new.select('*').from('subscription_commands')
|
80
|
+
sql_builder.where("subscription_id IN (#{sql})", *subscription_ids)
|
81
|
+
sql_builder.where('subscriptions_set_id = ?', subscriptions_set_id)
|
82
|
+
sql_builder.order('id ASC')
|
81
83
|
pg_result = connection.with do |conn|
|
82
84
|
conn.exec_params(*sql_builder.to_exec_params)
|
83
85
|
end
|
@@ -28,7 +28,7 @@ module PgEventstore
|
|
28
28
|
pg_result = connection.with do |conn|
|
29
29
|
conn.exec_params(*builder.to_exec_params)
|
30
30
|
end
|
31
|
-
return if pg_result.ntuples
|
31
|
+
return if pg_result.ntuples == 0
|
32
32
|
|
33
33
|
deserialize(pg_result.to_a.first)
|
34
34
|
end
|
@@ -40,7 +40,7 @@ module PgEventstore
|
|
40
40
|
pg_result = connection.with do |conn|
|
41
41
|
conn.exec_params(*builder.to_exec_params)
|
42
42
|
end
|
43
|
-
return [] if pg_result.ntuples
|
43
|
+
return [] if pg_result.ntuples == 0
|
44
44
|
|
45
45
|
pg_result.map(&method(:deserialize))
|
46
46
|
end
|
@@ -50,24 +50,25 @@ module PgEventstore
|
|
50
50
|
def set_collection(state = nil)
|
51
51
|
builder = SQLBuilder.new.from('subscriptions').select('set').group('set').order('set ASC')
|
52
52
|
builder.where('state = ?', state) if state
|
53
|
-
connection.with do |conn|
|
53
|
+
raw_subscriptions = connection.with do |conn|
|
54
54
|
conn.exec_params(*builder.to_exec_params)
|
55
|
-
end
|
55
|
+
end
|
56
|
+
raw_subscriptions.map { |attrs| attrs['set'] }
|
56
57
|
end
|
57
58
|
|
58
59
|
# @param id [Integer]
|
59
60
|
# @return [Hash]
|
60
61
|
# @raise [PgEventstore::RecordNotFound]
|
61
62
|
def find!(id)
|
62
|
-
find_by(id: id) || raise(RecordNotFound.new(
|
63
|
+
find_by(id: id) || raise(RecordNotFound.new('subscriptions', id))
|
63
64
|
end
|
64
65
|
|
65
66
|
# @param attrs [Hash]
|
66
67
|
# @return [Hash]
|
67
68
|
def create(attrs)
|
68
69
|
sql = <<~SQL
|
69
|
-
INSERT INTO subscriptions (#{attrs.keys.join(', ')})
|
70
|
-
VALUES (#{Utils.positional_vars(attrs.values)})
|
70
|
+
INSERT INTO subscriptions (#{attrs.keys.join(', ')})
|
71
|
+
VALUES (#{Utils.positional_vars(attrs.values)})
|
71
72
|
RETURNING *
|
72
73
|
SQL
|
73
74
|
pg_result = connection.with do |conn|
|
@@ -93,13 +94,14 @@ module PgEventstore
|
|
93
94
|
pg_result = connection.with do |conn|
|
94
95
|
conn.exec_params(sql, [*attrs.values, id])
|
95
96
|
end
|
96
|
-
raise(RecordNotFound.new(
|
97
|
+
raise(RecordNotFound.new('subscriptions', id)) if pg_result.ntuples == 0
|
97
98
|
|
98
99
|
updated_attrs = pg_result.to_a.first
|
99
100
|
unless updated_attrs['locked_by'] == locked_by
|
100
101
|
# Subscription is force-locked by someone else. We have to roll back such transaction
|
101
102
|
raise(WrongLockIdError.new(updated_attrs['set'], updated_attrs['name'], updated_attrs['locked_by']))
|
102
103
|
end
|
104
|
+
|
103
105
|
updated_attrs
|
104
106
|
end
|
105
107
|
|
@@ -152,6 +154,7 @@ module PgEventstore
|
|
152
154
|
if attrs[:locked_by] && !force
|
153
155
|
raise SubscriptionAlreadyLockedError.new(attrs[:set], attrs[:name], attrs[:locked_by])
|
154
156
|
end
|
157
|
+
|
155
158
|
connection.with do |conn|
|
156
159
|
conn.exec_params('UPDATE subscriptions SET locked_by = $1 WHERE id = $2', [lock_id, id])
|
157
160
|
end
|
@@ -28,15 +28,12 @@ module PgEventstore
|
|
28
28
|
# @param command_name [String]
|
29
29
|
# @return [PgEventstore::SubscriptionFeederCommands::Base, nil]
|
30
30
|
def find_by(subscriptions_set_id:, command_name:)
|
31
|
-
sql_builder =
|
32
|
-
|
33
|
-
select('*').
|
34
|
-
from('subscriptions_set_commands').
|
35
|
-
where('subscriptions_set_id = ? AND name = ?', subscriptions_set_id, command_name)
|
31
|
+
sql_builder = SQLBuilder.new.select('*').from('subscriptions_set_commands')
|
32
|
+
sql_builder.where('subscriptions_set_id = ? AND name = ?', subscriptions_set_id, command_name)
|
36
33
|
pg_result = connection.with do |conn|
|
37
34
|
conn.exec_params(*sql_builder.to_exec_params)
|
38
35
|
end
|
39
|
-
return if pg_result.ntuples
|
36
|
+
return if pg_result.ntuples == 0
|
40
37
|
|
41
38
|
deserialize(pg_result.to_a.first)
|
42
39
|
end
|
@@ -47,7 +44,7 @@ module PgEventstore
|
|
47
44
|
# @return [PgEventstore::SubscriptionFeederCommands::Base]
|
48
45
|
def create(subscriptions_set_id:, command_name:, data:)
|
49
46
|
sql = <<~SQL
|
50
|
-
INSERT INTO subscriptions_set_commands (name, subscriptions_set_id, data)
|
47
|
+
INSERT INTO subscriptions_set_commands (name, subscriptions_set_id, data)
|
51
48
|
VALUES ($1, $2, $3)
|
52
49
|
RETURNING *
|
53
50
|
SQL
|
@@ -56,17 +53,15 @@ module PgEventstore
|
|
56
53
|
end
|
57
54
|
deserialize(pg_result.to_a.first)
|
58
55
|
rescue PG::ForeignKeyViolation
|
59
|
-
raise RecordNotFound.new(
|
56
|
+
raise RecordNotFound.new('subscriptions_set', subscriptions_set_id)
|
60
57
|
end
|
61
58
|
|
62
59
|
# @param subscriptions_set_id [Integer, nil]
|
63
60
|
# @return [Array<PgEventstore::SubscriptionFeederCommands::Base>]
|
64
61
|
def find_commands(subscriptions_set_id)
|
65
|
-
sql_builder =
|
66
|
-
|
67
|
-
|
68
|
-
where("subscriptions_set_id = ?", subscriptions_set_id).
|
69
|
-
order('id ASC')
|
62
|
+
sql_builder = SQLBuilder.new.select('*').from('subscriptions_set_commands')
|
63
|
+
sql_builder.where('subscriptions_set_id = ?', subscriptions_set_id)
|
64
|
+
sql_builder.order('id ASC')
|
70
65
|
pg_result = connection.with do |conn|
|
71
66
|
conn.exec_params(*sql_builder.to_exec_params)
|
72
67
|
end
|
@@ -46,9 +46,10 @@ module PgEventstore
|
|
46
46
|
def set_names
|
47
47
|
builder = SQLBuilder.new.select('name').from('subscriptions_set').group('name').order('name ASC')
|
48
48
|
|
49
|
-
connection.with do |conn|
|
49
|
+
raw_set = connection.with do |conn|
|
50
50
|
conn.exec_params(*builder.to_exec_params)
|
51
|
-
end
|
51
|
+
end
|
52
|
+
raw_set.map { |attrs| attrs['name'] }
|
52
53
|
end
|
53
54
|
|
54
55
|
# The same as #find_all, but returns first result
|
@@ -61,15 +62,15 @@ module PgEventstore
|
|
61
62
|
# @return [Hash]
|
62
63
|
# @raise [PgEventstore::RecordNotFound]
|
63
64
|
def find!(id)
|
64
|
-
find_by(id: id) || raise(RecordNotFound.new(
|
65
|
+
find_by(id: id) || raise(RecordNotFound.new('subscriptions_set', id))
|
65
66
|
end
|
66
67
|
|
67
68
|
# @param attrs [Hash]
|
68
69
|
# @return [Hash]
|
69
70
|
def create(attrs)
|
70
71
|
sql = <<~SQL
|
71
|
-
INSERT INTO subscriptions_set (#{attrs.keys.join(', ')})
|
72
|
-
VALUES (#{Utils.positional_vars(attrs.values)})
|
72
|
+
INSERT INTO subscriptions_set (#{attrs.keys.join(', ')})
|
73
|
+
VALUES (#{Utils.positional_vars(attrs.values)})
|
73
74
|
RETURNING *
|
74
75
|
SQL
|
75
76
|
pg_result = connection.with do |conn|
|
@@ -92,7 +93,7 @@ module PgEventstore
|
|
92
93
|
pg_result = connection.with do |conn|
|
93
94
|
conn.exec_params(sql, [*attrs.values, id])
|
94
95
|
end
|
95
|
-
raise(RecordNotFound.new(
|
96
|
+
raise(RecordNotFound.new('subscriptions_set', id)) if pg_result.ntuples == 0
|
96
97
|
|
97
98
|
deserialize(pg_result.to_a.first)
|
98
99
|
end
|
@@ -17,9 +17,10 @@ module PgEventstore
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def recovers?(error)
|
20
|
-
EXCEPTIONS_TO_HANDLE.any? {error.is_a?(_1) }
|
20
|
+
EXCEPTIONS_TO_HANDLE.any? { error.is_a?(_1) }
|
21
21
|
end
|
22
22
|
|
23
|
+
# rubocop:disable Lint/SuppressedException
|
23
24
|
def recover(_error)
|
24
25
|
loop do
|
25
26
|
sleep TIME_BETWEEN_RETRIES
|
@@ -32,6 +33,7 @@ module PgEventstore
|
|
32
33
|
rescue *EXCEPTIONS_TO_HANDLE
|
33
34
|
end
|
34
35
|
end
|
36
|
+
# rubocop:enable Lint/SuppressedException
|
35
37
|
|
36
38
|
private
|
37
39
|
|
data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_runner.rb
CHANGED
@@ -21,6 +21,7 @@ module PgEventstore
|
|
21
21
|
|
22
22
|
def recover(error)
|
23
23
|
return false if @restart_terminator&.call(@subscription.dup)
|
24
|
+
|
24
25
|
if @subscription.restart_count >= @subscription.max_restarts_number
|
25
26
|
@failed_subscription_notifier&.call(@subscription.dup, Utils.unwrap_exception(error))
|
26
27
|
return false
|