pg_eventstore 1.3.4 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/docs/configuration.md +16 -15
  4. data/docs/subscriptions.md +4 -2
  5. data/lib/pg_eventstore/config.rb +3 -0
  6. data/lib/pg_eventstore/extensions/callback_handlers_extension.rb +21 -0
  7. data/lib/pg_eventstore/subscriptions/basic_runner.rb +2 -2
  8. data/lib/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rb +38 -0
  9. data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +45 -0
  10. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +103 -0
  11. data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +75 -0
  12. data/lib/pg_eventstore/subscriptions/commands_handler.rb +13 -29
  13. data/lib/pg_eventstore/subscriptions/events_processor.rb +21 -43
  14. data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +77 -125
  15. data/lib/pg_eventstore/subscriptions/subscription_runner.rb +30 -55
  16. data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +70 -0
  17. data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +14 -3
  18. data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +39 -0
  19. data/lib/pg_eventstore/utils.rb +8 -0
  20. data/lib/pg_eventstore/version.rb +1 -1
  21. data/lib/pg_eventstore/web/application.rb +9 -1
  22. data/lib/pg_eventstore/web/paginator/events_collection.rb +1 -3
  23. data/lib/pg_eventstore/web/paginator/helpers.rb +4 -1
  24. data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +10 -1
  25. data/lib/pg_eventstore/web/subscriptions/helpers.rb +3 -4
  26. data/lib/pg_eventstore/web/views/home/dashboard.erb +11 -0
  27. data/lib/pg_eventstore/web/views/home/partials/events.erb +6 -1
  28. data/lib/pg_eventstore/web/views/subscriptions/index.erb +2 -2
  29. data/lib/pg_eventstore.rb +1 -0
  30. data/sig/interfaces/_raw_event_handler.rbs +3 -0
  31. data/sig/pg_eventstore/config.rbs +2 -0
  32. data/sig/pg_eventstore/extensions/callback_handlers_extension.rbs +11 -0
  33. data/sig/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rbs +10 -0
  34. data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +11 -0
  35. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +31 -0
  36. data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +19 -0
  37. data/sig/pg_eventstore/subscriptions/events_processor.rbs +1 -1
  38. data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +2 -30
  39. data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +27 -0
  40. data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +3 -2
  41. data/sig/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rbs +24 -0
  42. data/sig/pg_eventstore/utils.rbs +2 -0
  43. data/sig/pg_eventstore/web/subscriptions/helpers.rbs +1 -1
  44. metadata +17 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66aeaf1c1365b962133efe784a507722fe0be9c80cb1911edd76512cb7a1af17
4
- data.tar.gz: 34ebc698b212e0631f005f754bbcf808f95796c149f969a1466ede8c7842e362
3
+ metadata.gz: ec41b398e1f38d66e73967447ea4f12896ed2cf57eb0dbad8b71acf4257f9cd4
4
+ data.tar.gz: ce571ef411adeeb18b7bae463cac339a053d96f1b36e3de0b1023b8551b03ac5
5
5
  SHA512:
6
- metadata.gz: ad3c0661aa7c995c357588cbbaa4c9958868068ec452284250f41e0c2e3ae98eabdae837e7c00e63e59f22df7c5920aef2ad1497340d99dcc5bdc8e3e98ffb1f
7
- data.tar.gz: 395e5697dad7945e3c5bc2b51956195097aca503e9e20445b0d3b857fcdb17e4074ec392dc8fdf8a840e132e7f353093985e715a521ef7c73db5b902e0bcbffb
6
+ metadata.gz: 9273a4efb9855d219b1a92b5d9081e3d0d62f9de7f21c48e634949d218ed6a8278d81367f794ee8b70efa64541d7a4442ccf09e7b4cc3bcbd39398f5c7980ec2
7
+ data.tar.gz: ffb85f8c9063aa6e11b8f900e128b4b873e89db117b39e703de6603fc73c014811b63950ff0a0340b0316da11ed2562418f56f1faf42a04491fb0105d913fced
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.5.0]
4
+ - Add ability to toggle link events in the admin UI
5
+ - Mark linked events in the admin UI with "link" icon
6
+
7
+ ## [1.4.0]
8
+ - Add an ability to configure subscription graceful shutdown timeout globally and per subscription. Default value is 15 seconds. Previously it was hardcoded to 5 seconds. Examples:
9
+
10
+ ```ruby
11
+ # Set it globally, for all subscriptions
12
+ PgEventstore.configure do |config|
13
+ config.subscription_graceful_shutdown_timeout = 5
14
+ end
15
+
16
+ # Set it per subscription
17
+ subscriptions_manager.subscribe(
18
+ 'MySubscriptionWithHeavyLiftingTask',
19
+ handler: proc { |event| puts event },
20
+ graceful_shutdown_timeout: 20
21
+ )
22
+ ```
23
+
3
24
  ## [1.3.4]
4
25
  - Fix `NoMethodError` error in `Client#read_paginated` when stream does not exist or when there are no events matching the given filter
5
26
 
@@ -2,21 +2,22 @@
2
2
 
3
3
  Configuration options:
4
4
 
5
- | name | value | default value | description |
6
- |------------------------------------|---------|--------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
7
- | pg_uri | String | `'postgresql://postgres:postgres@localhost:5432/eventstore'` | PostgreSQL connection string. See PostgreSQL [docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS) for more information. |
8
- | max_count | Integer | `1000` | Number of events to return in one response when reading from a stream. |
9
- | middlewares | Array | `{}` | A hash where a key is a name of your middleware and value is an object that respond to `#serialize` and `#deserialize` methods. See [**Writing middleware**](writing_middleware.md) chapter. |
10
- | event_class_resolver | `#call` | `PgEventstore::EventClassResolver.new` | A `#call`-able object that accepts a string and returns an event's class. See **Resolving events classes** chapter bellow for more info. |
11
- | connection_pool_size | Integer | `5` | Max number of connections per ruby process. It must equal the number of threads of your application. When using subscriptions it is recommended to set it to the number of subscriptions divided by two or greater. See [**Picking max connections number**](#picking-max-connections-number) chapter of this section. |
12
- | connection_pool_timeout | Integer | `5` | Time in seconds to wait for a connection in the pool to be released. If no connections are available during this time - `ConnectionPool::TimeoutError` will be raised. See `connection_pool` gem [docs](https://github.com/mperham/connection_pool#usage) for more info. |
13
- | subscription_pull_interval | Float | `1.0` | How often to pull new subscription events in seconds. The minimum meaningful value is `0.2`. Values less than `0.2` will act as it is `0.2`. |
14
- | subscription_max_retries | Integer | `5` | Max number of retries of failed subscription. |
15
- | subscription_retries_interval | Integer | `1` | Interval in seconds between retries of failed subscriptions. |
16
- | subscriptions_set_max_retries | Integer | `10` | Max number of retries for failed subscription sets. |
17
- | subscriptions_set_retries_interval | Integer | `1` | Interval in seconds between retries of failed subscription sets. |
18
- | subscription_restart_terminator | `#call` | `nil` | A callable object that accepts `PgEventstore::Subscription` object to determine whether restarts should be stopped(true - stops restarts, false - continues restarts). |
19
- | failed_subscription_notifier | `#call` | `nil` | A callable object which is invoked with `PgEventstore::Subscription` instance and error instance after the related subscription died due to error and no longer can be automatically restarted due to max retries number reached. You can use this hook to send a notification about failed subscription. |
5
+ | name | value | default value | description |
6
+ |----------------------------------------|----------------|--------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
7
+ | pg_uri | String | `'postgresql://postgres:postgres@localhost:5432/eventstore'` | PostgreSQL connection string. See PostgreSQL [docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS) for more information. |
8
+ | max_count | Integer | `1000` | Number of events to return in one response when reading from a stream. |
9
+ | middlewares | Array | `{}` | A hash where a key is a name of your middleware and value is an object that respond to `#serialize` and `#deserialize` methods. See [**Writing middleware**](writing_middleware.md) chapter. |
10
+ | event_class_resolver | `#call` | `PgEventstore::EventClassResolver.new` | A `#call`-able object that accepts a string and returns an event's class. See **Resolving events classes** chapter bellow for more info. |
11
+ | connection_pool_size | Integer | `5` | Max number of connections per ruby process. It must equal the number of threads of your application. When using subscriptions it is recommended to set it to the number of subscriptions divided by two or greater. See [**Picking max connections number**](#picking-max-connections-number) chapter of this section. |
12
+ | connection_pool_timeout | Integer | `5` | Time in seconds to wait for a connection in the pool to be released. If no connections are available during this time - `ConnectionPool::TimeoutError` will be raised. See `connection_pool` gem [docs](https://github.com/mperham/connection_pool#usage) for more info. |
13
+ | subscription_pull_interval | Float | `1.0` | How often to pull new subscription events in seconds. The minimum meaningful value is `0.2`. Values less than `0.2` will act as it is `0.2`. |
14
+ | subscription_max_retries | Integer | `5` | Max number of retries of failed subscription. |
15
+ | subscription_retries_interval | Integer | `1` | Interval in seconds between retries of failed subscriptions. |
16
+ | subscriptions_set_max_retries | Integer | `10` | Max number of retries for failed subscription sets. |
17
+ | subscriptions_set_retries_interval | Integer | `1` | Interval in seconds between retries of failed subscription sets. |
18
+ | subscription_restart_terminator | `#call` | `nil` | A callable object that accepts `PgEventstore::Subscription` object to determine whether restarts should be stopped(true - stops restarts, false - continues restarts). |
19
+ | failed_subscription_notifier | `#call` | `nil` | A callable object which is invoked with `PgEventstore::Subscription` instance and error instance after the related subscription died due to error and no longer can be automatically restarted due to max retries number reached. You can use this hook to send a notification about failed subscription. |
20
+ | subscription_graceful_shutdown_timeout | Integer, Float | `15` | The number of seconds to wait until force-shutdown the subscription during the stop process. If your subscription handler does not finish current event processing during this time(for example because of heavy-lifting task) - it will be force-shutdown. |
20
21
 
21
22
  ## Multiple configurations
22
23
 
@@ -151,7 +151,7 @@ You will then see the output of your subscription handlers. To gracefully stop t
151
151
 
152
152
  ## Overriding Subscription config values
153
153
 
154
- You can override `subscription_pull_interval`, `subscription_max_retries`, `subscription_retries_interval`, `subscription_restart_terminator` and `failed_subscription_notifier` config values (see [**Configuration**](configuration.md) chapter for details) for the specific subscription by providing the corresponding arguments. Example:
154
+ You can override `subscription_pull_interval`, `subscription_max_retries`, `subscription_retries_interval`, `subscription_restart_terminator`, `failed_subscription_notifier` and `subscription_graceful_shutdown_timeout` config values (see [**Configuration**](configuration.md) chapter for details) for the specific subscription by providing the corresponding arguments. Example:
155
155
 
156
156
  ```ruby
157
157
  subscriptions_manager.subscribe(
@@ -166,7 +166,9 @@ subscriptions_manager.subscribe(
166
166
  # overrides config.subscription_restart_terminator
167
167
  restart_terminator: proc { |subscription| subscription.last_error['class'] == 'NoMethodError' },
168
168
  # overrides config.failed_subscription_notifier
169
- failed_subscription_notifier: proc { |_subscription, err| p err }
169
+ failed_subscription_notifier: proc { |_subscription, err| p err },
170
+ # overrides config.subscription_graceful_shutdown_timeout
171
+ graceful_shutdown_timeout: 20
170
172
  )
171
173
  ```
172
174
 
@@ -52,6 +52,9 @@ module PgEventstore
52
52
  # @return [#call, nil] provide callable object that accepts Subscription instance and error. It is useful when you
53
53
  # want to be get notified when your Subscription fails and no longer can be restarted
54
54
  option(:failed_subscription_notifier)
55
+ # @!attribute subscription_graceful_shutdown_timeout
56
+ # @return [Integer] the number of seconds to wait until force-shutdown the subscription during the stop process
57
+ option(:subscription_graceful_shutdown_timeout) { 15 }
55
58
 
56
59
  # @param name [Symbol] config's name. Its value matches the appropriate key in PgEventstore.config hash
57
60
  def initialize(name:, **options)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ module Extensions
5
+ module CallbackHandlersExtension
6
+ def self.included(klass)
7
+ klass.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ # @param name [Symbol] a name of the handler
12
+ # @return [Proc]
13
+ def setup_handler(name, *args)
14
+ proc do |*rest|
15
+ public_send(name, *args, *rest)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -83,8 +83,8 @@ module PgEventstore
83
83
 
84
84
  # @param run_interval [Integer, Float] seconds. Determines how often to run async task. Async task is determined by
85
85
  # :after_runner_stopped callback
86
- # @param async_shutdown_time [Integer, Float] seconds. Determines how long to wait for the async shutdown to wait
87
- # for the runner to finish.
86
+ # @param async_shutdown_time [Integer, Float] seconds. Determines how long to wait before force-shutdown the runner.
87
+ # It is only meaningful for the #stop_async
88
88
  def initialize(run_interval, async_shutdown_time)
89
89
  @run_interval = run_interval
90
90
  @async_shutdown_time = async_shutdown_time
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ class CommandsHandlerHandlers
5
+ include Extensions::CallbackHandlersExtension
6
+
7
+ class << self
8
+ # @param config_name [Symbol]
9
+ # @param subscription_feeder [PgEventstore::SubscriptionFeeder]
10
+ # @return [void]
11
+ def process_feeder_commands(config_name, subscription_feeder)
12
+ CommandHandlers::SubscriptionFeederCommands.new(config_name, subscription_feeder).process
13
+ end
14
+
15
+ # @param config_name [Symbol]
16
+ # @param runners [Array<PgEventstore::SubscriptionRunner>]
17
+ # @param subscription_feeder [PgEventstore::SubscriptionFeeder]
18
+ # @return [void]
19
+ def process_runners_commands(config_name, runners, subscription_feeder)
20
+ CommandHandlers::SubscriptionRunnersCommands.new(config_name, runners, subscription_feeder.id).process
21
+ 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
+ end
37
+ end
38
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ class EventsProcessorHandlers
5
+ include Extensions::CallbackHandlersExtension
6
+
7
+ class << self
8
+ # @param callbacks [PgEventstore::Callbacks]
9
+ # @param handler [#call]
10
+ # @param raw_events [Array<Hash>]
11
+ # @return [void]
12
+ def process_event(callbacks, handler, raw_events)
13
+ raw_event = raw_events.shift
14
+ return sleep 0.5 if raw_event.nil?
15
+
16
+ callbacks.run_callbacks(:process, Utils.original_global_position(raw_event)) do
17
+ handler.call(raw_event)
18
+ end
19
+ rescue
20
+ raw_events.unshift(raw_event)
21
+ raise
22
+ end
23
+
24
+ # @param callbacks [PgEventstore::Callbacks]
25
+ # @param error [StandardError]
26
+ # @return [void]
27
+ def after_runner_died(callbacks, error)
28
+ callbacks.run_callbacks(:error, error)
29
+ end
30
+
31
+ # @param callbacks [PgEventstore::Callbacks]
32
+ # @return [void]
33
+ def before_runner_restored(callbacks)
34
+ callbacks.run_callbacks(:restart)
35
+ end
36
+
37
+ # @param callbacks [PgEventstore::Callbacks]
38
+ # @param state [String]
39
+ # @return [void]
40
+ def change_state(callbacks, state)
41
+ callbacks.run_callbacks(:change_state, state)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ class SubscriptionFeederHandlers
5
+ include Extensions::CallbackHandlersExtension
6
+
7
+ class << self
8
+ # @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
9
+ # @param state [String]
10
+ # @return [void]
11
+ def update_subscriptions_set_state(subscriptions_set_lifecycle, state)
12
+ subscriptions_set_lifecycle.persisted_subscriptions_set.update(state: state)
13
+ end
14
+
15
+ # @param subscriptions_lifecycle [PgEventstore::SubscriptionsLifecycle]
16
+ # @return [void]
17
+ def lock_subscriptions(subscriptions_lifecycle)
18
+ subscriptions_lifecycle.lock_all
19
+ end
20
+
21
+ # @param subscriptions_lifecycle [PgEventstore::SubscriptionsLifecycle]
22
+ # @return [void]
23
+ def start_runners(subscriptions_lifecycle)
24
+ subscriptions_lifecycle.runners.each(&:start)
25
+ end
26
+
27
+ # @param cmds_handler [PgEventstore::CommandsHandler]
28
+ # @return [void]
29
+ def start_cmds_handler(cmds_handler)
30
+ cmds_handler.start
31
+ end
32
+
33
+ # @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
34
+ # @param error [StandardError]
35
+ # @return [void]
36
+ def persist_error_info(subscriptions_set_lifecycle, error)
37
+ subscriptions_set_lifecycle.persisted_subscriptions_set.update(
38
+ last_error: Utils.error_info(error), last_error_occurred_at: Time.now.utc
39
+ )
40
+ end
41
+
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
+ # @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
57
+ # @return [void]
58
+ def ping_subscriptions_set(subscriptions_set_lifecycle)
59
+ subscriptions_set_lifecycle.ping_subscriptions_set
60
+ end
61
+
62
+ # @param subscriptions_lifecycle [PgEventstore::SubscriptionsLifecycle]
63
+ # @param config_name [Symbol]
64
+ # @return [void]
65
+ def feed_runners(subscriptions_lifecycle, config_name)
66
+ SubscriptionRunnersFeeder.new(config_name).feed(subscriptions_lifecycle.runners)
67
+ end
68
+
69
+ # @param subscriptions_lifecycle [PgEventstore::SubscriptionsLifecycle]
70
+ # @return [void]
71
+ def ping_subscriptions(subscriptions_lifecycle)
72
+ subscriptions_lifecycle.ping_subscriptions
73
+ end
74
+
75
+ # @param subscriptions_lifecycle [PgEventstore::SubscriptionsLifecycle]
76
+ # @return [void]
77
+ def stop_runners(subscriptions_lifecycle)
78
+ subscriptions_lifecycle.runners.each(&:stop_async).each(&:wait_for_finish)
79
+ end
80
+
81
+ # @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
82
+ # @return [void]
83
+ def reset_subscriptions_set(subscriptions_set_lifecycle)
84
+ subscriptions_set_lifecycle.reset_subscriptions_set
85
+ end
86
+
87
+ # @param cmds_handler [PgEventstore::CommandsHandler]
88
+ # @return [void]
89
+ def stop_commands_handler(cmds_handler)
90
+ cmds_handler.stop
91
+ end
92
+
93
+ # @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
94
+ # @return [void]
95
+ def update_subscriptions_set_restarts(subscriptions_set_lifecycle)
96
+ subscriptions_set = subscriptions_set_lifecycle.persisted_subscriptions_set
97
+ subscriptions_set.update(
98
+ last_restarted_at: Time.now.utc, restart_count: subscriptions_set.restart_count + 1
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgEventstore
4
+ class SubscriptionRunnerHandlers
5
+ include Extensions::CallbackHandlersExtension
6
+
7
+ class << self
8
+ # @param stats [PgEventstore::SubscriptionHandlerPerformance]
9
+ # @param action [Proc]
10
+ # @param _current_position [Integer]
11
+ # @return [void]
12
+ def track_exec_time(stats, action, _current_position)
13
+ stats.track_exec_time { action.call }
14
+ end
15
+
16
+ # @param subscription [PgEventstore::Subscription]
17
+ # @param stats [PgEventstore::SubscriptionHandlerPerformance]
18
+ # @param current_position [Integer]
19
+ # @return [void]
20
+ def update_subscription_stats(subscription, stats, current_position)
21
+ subscription.update(
22
+ average_event_processing_time: stats.average_event_processing_time,
23
+ current_position: current_position,
24
+ total_processed_events: subscription.total_processed_events + 1
25
+ )
26
+ end
27
+
28
+ # @param subscription [PgEventstore::Subscription]
29
+ # @param error [StandardError]
30
+ # @return [void]
31
+ def update_subscription_error(subscription, error)
32
+ subscription.update(last_error: Utils.error_info(error), last_error_occurred_at: Time.now.utc)
33
+ end
34
+
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 [StandardError]
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, error)
46
+ end
47
+
48
+ Thread.new do
49
+ sleep subscription.time_between_restarts
50
+ events_processor.restore
51
+ end
52
+ end
53
+
54
+ # @param subscription [PgEventstore::Subscription]
55
+ # @param global_position [Integer]
56
+ # @return [void]
57
+ def update_subscription_chunk_stats(subscription, global_position)
58
+ subscription.update(last_chunk_fed_at: Time.now.utc, last_chunk_greatest_position: global_position)
59
+ end
60
+
61
+ # @param subscription [PgEventstore::Subscription]
62
+ # @return [void]
63
+ def update_subscription_restarts(subscription)
64
+ subscription.update(last_restarted_at: Time.now.utc, restart_count: subscription.restart_count + 1)
65
+ end
66
+
67
+ # @param subscription [PgEventstore::Subscription]
68
+ # @param state [String]
69
+ # @return [void]
70
+ def update_subscription_state(subscription, state)
71
+ subscription.update(state: state)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -31,35 +31,19 @@ module PgEventstore
31
31
  private
32
32
 
33
33
  def attach_runner_callbacks
34
- @basic_runner.define_callback(:process_async, :before, method(:process_async))
35
- @basic_runner.define_callback(:after_runner_died, :before, method(:after_runner_died))
36
- end
37
-
38
- def process_async
39
- subscription_feeder_commands.process
40
- subscription_runners_commands.process
41
- end
42
-
43
- # @param error [StandardError]
44
- # @return [void]
45
- def after_runner_died(error)
46
- PgEventstore.logger&.error "#{self.class.name}: Error occurred: #{error.message}"
47
- PgEventstore.logger&.error "#{self.class.name}: Backtrace: #{error.backtrace&.join("\n")}"
48
- PgEventstore.logger&.error "#{self.class.name}: Trying to auto-repair in #{RESTART_DELAY} seconds..."
49
- Thread.new do
50
- sleep RESTART_DELAY
51
- @basic_runner.restore
52
- end
53
- end
54
-
55
- # @return [PgEventstore::CommandHandlers::SubscriptionFeederCommands]
56
- def subscription_feeder_commands
57
- CommandHandlers::SubscriptionFeederCommands.new(@config_name, @subscription_feeder)
58
- end
59
-
60
- # @return [PgEventstore::CommandHandlers::SubscriptionRunnersCommands]
61
- def subscription_runners_commands
62
- CommandHandlers::SubscriptionRunnersCommands.new(@config_name, @runners, @subscription_feeder.id)
34
+ @basic_runner.define_callback(
35
+ :process_async, :before,
36
+ CommandsHandlerHandlers.setup_handler(:process_feeder_commands, @config_name, @subscription_feeder)
37
+ )
38
+ @basic_runner.define_callback(
39
+ :process_async, :before,
40
+ CommandsHandlerHandlers.setup_handler(:process_runners_commands, @config_name, @runners, @subscription_feeder)
41
+ )
42
+
43
+ @basic_runner.define_callback(
44
+ :after_runner_died, :before,
45
+ CommandsHandlerHandlers.setup_handler(:restore_runner, @basic_runner, RESTART_DELAY)
46
+ )
63
47
  end
64
48
  end
65
49
  end
@@ -11,10 +11,12 @@ module PgEventstore
11
11
  :within_state
12
12
 
13
13
  # @param handler [#call]
14
- def initialize(handler)
14
+ # @param graceful_shutdown_timeout [Integer, Float] seconds. Determines how long to wait before force-shutdown
15
+ # the runner when stopping it using #stop_async
16
+ def initialize(handler, graceful_shutdown_timeout:)
15
17
  @handler = handler
16
18
  @raw_events = []
17
- @basic_runner = BasicRunner.new(0, 5)
19
+ @basic_runner = BasicRunner.new(0, graceful_shutdown_timeout)
18
20
  attach_runner_callbacks
19
21
  end
20
22
 
@@ -24,7 +26,7 @@ module PgEventstore
24
26
  raise EmptyChunkFedError.new("Empty chunk was fed!") if raw_events.empty?
25
27
 
26
28
  within_state(:running) do
27
- callbacks.run_callbacks(:feed, global_position(raw_events.last))
29
+ callbacks.run_callbacks(:feed, Utils.original_global_position(raw_events.last))
28
30
  @raw_events.push(*raw_events)
29
31
  end
30
32
  end
@@ -42,47 +44,23 @@ module PgEventstore
42
44
 
43
45
  private
44
46
 
45
- # @param raw_event [Hash]
46
- # @return [void]
47
- def process_event(raw_event)
48
- callbacks.run_callbacks(:process, global_position(raw_event)) do
49
- @handler.call(raw_event)
50
- end
51
- end
52
-
53
47
  def attach_runner_callbacks
54
- @basic_runner.define_callback(:process_async, :before, method(:process_async))
55
- @basic_runner.define_callback(:after_runner_died, :before, method(:after_runner_died))
56
- @basic_runner.define_callback(:before_runner_restored, :before, method(:before_runner_restored))
57
- @basic_runner.define_callback(:change_state, :before, method(:change_state))
58
- end
59
-
60
- def process_async
61
- raw_event = @raw_events.shift
62
- return sleep 0.5 if raw_event.nil?
63
-
64
- process_event(raw_event)
65
- rescue
66
- @raw_events.unshift(raw_event)
67
- raise
68
- end
69
-
70
- def after_runner_died(...)
71
- callbacks.run_callbacks(:error, ...)
72
- end
73
-
74
- def before_runner_restored
75
- callbacks.run_callbacks(:restart)
76
- end
77
-
78
- def change_state(...)
79
- callbacks.run_callbacks(:change_state, ...)
80
- end
81
-
82
- # @param raw_event [Hash]
83
- # @return [Integer]
84
- def global_position(raw_event)
85
- raw_event['link'] ? raw_event['link']['global_position'] : raw_event['global_position']
48
+ @basic_runner.define_callback(
49
+ :process_async, :before,
50
+ EventsProcessorHandlers.setup_handler(:process_event, @callbacks, @handler, @raw_events)
51
+ )
52
+
53
+ @basic_runner.define_callback(
54
+ :after_runner_died, :before, EventsProcessorHandlers.setup_handler(:after_runner_died, callbacks)
55
+ )
56
+
57
+ @basic_runner.define_callback(
58
+ :before_runner_restored, :before, EventsProcessorHandlers.setup_handler(:before_runner_restored, callbacks)
59
+ )
60
+
61
+ @basic_runner.define_callback(
62
+ :change_state, :before, EventsProcessorHandlers.setup_handler(:change_state, callbacks)
63
+ )
86
64
  end
87
65
  end
88
66
  end