pg_eventstore 1.4.0 → 1.5.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 +4 -0
- data/lib/pg_eventstore/extensions/callback_handlers_extension.rb +21 -0
- 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 +17 -41
- 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 +8 -2
- 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/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 +1 -1
- 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'
|
@@ -84,7 +90,7 @@ module PgEventstore
|
|
84
90
|
runner = SubscriptionRunner.new(
|
85
91
|
stats: SubscriptionHandlerPerformance.new,
|
86
92
|
events_processor: EventsProcessor.new(
|
87
|
-
|
93
|
+
create_raw_event_handler(middlewares, handler), graceful_shutdown_timeout: graceful_shutdown_timeout
|
88
94
|
),
|
89
95
|
subscription: subscription,
|
90
96
|
restart_terminator: restart_terminator,
|
@@ -118,7 +124,7 @@ module PgEventstore
|
|
118
124
|
# @param middlewares [Array<Symbol>, nil]
|
119
125
|
# @param handler [#call]
|
120
126
|
# @return [Proc]
|
121
|
-
def
|
127
|
+
def create_raw_event_handler(middlewares, handler)
|
122
128
|
deserializer = EventDeserializer.new(select_middlewares(middlewares), config.event_class_resolver)
|
123
129
|
->(raw_event) { handler.call(deserializer.deserialize(raw_event)) }
|
124
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">
|
@@ -26,6 +26,7 @@
|
|
26
26
|
<br/>
|
27
27
|
<form id="filters-form" action="<%= url('/') %>" method="GET" data-parsley-validate class="form-horizontal form-label-left">
|
28
28
|
<input type="hidden" name="per_page" value="<%= params[:per_page].to_i %>">
|
29
|
+
<input type="hidden" name="resolve_link_tos" value="<%= resolve_link_tos? %>">
|
29
30
|
<div class="stream-filters">
|
30
31
|
<% streams_filter&.each do |attrs| %>
|
31
32
|
<%= erb :'home/partials/stream_filter', { layout: false }, { stream: attrs } %>
|
@@ -89,6 +90,16 @@
|
|
89
90
|
<button type="submit" class="d-none"></button>
|
90
91
|
</div>
|
91
92
|
</form>
|
93
|
+
<div class="float-md-right mt-2 mr-2">
|
94
|
+
<div class="form-group">
|
95
|
+
<div class="checkbox">
|
96
|
+
<label for="resolve-link-tos" class="control-label">
|
97
|
+
Resolve links
|
98
|
+
<input type="checkbox" id="resolve-link-tos" value="true" autocomplete="off" <% if resolve_link_tos? %> checked <% end %> data-url-checked="<%= resolve_link_tos_url(true) %>" data-url-unchecked="<%= resolve_link_tos_url(false) %>">
|
99
|
+
</label>
|
100
|
+
</div>
|
101
|
+
</div>
|
102
|
+
</div>
|
92
103
|
<div class="float-md-right mt-2 mr-2">
|
93
104
|
<div class="form-group">
|
94
105
|
<div class="checkbox">
|