pg_eventstore 1.12.0 → 1.13.1

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/lib/pg_eventstore/cli/commands/base_command.rb +2 -0
  4. data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +1 -0
  5. data/lib/pg_eventstore/cli/commands/help_command.rb +1 -0
  6. data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +3 -1
  7. data/lib/pg_eventstore/cli/commands/stop_subscriptions_command.rb +1 -0
  8. data/lib/pg_eventstore/cli/exit_codes.rb +1 -0
  9. data/lib/pg_eventstore/cli/parser_options/base_options.rb +1 -0
  10. data/lib/pg_eventstore/cli/parser_options/default_options.rb +1 -0
  11. data/lib/pg_eventstore/cli/parser_options/metadata.rb +1 -0
  12. data/lib/pg_eventstore/cli/parser_options/subscription_options.rb +1 -0
  13. data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +2 -1
  14. data/lib/pg_eventstore/cli/try_unlock_subscriptions_set.rb +1 -0
  15. data/lib/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rb +1 -0
  16. data/lib/pg_eventstore/connection.rb +8 -3
  17. data/lib/pg_eventstore/extensions/callback_handlers_extension.rb +1 -0
  18. data/lib/pg_eventstore/pg_connection.rb +1 -0
  19. data/lib/pg_eventstore/query_builders/events_filtering.rb +1 -1
  20. data/lib/pg_eventstore/query_builders/partitions_filtering.rb +1 -1
  21. data/lib/pg_eventstore/subscriptions/basic_runner.rb +122 -35
  22. data/lib/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rb +1 -14
  23. data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +4 -3
  24. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +1 -14
  25. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +1 -19
  26. data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +1 -0
  27. data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +1 -0
  28. data/lib/pg_eventstore/subscriptions/commands_handler.rb +5 -8
  29. data/lib/pg_eventstore/subscriptions/events_processor.rb +7 -2
  30. data/lib/pg_eventstore/subscriptions/extensions/base_command_extension.rb +1 -0
  31. data/lib/pg_eventstore/subscriptions/extensions/command_class_lookup_extension.rb +1 -0
  32. data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_connection.rb +44 -0
  33. data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_feeder.rb +27 -0
  34. data/lib/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_runner.rb +34 -0
  35. data/lib/pg_eventstore/subscriptions/runner_recovery_strategies.rb +5 -0
  36. data/lib/pg_eventstore/subscriptions/runner_recovery_strategy.rb +21 -0
  37. data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +18 -5
  38. data/lib/pg_eventstore/subscriptions/subscription_runner.rb +1 -13
  39. data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +1 -0
  40. data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +20 -3
  41. data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +1 -0
  42. data/lib/pg_eventstore/version.rb +1 -1
  43. data/lib/pg_eventstore/web/application.rb +1 -0
  44. data/lib/pg_eventstore.rb +2 -1
  45. data/sig/pg_eventstore/connection.rbs +2 -0
  46. data/sig/pg_eventstore/subscriptions/basic_runner.rbs +26 -10
  47. data/sig/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rbs +5 -5
  48. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +1 -3
  49. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +0 -4
  50. data/sig/pg_eventstore/subscriptions/commands_handler.rbs +8 -11
  51. data/sig/pg_eventstore/subscriptions/events_processor.rbs +5 -17
  52. data/sig/pg_eventstore/subscriptions/runner_recovery_strategies/restore_connection.rbs +18 -0
  53. data/sig/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_feeder.rbs +11 -0
  54. data/sig/pg_eventstore/subscriptions/runner_recovery_strategies/restore_subscription_runner.rbs +17 -0
  55. data/sig/pg_eventstore/subscriptions/runner_recovery_strategy.rbs +7 -0
  56. data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +6 -8
  57. data/sig/pg_eventstore/subscriptions/subscription_runner.rbs +6 -35
  58. data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +8 -0
  59. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0de14f58d080bcab32005e045530ca7888761e5415d013dbb262746a02806ba
4
- data.tar.gz: fee7b7335212796361b5ded87497c7d744ed5ddfd011a47d54cc2b545991b227
3
+ metadata.gz: b8ddfb0310abc9f9b78b20c0f8c6466ad2ae585ce66fd7ecae7a94bd6fe179a6
4
+ data.tar.gz: 2e06fa6f70db70b61f867d33a02eb8c13b50937f6c5afc7cf29f670f8759a9af
5
5
  SHA512:
6
- metadata.gz: 0dbdd04e5778b292177062e5a44ed9ee4c2fc03e98da945a73523036f62ed09cc27ac00d1a5853e99975b82fc798f2f83639bbb2cd4f1745150ff121ca5d7056
7
- data.tar.gz: 060f8b1b51cda7be2c472f914dc611fee2db052a2cd773676013b2175e58cf12c3895e8b631a0d79443804f3a733ca25d7012fac6519551308c890df065457d5
6
+ metadata.gz: f31f6c07073da1a1f460cf65ec55056e1bc7ab28c72f69361cf2c77219ea529324eba85aebbd94cffecbcf3d0354c73b43ca9c3441a7dae39fbc91df8bce792f
7
+ data.tar.gz: 8b60ce11b14e5832d53f732690043c55662d5007a919d7a734199a55b4f07be7f1faa1ee13163dda12cac4e7772d6f7a12d57966a07ea021dd00b6cfee80a549
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.13.1]
4
+
5
+ - Do not modify public methods arguments
6
+
7
+ ## [1.13.0]
8
+
9
+ - Introduce automatic subscriptions recovery from connection errors. This way if a subscription process loses the connection to the database - it will be trying to reconnect until the connection is restored.
10
+ - Resolve ambiguity in usage of `PgEventstore.config` method. It now returns the frozen object.
11
+
3
12
  ## [1.12.0]
4
13
 
5
14
  - Introduce `#read_grouped` API method that allows to group events by type
@@ -3,7 +3,9 @@
3
3
  module PgEventstore
4
4
  module CLI
5
5
  module Commands
6
+ # @!visibility private
6
7
  class BaseCommand
8
+ # @!visibility private
7
9
  module BaseCommandActions
8
10
  # @return [Integer] exit code
9
11
  def call
@@ -4,6 +4,7 @@ module PgEventstore
4
4
  module CLI
5
5
  module Commands
6
6
  module CallbackHandlers
7
+ # @!visibility private
7
8
  class StartCmdHandlers
8
9
  include Extensions::CallbackHandlersExtension
9
10
 
@@ -3,6 +3,7 @@
3
3
  module PgEventstore
4
4
  module CLI
5
5
  module Commands
6
+ # @!visibility private
6
7
  class HelpCommand
7
8
  attr_reader :options
8
9
 
@@ -5,6 +5,7 @@ require_relative 'callback_handlers/start_cmd_handlers'
5
5
  module PgEventstore
6
6
  module CLI
7
7
  module Commands
8
+ # @!visibility private
8
9
  class StartSubscriptionsCommand < BaseCommand
9
10
  # @return [Integer] seconds
10
11
  KEEP_ALIVE_INTERVAL = 2
@@ -55,8 +56,9 @@ module PgEventstore
55
56
  manager.stop
56
57
  end
57
58
  end.each(&:join)
58
- Utils.remove_file(options.pid_path)
59
+ ensure
59
60
  @running = false
61
+ Utils.remove_file(options.pid_path)
60
62
  end
61
63
  end
62
64
 
@@ -3,6 +3,7 @@
3
3
  module PgEventstore
4
4
  module CLI
5
5
  module Commands
6
+ # @!visibility private
6
7
  class StopSubscriptionsCommand < BaseCommand
7
8
  # @return [Integer] exit code
8
9
  def call
@@ -2,6 +2,7 @@
2
2
 
3
3
  module PgEventstore
4
4
  module CLI
5
+ # @!visibility private
5
6
  module ExitCodes
6
7
  # @return [Integer]
7
8
  SUCCESS = 0
@@ -3,6 +3,7 @@
3
3
  module PgEventstore
4
4
  module CLI
5
5
  module ParserOptions
6
+ # @!visibility private
6
7
  class BaseOptions
7
8
  include Extensions::OptionsExtension
8
9
 
@@ -3,6 +3,7 @@
3
3
  module PgEventstore
4
4
  module CLI
5
5
  module ParserOptions
6
+ # @!visibility private
6
7
  class DefaultOptions < BaseOptions
7
8
  end
8
9
  end
@@ -3,6 +3,7 @@
3
3
  module PgEventstore
4
4
  module CLI
5
5
  module ParserOptions
6
+ # @!visibility private
6
7
  class Metadata
7
8
  include Extensions::OptionsExtension
8
9
 
@@ -3,6 +3,7 @@
3
3
  module PgEventstore
4
4
  module CLI
5
5
  module ParserOptions
6
+ # @!visibility private
6
7
  class SubscriptionOptions < BaseOptions
7
8
  option(
8
9
  :pid_path,
@@ -2,6 +2,7 @@
2
2
 
3
3
  module PgEventstore
4
4
  module CLI
5
+ # @!visibility private
5
6
  class TryToDeleteSubscriptionsSet
6
7
  class << self
7
8
  def try_to_delete(...)
@@ -30,7 +31,7 @@ module PgEventstore
30
31
  # Potentially CommandsHandler can be dead exactly at the same moment we expect it to process "Ping" command.
31
32
  # Wait for potential recover plus run interval and plus another second to allow potential processing of
32
33
  # "Ping" command. "Ping" command comes in prio, so it is guaranteed it will be processed as a first command.
33
- sleep CommandsHandler::RESTART_DELAY + CommandsHandler::PULL_INTERVAL + 1
34
+ sleep RunnerRecoveryStrategies::RestoreConnection::TIME_BETWEEN_RETRIES + CommandsHandler::PULL_INTERVAL + 1
34
35
  if subscriptions_set_commands_queries.find_by(subscriptions_set_id: subscriptions_set_id, command_name: cmd_name)
35
36
  # "Ping" command wasn't consumed. Related process must be dead.
36
37
  subscriptions_set_queries.delete(subscriptions_set_id)
@@ -5,6 +5,7 @@ require_relative 'wait_for_subscriptions_set_shutdown'
5
5
 
6
6
  module PgEventstore
7
7
  module CLI
8
+ # @!visibility private
8
9
  class TryUnlockSubscriptionsSet
9
10
  class << self
10
11
  def try_unlock(...)
@@ -2,6 +2,7 @@
2
2
 
3
3
  module PgEventstore
4
4
  module CLI
5
+ # @!visibility private
5
6
  class WaitForSubscriptionsSetShutdown
6
7
  class << self
7
8
  def wait_for_shutdown(...)
@@ -67,9 +67,9 @@ module PgEventstore
67
67
  should_retry = true
68
68
  @pool.with do |conn|
69
69
  yield conn
70
- rescue PG::ConnectionBad
71
- # Recover connection after fork. We do it only once and without any delay. Recover is required by both
72
- # processes - child process and parent process
70
+ rescue PG::ConnectionBad, PG::UnableToSend
71
+ # Recover a connection after fork or when we lost a connection to PostgreSQL. We retry only once and without any
72
+ # delay.
73
73
  conn.sync_reset
74
74
  raise unless should_retry
75
75
  should_retry = false
@@ -77,6 +77,11 @@ module PgEventstore
77
77
  end
78
78
  end
79
79
 
80
+ # @return [void]
81
+ def shutdown
82
+ @pool.shutdown(&:close)
83
+ end
84
+
80
85
  private
81
86
 
82
87
  # @return [ConnectionPool]
@@ -2,6 +2,7 @@
2
2
 
3
3
  module PgEventstore
4
4
  module Extensions
5
+ # @!visibility private
5
6
  module CallbackHandlersExtension
6
7
  def self.included(klass)
7
8
  klass.extend(ClassMethods)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgEventstore
4
+ # @!visibility private
4
5
  class PgConnection < PG::Connection
5
6
  def exec(sql)
6
7
  log(sql, [])
@@ -82,7 +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&.select! do
85
+ event_types = event_types&.select do
86
86
  _1.is_a?(String)
87
87
  end
88
88
  event_types || []
@@ -13,7 +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&.select! do
16
+ event_types = event_types&.select do
17
17
  _1.is_a?(String)
18
18
  end
19
19
  event_types || []
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgEventstore
4
- # Implements simple background jobs runner. The job execution is done via declaring a callback on the specific
5
- # action. The implementation also allows you to hook into different places of life cycle of the runner by defining
6
- # callbacks on various actions. Here is the list of available actions:
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(1, 2)
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 >= 3
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, async_shutdown_time)
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
- @state.halting!
131
- Thread.new do
132
- stopping_at = Time.now.utc
133
- halt = false
134
- loop do
135
- synchronize do
136
- # Give the runner up to @async_shutdown_time seconds for graceful shutdown
137
- @runner&.exit if Time.now.utc - stopping_at > @async_shutdown_time
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
- synchronize do
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
- loop do
206
- Thread.current.exit unless @state.running?
207
- sleep @run_interval
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
- @state.dead!
216
- callbacks.run_callbacks(:after_runner_died, error)
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]
@@ -2,6 +2,7 @@
2
2
 
3
3
  module PgEventstore
4
4
  module CommandHandlers
5
+ # @!visibility private
5
6
  class SubscriptionFeederCommands
6
7
  # @param config_name [Symbol]
7
8
  # @param subscription_feeder [PgEventstore::SubscriptionFeeder]
@@ -2,6 +2,7 @@
2
2
 
3
3
  module PgEventstore
4
4
  module CommandHandlers
5
+ # @!visibility private
5
6
  class SubscriptionRunnersCommands
6
7
  # @param config_name [Symbol]
7
8
  # @param runners [Array<PgEventstore::SubscriptionRunner>]
@@ -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(PULL_INTERVAL, 0)
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
@@ -13,10 +13,15 @@ module PgEventstore
13
13
  # @param handler [#call]
14
14
  # @param graceful_shutdown_timeout [Integer, Float] seconds. Determines how long to wait before force-shutdown
15
15
  # the runner when stopping it using #stop_async
16
- def initialize(handler, graceful_shutdown_timeout:)
16
+ # @param recovery_strategies [Array<PgEventstore::RunnerRecoveryStrategy>]
17
+ def initialize(handler, graceful_shutdown_timeout:, recovery_strategies: [])
17
18
  @handler = handler
18
19
  @raw_events = []
19
- @basic_runner = BasicRunner.new(0, graceful_shutdown_timeout)
20
+ @basic_runner = BasicRunner.new(
21
+ run_interval: 0,
22
+ async_shutdown_time: graceful_shutdown_timeout,
23
+ recovery_strategies: recovery_strategies
24
+ )
20
25
  attach_runner_callbacks
21
26
  end
22
27
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module PgEventstore
4
4
  module Extensions
5
+ # @!visibility private
5
6
  module BaseCommandExtension
6
7
  def self.included(klass)
7
8
  super