pg_eventstore 1.11.0 → 1.13.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/CHANGELOG.md +9 -0
- data/docs/reading_events.md +98 -0
- data/lib/pg_eventstore/cli/commands/base_command.rb +2 -0
- data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +1 -0
- data/lib/pg_eventstore/cli/commands/help_command.rb +1 -0
- data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +3 -1
- data/lib/pg_eventstore/cli/commands/stop_subscriptions_command.rb +1 -0
- data/lib/pg_eventstore/cli/exit_codes.rb +1 -0
- data/lib/pg_eventstore/cli/parser_options/base_options.rb +1 -0
- data/lib/pg_eventstore/cli/parser_options/default_options.rb +1 -0
- data/lib/pg_eventstore/cli/parser_options/metadata.rb +1 -0
- data/lib/pg_eventstore/cli/parser_options/subscription_options.rb +1 -0
- data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +2 -1
- data/lib/pg_eventstore/cli/try_unlock_subscriptions_set.rb +1 -0
- data/lib/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rb +1 -0
- data/lib/pg_eventstore/client.rb +22 -4
- data/lib/pg_eventstore/commands/all_stream_read_grouped.rb +69 -0
- data/lib/pg_eventstore/commands/regular_stream_read_grouped.rb +31 -0
- data/lib/pg_eventstore/commands.rb +2 -0
- data/lib/pg_eventstore/connection.rb +8 -3
- data/lib/pg_eventstore/extensions/callback_handlers_extension.rb +1 -0
- data/lib/pg_eventstore/partition.rb +23 -0
- data/lib/pg_eventstore/pg_connection.rb +1 -0
- data/lib/pg_eventstore/queries/event_queries.rb +18 -0
- data/lib/pg_eventstore/queries/partition_queries.rb +21 -0
- data/lib/pg_eventstore/queries.rb +2 -0
- data/lib/pg_eventstore/query_builders/basic_filtering.rb +27 -0
- data/lib/pg_eventstore/query_builders/events_filtering.rb +47 -31
- data/lib/pg_eventstore/query_builders/partitions_filtering.rb +83 -0
- data/lib/pg_eventstore/sql_builder.rb +10 -0
- data/lib/pg_eventstore/subscriptions/basic_runner.rb +122 -35
- data/lib/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rb +1 -14
- data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +4 -3
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +1 -14
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +1 -19
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +1 -0
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +1 -0
- data/lib/pg_eventstore/subscriptions/commands_handler.rb +5 -8
- data/lib/pg_eventstore/subscriptions/events_processor.rb +7 -2
- data/lib/pg_eventstore/subscriptions/extensions/base_command_extension.rb +1 -0
- data/lib/pg_eventstore/subscriptions/extensions/command_class_lookup_extension.rb +1 -0
- data/lib/pg_eventstore/subscriptions/queries/subscription_queries.rb +1 -9
- data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_connection.rb +44 -0
- data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_feeder.rb +27 -0
- data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_runner.rb +34 -0
- data/lib/pg_eventstore/subscriptions/runner_recovery_strategies.rb +5 -0
- data/lib/pg_eventstore/subscriptions/runner_recovery_strategy.rb +21 -0
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +18 -5
- data/lib/pg_eventstore/subscriptions/subscription_runner.rb +1 -13
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +1 -0
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +20 -3
- data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +1 -0
- data/lib/pg_eventstore/utils.rb +5 -4
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +5 -3
- data/lib/pg_eventstore.rb +3 -1
- data/sig/pg_eventstore/client.rbs +2 -0
- data/sig/pg_eventstore/commands/all_stream_read_grouped.rbs +16 -0
- data/sig/pg_eventstore/commands/regular_stream_read_grouped.rbs +8 -0
- data/sig/pg_eventstore/connection.rbs +2 -0
- data/sig/pg_eventstore/partition.rbs +15 -0
- data/sig/pg_eventstore/queries/event_queries.rbs +2 -0
- data/sig/pg_eventstore/queries/partition_queries.rbs +6 -0
- data/sig/pg_eventstore/query_builders/basic_filtering.rbs +15 -0
- data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +17 -17
- data/sig/pg_eventstore/query_builders/partitions_filtering.rbs +21 -0
- data/sig/pg_eventstore/sql_builder.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/basic_runner.rbs +26 -10
- data/sig/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rbs +5 -5
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +1 -3
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +0 -4
- data/sig/pg_eventstore/subscriptions/commands_handler.rbs +8 -11
- data/sig/pg_eventstore/subscriptions/events_processor.rbs +5 -17
- data/sig/pg_eventstore/subscriptions/runner_recovery_strategies/restore_connection.rbs +18 -0
- data/sig/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_feeder.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_runner.rbs +17 -0
- data/sig/pg_eventstore/subscriptions/runner_recovery_strategy.rbs +7 -0
- data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +6 -8
- data/sig/pg_eventstore/subscriptions/subscription_runner.rbs +6 -35
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +8 -0
- metadata +22 -6
@@ -3,7 +3,11 @@
|
|
3
3
|
module PgEventstore
|
4
4
|
module QueryBuilders
|
5
5
|
# @!visibility private
|
6
|
-
class EventsFiltering
|
6
|
+
class EventsFiltering < BasicFiltering
|
7
|
+
# @return [String]
|
8
|
+
TABLE_NAME = 'events'
|
9
|
+
private_constant :TABLE_NAME
|
10
|
+
|
7
11
|
# @return [Integer]
|
8
12
|
DEFAULT_LIMIT = 1_000
|
9
13
|
# @return [Hash<String => String, Symbol => String>]
|
@@ -26,6 +30,7 @@ module PgEventstore
|
|
26
30
|
# @return [PgEventstore::EventsFiltering]
|
27
31
|
def events_filtering(stream, options)
|
28
32
|
return all_stream_filtering(options) if stream.all_stream?
|
33
|
+
|
29
34
|
if stream.system? && Stream::KNOWN_SYSTEM_STREAMS.include?(stream.context)
|
30
35
|
return system_stream_filtering(stream, options)
|
31
36
|
end
|
@@ -43,11 +48,9 @@ module PgEventstore
|
|
43
48
|
# @return [PgEventstore::QueryBuilders::EventsFiltering]
|
44
49
|
def all_stream_filtering(options)
|
45
50
|
event_filter = new
|
46
|
-
options
|
47
|
-
event_filter.add_event_types(event_types)
|
51
|
+
event_filter.add_event_types(extract_event_types_filter(options))
|
48
52
|
event_filter.add_limit(options[:max_count])
|
49
|
-
options
|
50
|
-
streams&.each { |attrs| event_filter.add_stream_attrs(**attrs) }
|
53
|
+
extract_streams_filter(options).each { |attrs| event_filter.add_stream_attrs(**attrs) }
|
51
54
|
event_filter.add_global_position(options[:from_position], options[:direction])
|
52
55
|
event_filter.add_all_stream_direction(options[:direction])
|
53
56
|
event_filter
|
@@ -58,8 +61,7 @@ module PgEventstore
|
|
58
61
|
# @return [PgEventstore::QueryBuilders::EventsFiltering]
|
59
62
|
def specific_stream_filtering(stream, options)
|
60
63
|
event_filter = new
|
61
|
-
options
|
62
|
-
event_filter.add_event_types(event_types)
|
64
|
+
event_filter.add_event_types(extract_event_types_filter(options))
|
63
65
|
event_filter.add_limit(options[:max_count])
|
64
66
|
event_filter.add_stream_attrs(**stream.to_hash)
|
65
67
|
event_filter.add_revision(options[:from_revision], options[:direction])
|
@@ -75,14 +77,39 @@ module PgEventstore
|
|
75
77
|
event_filter.set_source(stream.context)
|
76
78
|
end
|
77
79
|
end
|
80
|
+
|
81
|
+
# @param options [Hash]
|
82
|
+
# @return [Array<String>]
|
83
|
+
def extract_event_types_filter(options)
|
84
|
+
options in { filter: { event_types: Array => event_types } }
|
85
|
+
event_types&.select! do
|
86
|
+
_1.is_a?(String)
|
87
|
+
end
|
88
|
+
event_types || []
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param options [Hash]
|
92
|
+
# @return [Array<Hash[Symbol, String]>]
|
93
|
+
def extract_streams_filter(options)
|
94
|
+
options in { filter: { streams: Array => streams } }
|
95
|
+
streams = streams&.map do
|
96
|
+
_1 in { context: String | NilClass => context }
|
97
|
+
_1 in { stream_name: String | NilClass => stream_name }
|
98
|
+
_1 in { stream_id: String | NilClass => stream_id }
|
99
|
+
{ context: context, stream_name: stream_name, stream_id: stream_id }
|
100
|
+
end
|
101
|
+
streams || []
|
102
|
+
end
|
78
103
|
end
|
79
104
|
|
80
105
|
def initialize
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
106
|
+
super
|
107
|
+
@sql_builder.limit(DEFAULT_LIMIT)
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [String]
|
111
|
+
def to_table_name
|
112
|
+
TABLE_NAME
|
86
113
|
end
|
87
114
|
|
88
115
|
# @param context [String, nil]
|
@@ -95,21 +122,20 @@ module PgEventstore
|
|
95
122
|
|
96
123
|
stream_attrs.compact!
|
97
124
|
sql = stream_attrs.map do |attr, _|
|
98
|
-
"
|
125
|
+
"#{to_table_name}.#{attr} = ?"
|
99
126
|
end.join(" AND ")
|
100
127
|
@sql_builder.where_or(sql, *stream_attrs.values)
|
101
128
|
end
|
102
129
|
|
103
|
-
# @param event_types [Array<String
|
130
|
+
# @param event_types [Array<String>]
|
104
131
|
# @return [void]
|
105
132
|
def add_event_types(event_types)
|
106
|
-
return if event_types.nil?
|
107
133
|
return if event_types.empty?
|
108
134
|
|
109
135
|
sql = event_types.size.times.map do
|
110
136
|
"?"
|
111
137
|
end.join(", ")
|
112
|
-
@sql_builder.where("
|
138
|
+
@sql_builder.where("#{to_table_name}.type IN (#{sql})", *event_types)
|
113
139
|
end
|
114
140
|
|
115
141
|
# @param revision [Integer, nil]
|
@@ -118,7 +144,7 @@ module PgEventstore
|
|
118
144
|
def add_revision(revision, direction)
|
119
145
|
return unless revision
|
120
146
|
|
121
|
-
@sql_builder.where("
|
147
|
+
@sql_builder.where("#{to_table_name}.stream_revision #{direction_operator(direction)} ?", revision)
|
122
148
|
end
|
123
149
|
|
124
150
|
# @param position [Integer, nil]
|
@@ -127,19 +153,19 @@ module PgEventstore
|
|
127
153
|
def add_global_position(position, direction)
|
128
154
|
return unless position
|
129
155
|
|
130
|
-
@sql_builder.where("
|
156
|
+
@sql_builder.where("#{to_table_name}.global_position #{direction_operator(direction)} ?", position)
|
131
157
|
end
|
132
158
|
|
133
159
|
# @param direction [String, Symbol, nil]
|
134
160
|
# @return [void]
|
135
161
|
def add_stream_direction(direction)
|
136
|
-
@sql_builder.order("
|
162
|
+
@sql_builder.order("#{to_table_name}.stream_revision #{SQL_DIRECTIONS[direction]}")
|
137
163
|
end
|
138
164
|
|
139
165
|
# @param direction [String, Symbol, nil]
|
140
166
|
# @return [void]
|
141
167
|
def add_all_stream_direction(direction)
|
142
|
-
@sql_builder.order("
|
168
|
+
@sql_builder.order("#{to_table_name}.global_position #{SQL_DIRECTIONS[direction]}")
|
143
169
|
end
|
144
170
|
|
145
171
|
# @param limit [Integer, nil]
|
@@ -150,20 +176,10 @@ module PgEventstore
|
|
150
176
|
@sql_builder.limit(limit)
|
151
177
|
end
|
152
178
|
|
153
|
-
# @return [PgEventstore::SQLBuilder]
|
154
|
-
def to_sql_builder
|
155
|
-
@sql_builder
|
156
|
-
end
|
157
|
-
|
158
|
-
# @return [Array]
|
159
|
-
def to_exec_params
|
160
|
-
@sql_builder.to_exec_params
|
161
|
-
end
|
162
|
-
|
163
179
|
# @param table_name [String] system stream view name
|
164
180
|
# @return [void]
|
165
181
|
def set_source(table_name)
|
166
|
-
@sql_builder.from(%{ "#{PG::Connection.escape(table_name)}"
|
182
|
+
@sql_builder.from(%{ "#{PG::Connection.escape(table_name)}" #{to_table_name} })
|
167
183
|
end
|
168
184
|
|
169
185
|
private
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module QueryBuilders
|
5
|
+
# @!visibility private
|
6
|
+
class PartitionsFiltering < BasicFiltering
|
7
|
+
# @return [String]
|
8
|
+
TABLE_NAME = 'partitions'
|
9
|
+
private_constant :TABLE_NAME
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# @param options [Hash]
|
13
|
+
# @return [Array<String>]
|
14
|
+
def extract_event_types_filter(options)
|
15
|
+
options in { filter: { event_types: Array => event_types } }
|
16
|
+
event_types&.select! do
|
17
|
+
_1.is_a?(String)
|
18
|
+
end
|
19
|
+
event_types || []
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param options [Hash]
|
23
|
+
# @return [Array<Hash[Symbol, String]>]
|
24
|
+
def extract_streams_filter(options)
|
25
|
+
options in { filter: { streams: Array => streams } }
|
26
|
+
streams = streams&.map do
|
27
|
+
_1 in { context: String | NilClass => context }
|
28
|
+
_1 in { stream_name: String | NilClass => stream_name }
|
29
|
+
{ context: context, stream_name: stream_name }
|
30
|
+
end
|
31
|
+
streams || []
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [String]
|
36
|
+
def to_table_name
|
37
|
+
TABLE_NAME
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param context [String, nil]
|
41
|
+
# @param stream_name [String, nil]
|
42
|
+
# @return [void]
|
43
|
+
def add_stream_attrs(context: nil, stream_name: nil)
|
44
|
+
stream_attrs = { context: context, stream_name: stream_name }
|
45
|
+
return unless correct_stream_filter?(stream_attrs)
|
46
|
+
|
47
|
+
stream_attrs.compact!
|
48
|
+
sql = stream_attrs.map do |attr, _|
|
49
|
+
"#{to_table_name}.#{attr} = ?"
|
50
|
+
end.join(" AND ")
|
51
|
+
@sql_builder.where_or(sql, *stream_attrs.values)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param event_types [Array<String>]
|
55
|
+
# @return [void]
|
56
|
+
def add_event_types(event_types)
|
57
|
+
return if event_types.empty?
|
58
|
+
|
59
|
+
@sql_builder.where("#{to_table_name}.event_type = ANY(?::varchar[])", event_types)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [void]
|
63
|
+
def with_event_types
|
64
|
+
@sql_builder.where('event_type IS NOT NULL')
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# @param stream_attrs [Hash]
|
70
|
+
# @return [Boolean]
|
71
|
+
def correct_stream_filter?(stream_attrs)
|
72
|
+
result = (stream_attrs in { context: String, stream_name: String } | { context: String, stream_name: nil })
|
73
|
+
return true if result
|
74
|
+
|
75
|
+
PgEventstore&.logger&.debug(<<~TEXT)
|
76
|
+
Ignoring unsupported stream filter format for grouped read #{stream_attrs.compact.inspect}. \
|
77
|
+
See docs/reading_events.md docs for supported formats.
|
78
|
+
TEXT
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -4,6 +4,16 @@ module PgEventstore
|
|
4
4
|
# Deadly simple SQL builder
|
5
5
|
# @!visibility private
|
6
6
|
class SQLBuilder
|
7
|
+
class << self
|
8
|
+
# @param builders [Array<PgEventstore::SQLBuilder>]
|
9
|
+
# @return [PgEventstore::SQLBuilder]
|
10
|
+
def union_builders(builders)
|
11
|
+
builders[1..].each_with_object(builders[0]) do |builder, first_builder|
|
12
|
+
first_builder.union(builder)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
7
17
|
def initialize
|
8
18
|
@select_values = []
|
9
19
|
@from_value = nil
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PgEventstore
|
4
|
-
# Implements simple background
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# Implements simple background job runner. A job execution is done via declaring a callback on a specific action. The
|
5
|
+
# implementation also allows you to hook into different places of life cycle of the runner by defining callbacks on
|
6
|
+
# various actions. Here is the list of available actions:
|
7
7
|
# - :before_runner_started. Happens before the runner's state switches from "initial"/"stopped" to "running" and
|
8
8
|
# runner's thread is started. It is also fired when the runner is restoring - right after :before_runner_restored
|
9
9
|
# action.
|
@@ -24,12 +24,41 @@ module PgEventstore
|
|
24
24
|
#
|
25
25
|
# def_delegators :@basic_runner, :start, :stop, :wait_for_finish, :stop_async, :restore
|
26
26
|
#
|
27
|
+
# class SimpleRecoveryStrategy
|
28
|
+
# include PgEventstore::RunnerRecoveryStrategy
|
29
|
+
#
|
30
|
+
# def initialize(restore_func)
|
31
|
+
# @attempts_count = 0
|
32
|
+
# @restore_func = restore_func
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# def recovers?(error)
|
36
|
+
# error.message.include?("I can not handle this any more!")
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# def recover(error)
|
40
|
+
# (@attempts_count < 3).tap do |res|
|
41
|
+
# @attempts_count += 1
|
42
|
+
# @restore_func.call if res
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
27
47
|
# def initialize
|
28
|
-
# @basic_runner = PgEventstore::BasicRunner.new(
|
48
|
+
# @basic_runner = PgEventstore::BasicRunner.new(
|
49
|
+
# run_interval: 1, async_shutdown_time: 2, recovery_strategies: recovery_strategies
|
50
|
+
# )
|
29
51
|
# @jobs_performed = 0
|
52
|
+
# @jobs_limit = 3
|
30
53
|
# attach_runner_callbacks
|
31
54
|
# end
|
32
55
|
#
|
56
|
+
# protected
|
57
|
+
#
|
58
|
+
# def work_harder
|
59
|
+
# @jobs_limit += 3
|
60
|
+
# end
|
61
|
+
#
|
33
62
|
# private
|
34
63
|
#
|
35
64
|
# def attach_runner_callbacks
|
@@ -42,7 +71,7 @@ module PgEventstore
|
|
42
71
|
# end
|
43
72
|
#
|
44
73
|
# def process_action
|
45
|
-
# raise "What's the point? I can not handle this any more!" if @jobs_performed >=
|
74
|
+
# raise "What's the point? I can not handle this any more!" if @jobs_performed >= @jobs_limit
|
46
75
|
# puts "Doing some heavy lifting job"
|
47
76
|
# sleep 2 # Simulate long running job
|
48
77
|
# end
|
@@ -67,6 +96,10 @@ module PgEventstore
|
|
67
96
|
# def after_runner_died(error)
|
68
97
|
# puts "Error occurred: #{error.inspect}"
|
69
98
|
# end
|
99
|
+
#
|
100
|
+
# def recovery_strategies
|
101
|
+
# [SimpleRecoveryStrategy.new(method(:work_harder))]
|
102
|
+
# end
|
70
103
|
# end
|
71
104
|
#
|
72
105
|
# runner = MyAwesomeRunner.new
|
@@ -85,11 +118,13 @@ module PgEventstore
|
|
85
118
|
# :after_runner_stopped callback
|
86
119
|
# @param async_shutdown_time [Integer, Float] seconds. Determines how long to wait before force-shutdown the runner.
|
87
120
|
# It is only meaningful for the #stop_async
|
88
|
-
def initialize(run_interval
|
121
|
+
def initialize(run_interval:, async_shutdown_time:, recovery_strategies: [])
|
89
122
|
@run_interval = run_interval
|
90
123
|
@async_shutdown_time = async_shutdown_time
|
124
|
+
@recovery_strategies = recovery_strategies
|
91
125
|
@state = RunnerState.new
|
92
126
|
@mutex = Thread::Mutex.new
|
127
|
+
@runner = nil
|
93
128
|
delegate_change_state_cbx
|
94
129
|
end
|
95
130
|
|
@@ -127,36 +162,40 @@ module PgEventstore
|
|
127
162
|
synchronize do
|
128
163
|
return self unless @state.running? || @state.dead?
|
129
164
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
165
|
+
begin
|
166
|
+
@state.halting!
|
167
|
+
ensure
|
168
|
+
Thread.new do
|
169
|
+
stopping_at = Time.now.utc
|
170
|
+
halt = false
|
171
|
+
loop do
|
172
|
+
synchronize do
|
173
|
+
# Give the runner up to @async_shutdown_time seconds for graceful shutdown
|
174
|
+
@runner&.exit if Time.now.utc - stopping_at > @async_shutdown_time
|
175
|
+
|
176
|
+
unless @runner&.alive?
|
177
|
+
@state.stopped!
|
178
|
+
callbacks.run_callbacks(:after_runner_stopped)
|
179
|
+
end
|
180
|
+
ensure
|
181
|
+
next if @runner&.alive?
|
138
182
|
|
139
|
-
unless @runner&.alive?
|
140
|
-
@state.stopped!
|
141
183
|
@runner = nil
|
142
|
-
callbacks.run_callbacks(:after_runner_stopped)
|
143
184
|
halt = true
|
144
185
|
end
|
186
|
+
break if halt
|
187
|
+
sleep 0.1
|
145
188
|
end
|
146
|
-
break if halt
|
147
|
-
sleep 0.1
|
148
189
|
end
|
149
190
|
end
|
150
|
-
self
|
151
191
|
end
|
192
|
+
self
|
152
193
|
end
|
153
194
|
|
154
195
|
# Restores the runner after its death.
|
155
196
|
# @return [self]
|
156
197
|
def restore
|
157
|
-
|
158
|
-
return self unless @state.dead?
|
159
|
-
|
198
|
+
within_state(:dead) do
|
160
199
|
callbacks.run_callbacks(:before_runner_restored)
|
161
200
|
_start
|
162
201
|
end
|
@@ -183,7 +222,7 @@ module PgEventstore
|
|
183
222
|
end
|
184
223
|
|
185
224
|
# @param state [Symbol]
|
186
|
-
# @return [Object] a result of evaluating of passed block
|
225
|
+
# @return [Object, nil] a result of evaluating of passed block
|
187
226
|
def within_state(state, &blk)
|
188
227
|
synchronize do
|
189
228
|
return unless @state.public_send("#{RunnerState::STATES.fetch(state)}?")
|
@@ -192,6 +231,20 @@ module PgEventstore
|
|
192
231
|
end
|
193
232
|
end
|
194
233
|
|
234
|
+
protected
|
235
|
+
|
236
|
+
# @param error [StandardError]
|
237
|
+
# @param strategy [PgEventstore::RunnerRecoveryStrategy]
|
238
|
+
# @param current_runner_id [Integer]
|
239
|
+
# @return [Thread]
|
240
|
+
def async_recover(error, strategy, current_runner_id)
|
241
|
+
Thread.new do
|
242
|
+
init_recovery_ripper(current_runner_id)
|
243
|
+
Thread.current.exit unless strategy.recover(error)
|
244
|
+
recoverable { restore }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
195
248
|
private
|
196
249
|
|
197
250
|
def synchronize
|
@@ -201,19 +254,15 @@ module PgEventstore
|
|
201
254
|
# @return [void]
|
202
255
|
def _start
|
203
256
|
@state.running!
|
257
|
+
ensure
|
204
258
|
@runner = Thread.new do
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
callbacks.run_callbacks(:process_async)
|
210
|
-
end
|
211
|
-
rescue => error
|
212
|
-
synchronize do
|
213
|
-
raise unless @state.halting? || @state.running?
|
259
|
+
recoverable do
|
260
|
+
loop do
|
261
|
+
Thread.current.exit unless @state.running?
|
262
|
+
sleep @run_interval
|
214
263
|
|
215
|
-
|
216
|
-
|
264
|
+
callbacks.run_callbacks(:process_async)
|
265
|
+
end
|
217
266
|
end
|
218
267
|
end
|
219
268
|
end
|
@@ -228,5 +277,43 @@ module PgEventstore
|
|
228
277
|
def change_state(...)
|
229
278
|
callbacks.run_callbacks(:change_state, ...)
|
230
279
|
end
|
280
|
+
|
281
|
+
# @param error [StandardError]
|
282
|
+
# @return [PgEventstore::RunnerRecoveryStrategy, nil]
|
283
|
+
def suitable_strategy(error)
|
284
|
+
@recovery_strategies.find { _1.recovers?(error) }
|
285
|
+
end
|
286
|
+
|
287
|
+
# @return [void]
|
288
|
+
def recoverable
|
289
|
+
yield
|
290
|
+
rescue => error
|
291
|
+
synchronize do
|
292
|
+
raise unless @state.halting? || @state.running?
|
293
|
+
|
294
|
+
recovery_strategy = suitable_strategy(error)
|
295
|
+
@state.dead!
|
296
|
+
callbacks.run_callbacks(:after_runner_died, error)
|
297
|
+
ensure
|
298
|
+
async_recover(error, recovery_strategy, @runner.__id__) if recovery_strategy
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# @param current_runner_id [Integer]
|
303
|
+
# @return [Thread]
|
304
|
+
def init_recovery_ripper(current_runner_id)
|
305
|
+
recovery_job = Thread.current
|
306
|
+
Thread.new do
|
307
|
+
loop do
|
308
|
+
synchronize do
|
309
|
+
recovery_job.exit unless @state.dead?
|
310
|
+
recovery_job.exit unless current_runner_id == @runner.__id__
|
311
|
+
end
|
312
|
+
break unless recovery_job.alive?
|
313
|
+
|
314
|
+
sleep 1
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
231
318
|
end
|
232
319
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PgEventstore
|
4
|
+
# @!visibility private
|
4
5
|
class CommandsHandlerHandlers
|
5
6
|
include Extensions::CallbackHandlersExtension
|
6
7
|
|
@@ -19,20 +20,6 @@ module PgEventstore
|
|
19
20
|
def process_runners_commands(config_name, runners, subscription_feeder)
|
20
21
|
CommandHandlers::SubscriptionRunnersCommands.new(config_name, runners, subscription_feeder.id).process
|
21
22
|
end
|
22
|
-
|
23
|
-
# @param basic_runner [PgEventstore::BasicRunner]
|
24
|
-
# @param restart_delay [Integer]
|
25
|
-
# @param error [StandardError]
|
26
|
-
# @return [void]
|
27
|
-
def restore_runner(basic_runner, restart_delay, error)
|
28
|
-
PgEventstore.logger&.error "PgEventstore::CommandsHandler: Error occurred: #{error.message}"
|
29
|
-
PgEventstore.logger&.error "PgEventstore::CommandsHandler: Backtrace: #{error.backtrace&.join("\n")}"
|
30
|
-
PgEventstore.logger&.error "PgEventstore::CommandsHandler: Trying to auto-repair in #{restart_delay} seconds..."
|
31
|
-
Thread.new do
|
32
|
-
sleep restart_delay
|
33
|
-
basic_runner.restore
|
34
|
-
end
|
35
|
-
end
|
36
23
|
end
|
37
24
|
end
|
38
25
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PgEventstore
|
4
|
+
# @!visibility private
|
4
5
|
class EventsProcessorHandlers
|
5
6
|
include Extensions::CallbackHandlersExtension
|
6
7
|
|
@@ -15,10 +16,10 @@ module PgEventstore
|
|
15
16
|
|
16
17
|
callbacks.run_callbacks(:process, Utils.original_global_position(raw_event)) do
|
17
18
|
handler.call(raw_event)
|
19
|
+
rescue => exception
|
20
|
+
raw_events.unshift(raw_event)
|
21
|
+
raise Utils.wrap_exception(exception, global_position: Utils.original_global_position(raw_event))
|
18
22
|
end
|
19
|
-
rescue => exception
|
20
|
-
raw_events.unshift(raw_event)
|
21
|
-
raise Utils.wrap_exception(exception, global_position: Utils.original_global_position(raw_event))
|
22
23
|
end
|
23
24
|
|
24
25
|
# @param callbacks [PgEventstore::Callbacks]
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PgEventstore
|
4
|
+
# @!visibility private
|
4
5
|
class SubscriptionFeederHandlers
|
5
6
|
include Extensions::CallbackHandlersExtension
|
6
7
|
|
@@ -39,20 +40,6 @@ module PgEventstore
|
|
39
40
|
)
|
40
41
|
end
|
41
42
|
|
42
|
-
# @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
|
43
|
-
# @param basic_runner [PgEventstore::BasicRunner]
|
44
|
-
# @param _error [StandardError]
|
45
|
-
# @return [void]
|
46
|
-
def restart_runner(subscriptions_set_lifecycle, basic_runner, _error)
|
47
|
-
subscriptions_set = subscriptions_set_lifecycle.persisted_subscriptions_set
|
48
|
-
return if subscriptions_set.restart_count >= subscriptions_set.max_restarts_number
|
49
|
-
|
50
|
-
Thread.new do
|
51
|
-
sleep subscriptions_set.time_between_restarts
|
52
|
-
basic_runner.restore
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
43
|
# @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
|
57
44
|
# @return [void]
|
58
45
|
def ping_subscriptions_set(subscriptions_set_lifecycle)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PgEventstore
|
4
|
+
# @!visibility private
|
4
5
|
class SubscriptionRunnerHandlers
|
5
6
|
include Extensions::CallbackHandlersExtension
|
6
7
|
|
@@ -32,25 +33,6 @@ module PgEventstore
|
|
32
33
|
subscription.update(last_error: Utils.error_info(error), last_error_occurred_at: Time.now.utc)
|
33
34
|
end
|
34
35
|
|
35
|
-
# @param subscription [PgEventstore::Subscription]
|
36
|
-
# @param restart_terminator [#call, nil]
|
37
|
-
# @param failed_subscription_notifier [#call, nil]
|
38
|
-
# @param events_processor [PgEventstore::EventsProcessor]
|
39
|
-
# @param error [PgEventstore::WrappedException]
|
40
|
-
# @return [void]
|
41
|
-
def restart_events_processor(subscription, restart_terminator, failed_subscription_notifier, events_processor,
|
42
|
-
error)
|
43
|
-
return if restart_terminator&.call(subscription.dup)
|
44
|
-
if subscription.restart_count >= subscription.max_restarts_number
|
45
|
-
return failed_subscription_notifier&.call(subscription.dup, Utils.unwrap_exception(error))
|
46
|
-
end
|
47
|
-
|
48
|
-
Thread.new do
|
49
|
-
sleep subscription.time_between_restarts
|
50
|
-
events_processor.restore
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
36
|
# @param subscription [PgEventstore::Subscription]
|
55
37
|
# @param global_position [Integer]
|
56
38
|
# @return [void]
|
@@ -10,8 +10,6 @@ module PgEventstore
|
|
10
10
|
class CommandsHandler
|
11
11
|
extend Forwardable
|
12
12
|
|
13
|
-
# @return [Integer] the delay in seconds between runner restarts
|
14
|
-
RESTART_DELAY = 5
|
15
13
|
# @return [Integer] seconds, how often to check for new commands
|
16
14
|
PULL_INTERVAL = 1
|
17
15
|
|
@@ -24,7 +22,11 @@ module PgEventstore
|
|
24
22
|
@config_name = config_name
|
25
23
|
@subscription_feeder = subscription_feeder
|
26
24
|
@runners = runners
|
27
|
-
@basic_runner = BasicRunner.new(
|
25
|
+
@basic_runner = BasicRunner.new(
|
26
|
+
run_interval: PULL_INTERVAL,
|
27
|
+
async_shutdown_time: 0,
|
28
|
+
recovery_strategies: [RunnerRecoveryStrategies::RestoreConnection.new(config_name)]
|
29
|
+
)
|
28
30
|
attach_runner_callbacks
|
29
31
|
end
|
30
32
|
|
@@ -39,11 +41,6 @@ module PgEventstore
|
|
39
41
|
:process_async, :before,
|
40
42
|
CommandsHandlerHandlers.setup_handler(:process_runners_commands, @config_name, @runners, @subscription_feeder)
|
41
43
|
)
|
42
|
-
|
43
|
-
@basic_runner.define_callback(
|
44
|
-
:after_runner_died, :before,
|
45
|
-
CommandsHandlerHandlers.setup_handler(:restore_runner, @basic_runner, RESTART_DELAY)
|
46
|
-
)
|
47
44
|
end
|
48
45
|
end
|
49
46
|
end
|