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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/docs/configuration.md +16 -15
- data/docs/subscriptions.md +4 -2
- data/lib/pg_eventstore/config.rb +3 -0
- data/lib/pg_eventstore/extensions/callback_handlers_extension.rb +21 -0
- data/lib/pg_eventstore/subscriptions/basic_runner.rb +2 -2
- data/lib/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rb +38 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +45 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rb +103 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +75 -0
- data/lib/pg_eventstore/subscriptions/commands_handler.rb +13 -29
- data/lib/pg_eventstore/subscriptions/events_processor.rb +21 -43
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +77 -125
- data/lib/pg_eventstore/subscriptions/subscription_runner.rb +30 -55
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +70 -0
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +14 -3
- data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +39 -0
- data/lib/pg_eventstore/utils.rb +8 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +9 -1
- data/lib/pg_eventstore/web/paginator/events_collection.rb +1 -3
- data/lib/pg_eventstore/web/paginator/helpers.rb +4 -1
- data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +10 -1
- data/lib/pg_eventstore/web/subscriptions/helpers.rb +3 -4
- data/lib/pg_eventstore/web/views/home/dashboard.erb +11 -0
- data/lib/pg_eventstore/web/views/home/partials/events.erb +6 -1
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +2 -2
- data/lib/pg_eventstore.rb +1 -0
- data/sig/interfaces/_raw_event_handler.rbs +3 -0
- data/sig/pg_eventstore/config.rbs +2 -0
- data/sig/pg_eventstore/extensions/callback_handlers_extension.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/commands_handler_handlers.rbs +10 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_feeder_handlers.rbs +31 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +19 -0
- data/sig/pg_eventstore/subscriptions/events_processor.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +2 -30
- data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +27 -0
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +3 -2
- data/sig/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rbs +24 -0
- data/sig/pg_eventstore/utils.rbs +2 -0
- data/sig/pg_eventstore/web/subscriptions/helpers.rbs +1 -1
- metadata +17 -2
@@ -6,11 +6,8 @@ module PgEventstore
|
|
6
6
|
class SubscriptionFeeder
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
# @return [Integer] number of seconds between heartbeat updates
|
10
|
-
HEARTBEAT_INTERVAL = 10 # seconds
|
11
|
-
|
12
|
-
def_delegators :subscriptions_set, :id
|
13
9
|
def_delegators :@basic_runner, :start, :stop, :restore, :state, :wait_for_finish, :stop_async
|
10
|
+
def_delegators :@subscriptions_lifecycle, :force_lock!
|
14
11
|
|
15
12
|
# @param config_name [Symbol]
|
16
13
|
# @param set_name [String]
|
@@ -18,23 +15,27 @@ module PgEventstore
|
|
18
15
|
# @param retries_interval [Integer] a delay between retries of failed SubscriptionsSet
|
19
16
|
def initialize(config_name:, set_name:, max_retries:, retries_interval:)
|
20
17
|
@config_name = config_name
|
21
|
-
@runners = []
|
22
|
-
@subscriptions_set_attrs = {
|
23
|
-
name: set_name, max_restarts_number: max_retries, time_between_restarts: retries_interval
|
24
|
-
}
|
25
|
-
@commands_handler = CommandsHandler.new(@config_name, self, @runners)
|
26
18
|
@basic_runner = BasicRunner.new(0.2, 0)
|
27
|
-
@
|
28
|
-
|
19
|
+
@subscriptions_set_lifecycle = SubscriptionsSetLifecycle.new(
|
20
|
+
@config_name,
|
21
|
+
{ name: set_name, max_restarts_number: max_retries, time_between_restarts: retries_interval }
|
22
|
+
)
|
23
|
+
@subscriptions_lifecycle = SubscriptionsLifecycle.new(@config_name, @subscriptions_set_lifecycle)
|
24
|
+
@commands_handler = CommandsHandler.new(@config_name, self, @subscriptions_lifecycle.runners)
|
29
25
|
attach_runner_callbacks
|
30
26
|
end
|
31
27
|
|
28
|
+
# @return [Integer]
|
29
|
+
def id
|
30
|
+
@subscriptions_set_lifecycle.persisted_subscriptions_set.id
|
31
|
+
end
|
32
|
+
|
32
33
|
# Adds SubscriptionRunner to the set
|
33
34
|
# @param runner [PgEventstore::SubscriptionRunner]
|
34
35
|
# @return [PgEventstore::SubscriptionRunner]
|
35
36
|
def add(runner)
|
36
37
|
assert_proper_state!
|
37
|
-
@runners.push(runner)
|
38
|
+
@subscriptions_lifecycle.runners.push(runner)
|
38
39
|
runner
|
39
40
|
end
|
40
41
|
|
@@ -43,7 +44,7 @@ module PgEventstore
|
|
43
44
|
def start_all
|
44
45
|
return self unless @basic_runner.running?
|
45
46
|
|
46
|
-
@runners.each(&:start)
|
47
|
+
@subscriptions_lifecycle.runners.each(&:start)
|
47
48
|
self
|
48
49
|
end
|
49
50
|
|
@@ -52,135 +53,85 @@ module PgEventstore
|
|
52
53
|
def stop_all
|
53
54
|
return self unless @basic_runner.running?
|
54
55
|
|
55
|
-
@runners.each(&:stop_async)
|
56
|
+
@subscriptions_lifecycle.runners.each(&:stop_async)
|
56
57
|
self
|
57
58
|
end
|
58
59
|
|
59
|
-
# Sets the force_lock flash to true. If set - all related Subscriptions will ignore their lock state and will be
|
60
|
-
# locked by the new SubscriptionsSet.
|
61
|
-
# @return [void]
|
62
|
-
def force_lock!
|
63
|
-
@force_lock = true
|
64
|
-
end
|
65
|
-
|
66
60
|
# Produces a copy of currently running Subscriptions. This is needed, because original Subscriptions objects are
|
67
61
|
# dangerous to use - users may incidentally break their state.
|
68
62
|
# @return [Array<PgEventstore::Subscription>]
|
69
63
|
def read_only_subscriptions
|
70
|
-
@
|
64
|
+
@subscriptions_lifecycle.subscriptions.map(&:dup)
|
71
65
|
end
|
72
66
|
|
73
67
|
# Produces a copy of current SubscriptionsSet. This is needed, because original SubscriptionsSet object is
|
74
68
|
# dangerous to use - users may incidentally break its state.
|
75
69
|
# @return [PgEventstore::SubscriptionsSet, nil]
|
76
70
|
def read_only_subscriptions_set
|
77
|
-
@subscriptions_set&.dup
|
71
|
+
@subscriptions_set_lifecycle.subscriptions_set&.dup
|
78
72
|
end
|
79
73
|
|
80
74
|
private
|
81
75
|
|
82
|
-
# Locks all Subscriptions behind the current SubscriptionsSet
|
83
|
-
# @return [void]
|
84
|
-
def lock_all
|
85
|
-
@runners.each { |runner| runner.lock!(subscriptions_set.id, force: @force_lock) }
|
86
|
-
end
|
87
|
-
|
88
|
-
# @return [PgEventstore::SubscriptionsSet]
|
89
|
-
def subscriptions_set
|
90
|
-
@subscriptions_set ||= SubscriptionsSet.using_connection(@config_name).create(**@subscriptions_set_attrs)
|
91
|
-
end
|
92
|
-
|
93
|
-
# @return [PgEventstore::SubscriptionRunnersFeeder]
|
94
|
-
def feeder
|
95
|
-
SubscriptionRunnersFeeder.new(@config_name)
|
96
|
-
end
|
97
|
-
|
98
76
|
# @return [void]
|
99
77
|
def attach_runner_callbacks
|
100
|
-
@basic_runner.define_callback(
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@basic_runner.define_callback(
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
@
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
def ping_subscriptions
|
158
|
-
return if @subscriptions_pinged_at > Time.now.utc - HEARTBEAT_INTERVAL
|
159
|
-
|
160
|
-
runners = @runners.select do |runner|
|
161
|
-
next false unless runner.running?
|
162
|
-
|
163
|
-
runner.subscription.updated_at < Time.now.utc - HEARTBEAT_INTERVAL
|
164
|
-
end
|
165
|
-
unless runners.empty?
|
166
|
-
Subscription.using_connection(@config_name).ping_all(subscriptions_set.id, runners.map(&:subscription))
|
167
|
-
end
|
168
|
-
|
169
|
-
@subscriptions_pinged_at = Time.now.utc
|
170
|
-
end
|
171
|
-
|
172
|
-
# @return [void]
|
173
|
-
def after_runner_stopped
|
174
|
-
@runners.each(&:stop_async).each(&:wait_for_finish)
|
175
|
-
@subscriptions_set&.delete
|
176
|
-
@subscriptions_set = nil
|
177
|
-
@commands_handler.stop
|
178
|
-
end
|
179
|
-
|
180
|
-
# @param state [String]
|
181
|
-
# @return [void]
|
182
|
-
def update_subscriptions_set_state(state)
|
183
|
-
subscriptions_set.update(state: state)
|
78
|
+
@basic_runner.define_callback(
|
79
|
+
:change_state, :after,
|
80
|
+
SubscriptionFeederHandlers.setup_handler(:update_subscriptions_set_state, @subscriptions_set_lifecycle)
|
81
|
+
)
|
82
|
+
|
83
|
+
@basic_runner.define_callback(
|
84
|
+
:before_runner_started, :before,
|
85
|
+
SubscriptionFeederHandlers.setup_handler(:lock_subscriptions, @subscriptions_lifecycle)
|
86
|
+
)
|
87
|
+
@basic_runner.define_callback(
|
88
|
+
:before_runner_started, :before,
|
89
|
+
SubscriptionFeederHandlers.setup_handler(:start_runners, @subscriptions_lifecycle)
|
90
|
+
)
|
91
|
+
@basic_runner.define_callback(
|
92
|
+
:before_runner_started, :before,
|
93
|
+
SubscriptionFeederHandlers.setup_handler(:start_cmds_handler, @commands_handler)
|
94
|
+
)
|
95
|
+
|
96
|
+
@basic_runner.define_callback(
|
97
|
+
:after_runner_died, :before,
|
98
|
+
SubscriptionFeederHandlers.setup_handler(:persist_error_info, @subscriptions_set_lifecycle)
|
99
|
+
)
|
100
|
+
@basic_runner.define_callback(
|
101
|
+
:after_runner_died, :before,
|
102
|
+
SubscriptionFeederHandlers.setup_handler(:restart_runner, @subscriptions_set_lifecycle, @basic_runner)
|
103
|
+
)
|
104
|
+
|
105
|
+
@basic_runner.define_callback(
|
106
|
+
:process_async, :before,
|
107
|
+
SubscriptionFeederHandlers.setup_handler(:ping_subscriptions_set, @subscriptions_set_lifecycle)
|
108
|
+
)
|
109
|
+
@basic_runner.define_callback(
|
110
|
+
:process_async, :before,
|
111
|
+
SubscriptionFeederHandlers.setup_handler(:feed_runners, @subscriptions_lifecycle, @config_name)
|
112
|
+
)
|
113
|
+
@basic_runner.define_callback(
|
114
|
+
:process_async, :after,
|
115
|
+
SubscriptionFeederHandlers.setup_handler(:ping_subscriptions, @subscriptions_lifecycle)
|
116
|
+
)
|
117
|
+
|
118
|
+
@basic_runner.define_callback(
|
119
|
+
:after_runner_stopped, :before,
|
120
|
+
SubscriptionFeederHandlers.setup_handler(:stop_runners, @subscriptions_lifecycle)
|
121
|
+
)
|
122
|
+
@basic_runner.define_callback(
|
123
|
+
:after_runner_stopped, :before,
|
124
|
+
SubscriptionFeederHandlers.setup_handler(:reset_subscriptions_set, @subscriptions_set_lifecycle)
|
125
|
+
)
|
126
|
+
@basic_runner.define_callback(
|
127
|
+
:after_runner_stopped, :before,
|
128
|
+
SubscriptionFeederHandlers.setup_handler(:stop_commands_handler, @commands_handler)
|
129
|
+
)
|
130
|
+
|
131
|
+
@basic_runner.define_callback(
|
132
|
+
:before_runner_restored, :after,
|
133
|
+
SubscriptionFeederHandlers.setup_handler(:update_subscriptions_set_restarts, @subscriptions_set_lifecycle)
|
134
|
+
)
|
184
135
|
end
|
185
136
|
|
186
137
|
# This method helps to ensure that no Subscription is added after SubscriptionFeeder's runner is working
|
@@ -188,10 +139,11 @@ module PgEventstore
|
|
188
139
|
# @raise [RuntimeError]
|
189
140
|
def assert_proper_state!
|
190
141
|
return if @basic_runner.initial? || @basic_runner.stopped?
|
142
|
+
subscriptions_set = @subscriptions_set_lifecycle.persisted_subscriptions_set
|
191
143
|
|
192
144
|
error_message = <<~TEXT
|
193
|
-
Could not add subscription - #{subscriptions_set.name}##{subscriptions_set.id} must be
|
194
|
-
or in the stopped state, but it is in the #{@basic_runner.state} state now.
|
145
|
+
Could not add subscription - #{subscriptions_set.name}##{subscriptions_set.id} must be \
|
146
|
+
either in the initial or in the stopped state, but it is in the #{@basic_runner.state} state now.
|
195
147
|
TEXT
|
196
148
|
raise error_message
|
197
149
|
end
|
@@ -74,66 +74,41 @@ module PgEventstore
|
|
74
74
|
|
75
75
|
# @return [void]
|
76
76
|
def attach_callbacks
|
77
|
-
@events_processor.define_callback(
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@events_processor.define_callback(
|
82
|
-
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
# @param action [Proc]
|
87
|
-
# @return [void]
|
88
|
-
def track_exec_time(action, *)
|
89
|
-
@stats.track_exec_time { action.call }
|
90
|
-
end
|
91
|
-
|
92
|
-
# @param current_position [Integer]
|
93
|
-
# @return [void]
|
94
|
-
def update_subscription_stats(current_position)
|
95
|
-
@subscription.update(
|
96
|
-
average_event_processing_time: @stats.average_event_processing_time,
|
97
|
-
current_position: current_position,
|
98
|
-
total_processed_events: @subscription.total_processed_events + 1
|
77
|
+
@events_processor.define_callback(
|
78
|
+
:process, :around,
|
79
|
+
SubscriptionRunnerHandlers.setup_handler(:track_exec_time, @stats)
|
80
|
+
)
|
81
|
+
@events_processor.define_callback(
|
82
|
+
:process, :after,
|
83
|
+
SubscriptionRunnerHandlers.setup_handler(:update_subscription_stats, @subscription, @stats)
|
99
84
|
)
|
100
|
-
end
|
101
|
-
|
102
|
-
# @param state [String]
|
103
|
-
# @return [void]
|
104
|
-
def update_subscription_state(state)
|
105
|
-
@subscription.update(state: state)
|
106
|
-
end
|
107
85
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
86
|
+
@events_processor.define_callback(
|
87
|
+
:error, :after,
|
88
|
+
SubscriptionRunnerHandlers.setup_handler(:update_subscription_error, @subscription)
|
89
|
+
)
|
90
|
+
@events_processor.define_callback(
|
91
|
+
:error, :after,
|
92
|
+
SubscriptionRunnerHandlers.setup_handler(
|
93
|
+
:restart_events_processor,
|
94
|
+
@subscription, @restart_terminator, @failed_subscription_notifier, @events_processor
|
95
|
+
)
|
96
|
+
)
|
112
97
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
98
|
+
@events_processor.define_callback(
|
99
|
+
:feed, :after,
|
100
|
+
SubscriptionRunnerHandlers.setup_handler(:update_subscription_chunk_stats, @subscription)
|
101
|
+
)
|
118
102
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
end
|
103
|
+
@events_processor.define_callback(
|
104
|
+
:restart, :after,
|
105
|
+
SubscriptionRunnerHandlers.setup_handler(:update_subscription_restarts, @subscription)
|
106
|
+
)
|
124
107
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
if @subscription.restart_count >= @subscription.max_restarts_number
|
130
|
-
return @failed_subscription_notifier&.call(@subscription.dup, error)
|
131
|
-
end
|
132
|
-
|
133
|
-
Thread.new do
|
134
|
-
sleep @subscription.time_between_restarts
|
135
|
-
restore
|
136
|
-
end
|
108
|
+
@events_processor.define_callback(
|
109
|
+
:change_state, :after,
|
110
|
+
SubscriptionRunnerHandlers.setup_handler(:update_subscription_state, @subscription)
|
111
|
+
)
|
137
112
|
end
|
138
113
|
end
|
139
114
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
class SubscriptionsLifecycle
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
# @return [Integer] number of seconds between heartbeat updates
|
8
|
+
HEARTBEAT_INTERVAL = 10 # seconds
|
9
|
+
|
10
|
+
# @!attribute runners
|
11
|
+
# @return [Array<PgEventstore::SubscriptionRunner>]
|
12
|
+
attr_reader :runners
|
13
|
+
|
14
|
+
# @param config_name [Symbol]
|
15
|
+
# @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
|
16
|
+
def initialize(config_name, subscriptions_set_lifecycle)
|
17
|
+
@config_name = config_name
|
18
|
+
@subscriptions_set_lifecycle = subscriptions_set_lifecycle
|
19
|
+
@runners = []
|
20
|
+
@subscriptions_pinged_at = Time.at(0)
|
21
|
+
@force_lock = false
|
22
|
+
end
|
23
|
+
|
24
|
+
# Locks all Subscriptions behind the current SubscriptionsSet
|
25
|
+
# @return [void]
|
26
|
+
def lock_all
|
27
|
+
@runners.each do |runner|
|
28
|
+
runner.lock!(@subscriptions_set_lifecycle.persisted_subscriptions_set.id, force: force_locked?)
|
29
|
+
end
|
30
|
+
rescue PgEventstore::SubscriptionAlreadyLockedError
|
31
|
+
@subscriptions_set_lifecycle.reset_subscriptions_set
|
32
|
+
raise
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [void]
|
36
|
+
def ping_subscriptions
|
37
|
+
return if @subscriptions_pinged_at > Time.now.utc - HEARTBEAT_INTERVAL
|
38
|
+
|
39
|
+
runners = @runners.select do |runner|
|
40
|
+
next false unless runner.running?
|
41
|
+
|
42
|
+
runner.subscription.updated_at < Time.now.utc - HEARTBEAT_INTERVAL
|
43
|
+
end
|
44
|
+
unless runners.empty?
|
45
|
+
Subscription.using_connection(@config_name).ping_all(
|
46
|
+
@subscriptions_set_lifecycle.persisted_subscriptions_set.id, runners.map(&:subscription)
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
@subscriptions_pinged_at = Time.now.utc
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Array<PgEventstore::Subscription>]
|
54
|
+
def subscriptions
|
55
|
+
@runners.map(&:subscription)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sets the force_lock flag to true. If set - all related Subscriptions will ignore their lock state and will be
|
59
|
+
# locked by the new SubscriptionsSet.
|
60
|
+
# @return [void]
|
61
|
+
def force_lock!
|
62
|
+
@force_lock = true
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Boolean]
|
66
|
+
def force_locked?
|
67
|
+
@force_lock
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -9,6 +9,12 @@ require_relative 'subscription_handler_performance'
|
|
9
9
|
require_relative 'subscription_runner'
|
10
10
|
require_relative 'subscriptions_set'
|
11
11
|
require_relative 'subscription_runners_feeder'
|
12
|
+
require_relative 'subscriptions_set_lifecycle'
|
13
|
+
require_relative 'subscriptions_lifecycle'
|
14
|
+
require_relative 'callback_handlers/subscription_feeder_handlers'
|
15
|
+
require_relative 'callback_handlers/subscription_runner_handlers'
|
16
|
+
require_relative 'callback_handlers/events_processor_handlers'
|
17
|
+
require_relative 'callback_handlers/commands_handler_handlers'
|
12
18
|
require_relative 'subscription_feeder'
|
13
19
|
require_relative 'extensions/command_class_lookup_extension'
|
14
20
|
require_relative 'extensions/base_command_extension'
|
@@ -67,20 +73,25 @@ module PgEventstore
|
|
67
73
|
# PgEventstore::Subscription instance and error instance after the related subscription died due to error and no
|
68
74
|
# longer can be automatically restarted due to max retries number reached. You can use this hook to send a
|
69
75
|
# notification about failed subscription.
|
76
|
+
# @param graceful_shutdown_timeout [integer, Float] the number of seconds to wait until force-shutdown the
|
77
|
+
# subscription during the stop process
|
70
78
|
# @return [void]
|
71
79
|
def subscribe(subscription_name, handler:, options: {}, middlewares: nil,
|
72
80
|
pull_interval: config.subscription_pull_interval,
|
73
81
|
max_retries: config.subscription_max_retries,
|
74
82
|
retries_interval: config.subscription_retries_interval,
|
75
83
|
restart_terminator: config.subscription_restart_terminator,
|
76
|
-
failed_subscription_notifier: config.failed_subscription_notifier
|
84
|
+
failed_subscription_notifier: config.failed_subscription_notifier,
|
85
|
+
graceful_shutdown_timeout: config.subscription_graceful_shutdown_timeout)
|
77
86
|
subscription = Subscription.using_connection(config.name).new(
|
78
87
|
set: @set_name, name: subscription_name, options: options, chunk_query_interval: pull_interval,
|
79
88
|
max_restarts_number: max_retries, time_between_restarts: retries_interval
|
80
89
|
)
|
81
90
|
runner = SubscriptionRunner.new(
|
82
91
|
stats: SubscriptionHandlerPerformance.new,
|
83
|
-
events_processor: EventsProcessor.new(
|
92
|
+
events_processor: EventsProcessor.new(
|
93
|
+
create_raw_event_handler(middlewares, handler), graceful_shutdown_timeout: graceful_shutdown_timeout
|
94
|
+
),
|
84
95
|
subscription: subscription,
|
85
96
|
restart_terminator: restart_terminator,
|
86
97
|
failed_subscription_notifier: failed_subscription_notifier
|
@@ -113,7 +124,7 @@ module PgEventstore
|
|
113
124
|
# @param middlewares [Array<Symbol>, nil]
|
114
125
|
# @param handler [#call]
|
115
126
|
# @return [Proc]
|
116
|
-
def
|
127
|
+
def create_raw_event_handler(middlewares, handler)
|
117
128
|
deserializer = EventDeserializer.new(select_middlewares(middlewares), config.event_class_resolver)
|
118
129
|
->(raw_event) { handler.call(deserializer.deserialize(raw_event)) }
|
119
130
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
class SubscriptionsSetLifecycle
|
5
|
+
# @return [Integer] number of seconds between heartbeat updates
|
6
|
+
HEARTBEAT_INTERVAL = 10 # seconds
|
7
|
+
|
8
|
+
# @!attribute subscriptions_set
|
9
|
+
# @return [PgEventstore::SubscriptionsSet, nil]
|
10
|
+
attr_reader :subscriptions_set
|
11
|
+
|
12
|
+
# @param config_name [Symbol]
|
13
|
+
# @param subscriptions_set_attrs [Hash]
|
14
|
+
def initialize(config_name, subscriptions_set_attrs)
|
15
|
+
@config_name = config_name
|
16
|
+
@subscriptions_set_attrs = subscriptions_set_attrs
|
17
|
+
@subscriptions_set_pinged_at = Time.at(0)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [void]
|
21
|
+
def ping_subscriptions_set
|
22
|
+
return if @subscriptions_set_pinged_at > Time.now.utc - HEARTBEAT_INTERVAL
|
23
|
+
|
24
|
+
persisted_subscriptions_set.update(updated_at: Time.now.utc)
|
25
|
+
@subscriptions_set_pinged_at = Time.now.utc
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [PgEventstore::SubscriptionsSet]
|
29
|
+
def persisted_subscriptions_set
|
30
|
+
@subscriptions_set ||= SubscriptionsSet.using_connection(@config_name).create(**@subscriptions_set_attrs)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [void]
|
34
|
+
def reset_subscriptions_set
|
35
|
+
@subscriptions_set&.delete
|
36
|
+
@subscriptions_set = nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/pg_eventstore/utils.rb
CHANGED
@@ -61,6 +61,14 @@ module PgEventstore
|
|
61
61
|
str.gsub!(/[A-Z]/) { |letter| '_' + letter.downcase }
|
62
62
|
str
|
63
63
|
end
|
64
|
+
|
65
|
+
# Detect the global position of the event record in the database. If it is a link event - we pick a
|
66
|
+
# global_position of the link instead of picking a global_position of an event this link points to.
|
67
|
+
# @param raw_event [Hash]
|
68
|
+
# @return [Integer]
|
69
|
+
def original_global_position(raw_event)
|
70
|
+
raw_event['link'] ? raw_event['link']['global_position'] : raw_event['global_position']
|
71
|
+
end
|
64
72
|
end
|
65
73
|
end
|
66
74
|
end
|
@@ -69,6 +69,11 @@ module PgEventstore
|
|
69
69
|
def asset_url(path)
|
70
70
|
url("#{path}?v=#{PgEventstore::VERSION}")
|
71
71
|
end
|
72
|
+
|
73
|
+
# @return [Boolean]
|
74
|
+
def resolve_link_tos?
|
75
|
+
params.key?(:resolve_link_tos) ? params[:resolve_link_tos] == 'true' : true
|
76
|
+
end
|
72
77
|
end
|
73
78
|
|
74
79
|
get '/' do
|
@@ -77,7 +82,10 @@ module PgEventstore
|
|
77
82
|
starting_id: params[:starting_id]&.to_i,
|
78
83
|
per_page: Paginator::EventsCollection::PER_PAGE[params[:per_page]],
|
79
84
|
order: Paginator::EventsCollection::SQL_DIRECTIONS[params[:order]],
|
80
|
-
options: {
|
85
|
+
options: {
|
86
|
+
filter: { event_types: events_filter, streams: streams_filter },
|
87
|
+
resolve_link_tos: resolve_link_tos?
|
88
|
+
}
|
81
89
|
)
|
82
90
|
|
83
91
|
if request.xhr?
|
@@ -23,9 +23,7 @@ module PgEventstore
|
|
23
23
|
def collection
|
24
24
|
@_collection ||= PgEventstore.client(config_name).read(
|
25
25
|
PgEventstore::Stream.all_stream,
|
26
|
-
options: options.merge(
|
27
|
-
from_position: starting_id, max_count: per_page, direction: order, resolve_link_tos: true
|
28
|
-
),
|
26
|
+
options: options.merge(from_position: starting_id, max_count: per_page, direction: order),
|
29
27
|
middlewares: []
|
30
28
|
)
|
31
29
|
end
|
@@ -50,6 +50,10 @@ module PgEventstore
|
|
50
50
|
build_path(params.merge(order: order))
|
51
51
|
end
|
52
52
|
|
53
|
+
def resolve_link_tos_url(should_resolve)
|
54
|
+
build_path(params.merge(resolve_link_tos: should_resolve))
|
55
|
+
end
|
56
|
+
|
53
57
|
# @param number [Integer] total number of events by the current filter
|
54
58
|
# @return [String]
|
55
59
|
def total_count(number)
|
@@ -92,7 +96,6 @@ module PgEventstore
|
|
92
96
|
}
|
93
97
|
}
|
94
98
|
)
|
95
|
-
|
96
99
|
end
|
97
100
|
|
98
101
|
private
|
@@ -113,12 +113,12 @@ $(function(){
|
|
113
113
|
initEventTypeFilterAutocomplete($(this));
|
114
114
|
});
|
115
115
|
|
116
|
+
// Automatically refresh events list every two seconds
|
116
117
|
let autoRefreshInterval;
|
117
118
|
$('#auto-refresh').change(function(){
|
118
119
|
if($(this).is(':checked')) {
|
119
120
|
autoRefreshInterval = setInterval(function(){
|
120
121
|
$.getJSON(window.location.href).success(function(response, textStatus, xhr){
|
121
|
-
console.log(textStatus);
|
122
122
|
if(textStatus === 'success') {
|
123
123
|
$('#events-table').find('tbody').html(response.events);
|
124
124
|
$('#total-count').html(response.total_count);
|
@@ -131,6 +131,15 @@ $(function(){
|
|
131
131
|
}
|
132
132
|
});
|
133
133
|
|
134
|
+
// Resolve links checkbox
|
135
|
+
$('#resolve-link-tos').change(function(){
|
136
|
+
if($(this).is(':checked')) {
|
137
|
+
window.location.href = $(this).data('url-checked');
|
138
|
+
} else {
|
139
|
+
window.location.href = $(this).data('url-unchecked');
|
140
|
+
}
|
141
|
+
});
|
142
|
+
|
134
143
|
// Toggle event's JSON data/metadata details
|
135
144
|
$('#events-table').on('click', '.toggle-event-data', function(){
|
136
145
|
$(this).parents('tr').next().toggleClass('d-none');
|
@@ -78,13 +78,12 @@ module PgEventstore
|
|
78
78
|
# @param state [String]
|
79
79
|
# @param updated_at [Time]
|
80
80
|
# @return [String] html status
|
81
|
-
def colored_state(state, updated_at)
|
81
|
+
def colored_state(state, interval, updated_at)
|
82
82
|
if state == RunnerState::STATES[:running]
|
83
83
|
# -1 is added as a margin to prevent false-positive result
|
84
|
-
if updated_at < Time.now.utc -
|
84
|
+
if updated_at < Time.now.utc - interval - 1
|
85
85
|
title = <<~TEXT
|
86
|
-
Something is wrong. Last update was more than #{
|
87
|
-
ago(#{updated_at}).
|
86
|
+
Something is wrong. Last update was more than #{interval} seconds ago(#{updated_at}).
|
88
87
|
TEXT
|
89
88
|
<<~HTML
|
90
89
|
<span class="text-warning text-nowrap">
|