pg_eventstore 1.5.0 → 1.6.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 +3 -0
- data/README.md +4 -0
- data/docs/subscriptions.md +14 -56
- data/exe/pg-eventstore +16 -0
- data/lib/pg_eventstore/callbacks.rb +16 -0
- data/lib/pg_eventstore/cli/commands/base_command.rb +45 -0
- data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +38 -0
- data/lib/pg_eventstore/cli/commands/help_command.rb +22 -0
- data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +96 -0
- data/lib/pg_eventstore/cli/commands/stop_subscriptions_command.rb +22 -0
- data/lib/pg_eventstore/cli/commands.rb +6 -0
- data/lib/pg_eventstore/cli/exit_codes.rb +12 -0
- data/lib/pg_eventstore/cli/parser_options/base_options.rb +46 -0
- data/lib/pg_eventstore/cli/parser_options/default_options.rb +10 -0
- data/lib/pg_eventstore/cli/parser_options/metadata.rb +34 -0
- data/lib/pg_eventstore/cli/parser_options/subscription_options.rb +31 -0
- data/lib/pg_eventstore/cli/parser_options.rb +6 -0
- data/lib/pg_eventstore/cli/parsers/base_parser.rb +33 -0
- data/lib/pg_eventstore/cli/parsers/default_parser.rb +24 -0
- data/lib/pg_eventstore/cli/parsers/subscription_parser.rb +24 -0
- data/lib/pg_eventstore/cli/parsers.rb +5 -0
- data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +75 -0
- data/lib/pg_eventstore/cli/try_unlock_subscriptions_set.rb +16 -0
- data/lib/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rb +67 -0
- data/lib/pg_eventstore/cli.rb +42 -0
- data/lib/pg_eventstore/extensions/options_extension.rb +53 -8
- data/lib/pg_eventstore/rspec/has_option_matcher.rb +38 -13
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +13 -1
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +1 -1
- data/lib/pg_eventstore/subscriptions/queries/subscription_command_queries.rb +1 -1
- data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +3 -1
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +10 -50
- data/lib/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rb +22 -0
- data/lib/pg_eventstore/subscriptions/subscription_feeder_commands.rb +1 -0
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +7 -2
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +44 -10
- data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +1 -1
- data/lib/pg_eventstore/utils.rb +32 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +1 -1
- data/lib/pg_eventstore/web/views/home/partials/events.erb +5 -5
- data/lib/pg_eventstore/web/views/home/partials/stream_filter.erb +3 -3
- data/lib/pg_eventstore/web/views/layouts/application.erb +3 -3
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +6 -6
- data/lib/pg_eventstore.rb +5 -2
- data/sig/pg_eventstore/callbacks.rbs +4 -0
- data/sig/pg_eventstore/cli/commands/base_command.rbs +13 -0
- data/sig/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rbs +14 -0
- data/sig/pg_eventstore/cli/commands/help_command.rbs +13 -0
- data/sig/pg_eventstore/cli/commands/start_subscriptions_command.rbs +28 -0
- data/sig/pg_eventstore/cli/commands/stop_subscriptions_command.rbs +11 -0
- data/sig/pg_eventstore/cli/exit_codes.rbs +8 -0
- data/sig/pg_eventstore/cli/parser_options/base_options.rbs +15 -0
- data/sig/pg_eventstore/cli/parser_options/default_options.rbs +8 -0
- data/sig/pg_eventstore/cli/parser_options/metadata.rbs +11 -0
- data/sig/pg_eventstore/cli/parser_options/subscription_options.rbs +9 -0
- data/sig/pg_eventstore/cli/parsers/base_parser.rbs +18 -0
- data/sig/pg_eventstore/cli/parsers/default_parser.rbs +9 -0
- data/sig/pg_eventstore/cli/parsers/subscription_parser.rbs +9 -0
- data/sig/pg_eventstore/cli/try_to_delete_subscriptions_set.rbs +24 -0
- data/sig/pg_eventstore/cli/try_unlock_subscriptions_set.rbs +7 -0
- data/sig/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rbs +26 -0
- data/sig/pg_eventstore/cli.rbs +10 -0
- data/sig/pg_eventstore/extensions/options_extension.rbs +18 -1
- data/sig/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rbs +8 -0
- data/sig/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rbs +7 -1
- data/sig/pg_eventstore/subscriptions/queries/subscription_command_queries.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +10 -11
- data/sig/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +13 -1
- data/sig/pg_eventstore/utils.rbs +2 -0
- data/sig/pg_eventstore.rbs +2 -8
- metadata +44 -3
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
class TryToDeleteSubscriptionsSet
|
6
|
+
class << self
|
7
|
+
def try_to_delete(...)
|
8
|
+
new(...).try_to_delete
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :config_name, :subscriptions_set_id
|
13
|
+
|
14
|
+
# @param config_name [Symbol]
|
15
|
+
# @param subscriptions_set_id [Integer]
|
16
|
+
def initialize(config_name, subscriptions_set_id)
|
17
|
+
@config_name = config_name
|
18
|
+
@subscriptions_set_id = subscriptions_set_id
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Boolean] whether subscription was deleted
|
22
|
+
def try_to_delete
|
23
|
+
PgEventstore.logger&.info(
|
24
|
+
"Trying to delete SubscriptionsSet##{subscriptions_set_id}."
|
25
|
+
)
|
26
|
+
cmd_name = SubscriptionFeederCommands.command_class('Ping').new.name
|
27
|
+
subscriptions_set_commands_queries.find_or_create_by(
|
28
|
+
subscriptions_set_id: subscriptions_set_id, command_name: cmd_name, data: {}
|
29
|
+
)
|
30
|
+
# Potentially CommandsHandler can be dead exactly at the same moment we expect it to process "Ping" command.
|
31
|
+
# Wait for potential recover plus run interval and plus another second to allow potential processing of
|
32
|
+
# "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
|
+
if subscriptions_set_commands_queries.find_by(subscriptions_set_id: subscriptions_set_id, command_name: cmd_name)
|
35
|
+
# "Ping" command wasn't consumed. Related process must be dead.
|
36
|
+
subscriptions_set_queries.delete(subscriptions_set_id)
|
37
|
+
PgEventstore.logger&.info(
|
38
|
+
"SubscriptionsSet##{subscriptions_set_id} was deleted successfully. Proceeding with startup process."
|
39
|
+
)
|
40
|
+
return true
|
41
|
+
end
|
42
|
+
|
43
|
+
PgEventstore.logger&.warn(
|
44
|
+
"Failed to delete SubscriptionsSet##{subscriptions_set_id}. It looks alive."
|
45
|
+
)
|
46
|
+
false
|
47
|
+
rescue RecordNotFound
|
48
|
+
# Failed to create "Ping" command because related SubscriptionsSet does not exist.
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# @return [PgEventstore::SubscriptionsSetQueries]
|
55
|
+
def subscriptions_set_queries
|
56
|
+
SubscriptionsSetQueries.new(connection)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [PgEventstore::SubscriptionsSetCommandQueries]
|
60
|
+
def subscriptions_set_commands_queries
|
61
|
+
SubscriptionsSetCommandQueries.new(connection)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [PgEventstore::Connection]
|
65
|
+
def connection
|
66
|
+
PgEventstore.connection(config_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [PgEventstore::Config]
|
70
|
+
def config
|
71
|
+
PgEventstore.config(config_name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'try_to_delete_subscriptions_set'
|
4
|
+
require_relative 'wait_for_subscriptions_set_shutdown'
|
5
|
+
|
6
|
+
module PgEventstore
|
7
|
+
module CLI
|
8
|
+
class TryUnlockSubscriptionsSet
|
9
|
+
class << self
|
10
|
+
def try_unlock(...)
|
11
|
+
TryToDeleteSubscriptionsSet.try_to_delete(...) || WaitForSubscriptionsSetShutdown.wait_for_shutdown(...)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module CLI
|
5
|
+
class WaitForSubscriptionsSetShutdown
|
6
|
+
class << self
|
7
|
+
def wait_for_shutdown(...)
|
8
|
+
new(...).wait_for_shutdown
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Float] seconds
|
13
|
+
SHUTDOWN_CHECK_INTERVAL = 2.0
|
14
|
+
|
15
|
+
attr_reader :config_name, :subscriptions_set_id
|
16
|
+
|
17
|
+
# @param config_name [Symbol]
|
18
|
+
# @param subscriptions_set_id [Integer]
|
19
|
+
def initialize(config_name, subscriptions_set_id)
|
20
|
+
@config_name = config_name
|
21
|
+
@subscriptions_set_id = subscriptions_set_id
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean]
|
25
|
+
def wait_for_shutdown
|
26
|
+
PgEventstore.logger&.info(
|
27
|
+
"Trying to wait for shutdown of SubscriptionsSet##{subscriptions_set_id}."
|
28
|
+
)
|
29
|
+
deadline = Time.now.utc + config.subscription_graceful_shutdown_timeout
|
30
|
+
loop do
|
31
|
+
find_set!
|
32
|
+
return false if Time.now.utc > deadline
|
33
|
+
|
34
|
+
sleep SHUTDOWN_CHECK_INTERVAL
|
35
|
+
end
|
36
|
+
rescue RecordNotFound
|
37
|
+
PgEventstore.logger&.warn(
|
38
|
+
"SubscriptionsSet##{subscriptions_set_id} no longer exists. Proceeding with startup process."
|
39
|
+
)
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @return [PgEventstore::SubscriptionsSetQueries]
|
46
|
+
def subscriptions_set_queries
|
47
|
+
SubscriptionsSetQueries.new(connection)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [PgEventstore::SubscriptionsSet]
|
51
|
+
# @raise [PgEventstore::RecordNotFound]
|
52
|
+
def find_set!
|
53
|
+
SubscriptionsSet.using_connection(config_name).new(**subscriptions_set_queries.find!(subscriptions_set_id))
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [PgEventstore::Connection]
|
57
|
+
def connection
|
58
|
+
PgEventstore.connection(config_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [PgEventstore::Config]
|
62
|
+
def config
|
63
|
+
PgEventstore.config(config_name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require_relative 'cli/parsers'
|
5
|
+
require_relative 'cli/parser_options'
|
6
|
+
require_relative 'cli/try_unlock_subscriptions_set'
|
7
|
+
require_relative 'cli/exit_codes'
|
8
|
+
require_relative 'cli/commands'
|
9
|
+
|
10
|
+
module PgEventstore
|
11
|
+
module CLI
|
12
|
+
OPTIONS_PARSER = {
|
13
|
+
"subscriptions" => [Parsers::SubscriptionParser, ParserOptions::SubscriptionOptions].freeze
|
14
|
+
}.tap do |directions|
|
15
|
+
directions.default = [Parsers::DefaultParser, ParserOptions::DefaultOptions].freeze
|
16
|
+
end.freeze
|
17
|
+
|
18
|
+
COMMANDS = {
|
19
|
+
["subscriptions", "start"].freeze => Commands::StartSubscriptionsCommand,
|
20
|
+
["subscriptions", "stop"].freeze => Commands::StopSubscriptionsCommand
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
class << self
|
24
|
+
# @return [PgEventstore::Callbacks]
|
25
|
+
def callbacks
|
26
|
+
@callbacks ||= Callbacks.new
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param args [Array<String>]
|
30
|
+
# @return [Integer] exit code
|
31
|
+
def execute(args)
|
32
|
+
options_parser_class, options_class = OPTIONS_PARSER[args[0]]
|
33
|
+
command, parsed_options = options_parser_class.new(ARGV, options_class.new).parse
|
34
|
+
return Commands::HelpCommand.new(parsed_options).call if parsed_options.help
|
35
|
+
return COMMANDS[command].new(parsed_options).call if COMMANDS[command]
|
36
|
+
|
37
|
+
_, parsed_options = options_parser_class.new(['-h'], options_class.new).parse
|
38
|
+
Commands::HelpCommand.new(parsed_options).call
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -40,14 +40,59 @@ module PgEventstore
|
|
40
40
|
#
|
41
41
|
# SomeClass.new(attr1: 'hihi', attr4: 'byebye')
|
42
42
|
module OptionsExtension
|
43
|
+
class Option
|
44
|
+
attr_reader :name, :metadata
|
45
|
+
|
46
|
+
# @param name [Symbol]
|
47
|
+
# @param metadata [Object, nil]
|
48
|
+
def initialize(name, metadata: nil)
|
49
|
+
@name = name
|
50
|
+
@metadata = metadata
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param other_option [Object]
|
54
|
+
# @return [Boolean]
|
55
|
+
def ==(other_option)
|
56
|
+
return false unless other_option.is_a?(Option)
|
57
|
+
|
58
|
+
name == other_option.name
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param other_option [Object]
|
62
|
+
# @return [Boolean]
|
63
|
+
def eql?(other_option)
|
64
|
+
return false unless other_option.is_a?(Option)
|
65
|
+
|
66
|
+
name.eql?(other_option.name)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Integer]
|
70
|
+
def hash
|
71
|
+
name.hash
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Options < Set
|
76
|
+
def add(o)
|
77
|
+
@hash[o] = o
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param option [Symbol]
|
82
|
+
# @return [PgEventstore::Extensions::OptionsExtension::Option, nil]
|
83
|
+
def [](option)
|
84
|
+
@hash[Option.new(option)]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
43
88
|
# @!visibility private
|
44
89
|
module ClassMethods
|
45
90
|
# @param opt_name [Symbol] option name
|
46
91
|
# @param blk [Proc] provide define value using block. It will be later evaluated in the
|
47
92
|
# context of your object to determine the default value of the option
|
48
93
|
# @return [Symbol]
|
49
|
-
def option(opt_name, &blk)
|
50
|
-
self.options = (options +
|
94
|
+
def option(opt_name, metadata: nil, &blk)
|
95
|
+
self.options = (options + Options.new([Option.new(opt_name, metadata: metadata)])).freeze
|
51
96
|
warn_already_defined(opt_name)
|
52
97
|
warn_already_defined(:"#{opt_name}=")
|
53
98
|
define_method "#{opt_name}=" do |value|
|
@@ -67,7 +112,7 @@ module PgEventstore
|
|
67
112
|
|
68
113
|
def inherited(klass)
|
69
114
|
super
|
70
|
-
klass.options =
|
115
|
+
klass.options = Options.new(options).freeze
|
71
116
|
end
|
72
117
|
|
73
118
|
private
|
@@ -86,7 +131,7 @@ module PgEventstore
|
|
86
131
|
|
87
132
|
def self.included(klass)
|
88
133
|
klass.singleton_class.attr_accessor(:options)
|
89
|
-
klass.options =
|
134
|
+
klass.options = Options.new.freeze
|
90
135
|
klass.extend(ClassMethods)
|
91
136
|
end
|
92
137
|
|
@@ -100,7 +145,7 @@ module PgEventstore
|
|
100
145
|
# @return [Hash]
|
101
146
|
def options_hash
|
102
147
|
self.class.options.each_with_object({}) do |option, res|
|
103
|
-
res[option] = public_send(option)
|
148
|
+
res[option.name] = public_send(option.name)
|
104
149
|
end
|
105
150
|
end
|
106
151
|
alias attributes_hash options_hash
|
@@ -108,7 +153,7 @@ module PgEventstore
|
|
108
153
|
# @param opt_name [Symbol]
|
109
154
|
# @return [Boolean]
|
110
155
|
def readonly!(opt_name)
|
111
|
-
return false unless self.class.options.include?(opt_name)
|
156
|
+
return false unless self.class.options.include?(Option.new(opt_name))
|
112
157
|
|
113
158
|
@readonly.add(opt_name)
|
114
159
|
true
|
@@ -136,8 +181,8 @@ module PgEventstore
|
|
136
181
|
def init_default_values(options)
|
137
182
|
self.class.options.each do |option|
|
138
183
|
# init default values of options
|
139
|
-
value = options.key?(option) ? options[option] : public_send(option)
|
140
|
-
public_send("#{option}=", value)
|
184
|
+
value = options.key?(option.name) ? options[option.name] : public_send(option.name)
|
185
|
+
public_send("#{option.name}=", value)
|
141
186
|
end
|
142
187
|
end
|
143
188
|
end
|
@@ -45,25 +45,46 @@
|
|
45
45
|
# end
|
46
46
|
RSpec::Matchers.define :has_option do |option_name|
|
47
47
|
match do |obj|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
option = obj.class.options[option_name]
|
49
|
+
is_correct = obj.class.respond_to?(:options) && option
|
50
|
+
if defined?(@default_value)
|
51
|
+
is_correct &&=
|
52
|
+
RSpec::Matchers::BuiltIn::Match.new(@default_value).matches?(obj.class.allocate.public_send(option_name))
|
53
|
+
end
|
54
|
+
if defined?(@metadata)
|
55
|
+
is_correct &&= RSpec::Matchers::BuiltIn::Match.new(@metadata).matches?(option.metadata)
|
53
56
|
end
|
57
|
+
is_correct
|
54
58
|
end
|
55
59
|
|
56
60
|
failure_message do |obj|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
option = obj.class.options[option_name]
|
62
|
+
option_presence = obj.class.respond_to?(:options) && option
|
63
|
+
|
64
|
+
default_value_message = "with default value #{@default_value.inspect}"
|
65
|
+
metadata_message = "with metadata #{@metadata.inspect}"
|
66
|
+
message = "Expected #{obj.class} to have `#{option_name.inspect}' option"
|
67
|
+
message += " #{default_value_message}," if defined?(@default_value)
|
68
|
+
message += " #{metadata_message}," if defined?(@metadata)
|
69
|
+
message += "," unless defined?(@metadata) || defined?(@default_value)
|
70
|
+
if option_presence
|
71
|
+
actual_default_value = obj.class.allocate.public_send(option_name)
|
72
|
+
actual_metadata = option.metadata
|
73
|
+
default_value_matches = RSpec::Matchers::BuiltIn::Match.new(@default_value).matches?(actual_default_value)
|
74
|
+
metadata_matches = RSpec::Matchers::BuiltIn::Match.new(@metadata).matches?(actual_metadata)
|
75
|
+
|
76
|
+
case [default_value_matches, metadata_matches]
|
77
|
+
when [false, true]
|
78
|
+
message += " but default value is #{actual_default_value.inspect}"
|
79
|
+
when [true, false]
|
80
|
+
message += " but metadata is #{actual_metadata.inspect}"
|
81
|
+
else
|
82
|
+
message += " but default value is #{actual_default_value.inspect} and metadata is #{actual_metadata.inspect}"
|
83
|
+
end
|
62
84
|
else
|
63
|
-
|
85
|
+
message += " but there is no option found with the given name"
|
64
86
|
end
|
65
|
-
|
66
|
-
msg
|
87
|
+
message
|
67
88
|
end
|
68
89
|
|
69
90
|
description do
|
@@ -83,6 +104,10 @@ RSpec::Matchers.define :has_option do |option_name|
|
|
83
104
|
chain :with_default_value do |val|
|
84
105
|
@default_value = val
|
85
106
|
end
|
107
|
+
|
108
|
+
chain :with_metadata do |val|
|
109
|
+
@metadata = val
|
110
|
+
end
|
86
111
|
end
|
87
112
|
|
88
113
|
RSpec::Matchers.alias_matcher :have_option, :has_option
|
@@ -13,7 +13,7 @@ module PgEventstore
|
|
13
13
|
# Look up commands for the given SubscriptionFeeder and execute them
|
14
14
|
# @return [void]
|
15
15
|
def process
|
16
|
-
|
16
|
+
commands.each do |command|
|
17
17
|
command.exec_cmd(@subscription_feeder)
|
18
18
|
queries.delete(command.id)
|
19
19
|
end
|
@@ -21,6 +21,18 @@ module PgEventstore
|
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
+
# @return [Array<PgEventstore::SubscriptionFeederCommands::Base>]
|
25
|
+
def commands
|
26
|
+
commands = queries.find_commands(@subscription_feeder.id)
|
27
|
+
ping_cmd = commands.find do |cmd|
|
28
|
+
cmd.name == 'Ping'
|
29
|
+
end
|
30
|
+
return commands unless ping_cmd
|
31
|
+
|
32
|
+
# "Ping" command should go in prio
|
33
|
+
[ping_cmd, *(commands - [ping_cmd])]
|
34
|
+
end
|
35
|
+
|
24
36
|
# @return [PgEventstore::SubscriptionsSetCommandQueries]
|
25
37
|
def queries
|
26
38
|
SubscriptionsSetCommandQueries.new(connection)
|
@@ -5,7 +5,7 @@ module PgEventstore
|
|
5
5
|
class SubscriptionRunnersCommands
|
6
6
|
# @param config_name [Symbol]
|
7
7
|
# @param runners [Array<PgEventstore::SubscriptionRunner>]
|
8
|
-
# @param subscriptions_set_id [Integer]
|
8
|
+
# @param subscriptions_set_id [Integer, nil]
|
9
9
|
def initialize(config_name, runners, subscriptions_set_id)
|
10
10
|
@config_name = config_name
|
11
11
|
@runners = runners.to_h { |runner| [runner.id, runner] }
|
@@ -64,7 +64,7 @@ module PgEventstore
|
|
64
64
|
end
|
65
65
|
|
66
66
|
# @param subscription_ids [Array<Integer>]
|
67
|
-
# @param subscriptions_set_id [Integer]
|
67
|
+
# @param subscriptions_set_id [Integer, nil]
|
68
68
|
# @return [Array<PgEventstore::SubscriptionRunnerCommands::Base>]
|
69
69
|
def find_commands(subscription_ids, subscriptions_set_id:)
|
70
70
|
return [] if subscription_ids.empty?
|
@@ -55,9 +55,11 @@ module PgEventstore
|
|
55
55
|
conn.exec_params(sql, [command_name, subscriptions_set_id, data])
|
56
56
|
end
|
57
57
|
deserialize(pg_result.to_a.first)
|
58
|
+
rescue PG::ForeignKeyViolation
|
59
|
+
raise RecordNotFound.new("subscriptions_set", subscriptions_set_id)
|
58
60
|
end
|
59
61
|
|
60
|
-
# @param subscriptions_set_id [Integer]
|
62
|
+
# @param subscriptions_set_id [Integer, nil]
|
61
63
|
# @return [Array<PgEventstore::SubscriptionFeederCommands::Base>]
|
62
64
|
def find_commands(subscriptions_set_id)
|
63
65
|
sql_builder =
|
@@ -6,37 +6,25 @@ module PgEventstore
|
|
6
6
|
class SubscriptionFeeder
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
attr_reader :config_name
|
10
|
+
|
11
|
+
def_delegators :@basic_runner, :start, :stop, :restore, :state, :wait_for_finish, :stop_async, :running?
|
11
12
|
|
12
13
|
# @param config_name [Symbol]
|
13
|
-
# @param
|
14
|
-
# @param
|
15
|
-
|
16
|
-
def initialize(config_name:, set_name:, max_retries:, retries_interval:)
|
14
|
+
# @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
|
15
|
+
# @param subscriptions_lifecycle [PgEventstore::SubscriptionsLifecycle]
|
16
|
+
def initialize(config_name:, subscriptions_set_lifecycle:, subscriptions_lifecycle:)
|
17
17
|
@config_name = config_name
|
18
18
|
@basic_runner = BasicRunner.new(0.2, 0)
|
19
|
-
@subscriptions_set_lifecycle =
|
20
|
-
|
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)
|
19
|
+
@subscriptions_set_lifecycle = subscriptions_set_lifecycle
|
20
|
+
@subscriptions_lifecycle = subscriptions_lifecycle
|
24
21
|
@commands_handler = CommandsHandler.new(@config_name, self, @subscriptions_lifecycle.runners)
|
25
22
|
attach_runner_callbacks
|
26
23
|
end
|
27
24
|
|
28
|
-
# @return [Integer]
|
25
|
+
# @return [Integer, nil]
|
29
26
|
def id
|
30
|
-
@subscriptions_set_lifecycle.
|
31
|
-
end
|
32
|
-
|
33
|
-
# Adds SubscriptionRunner to the set
|
34
|
-
# @param runner [PgEventstore::SubscriptionRunner]
|
35
|
-
# @return [PgEventstore::SubscriptionRunner]
|
36
|
-
def add(runner)
|
37
|
-
assert_proper_state!
|
38
|
-
@subscriptions_lifecycle.runners.push(runner)
|
39
|
-
runner
|
27
|
+
@subscriptions_set_lifecycle.subscriptions_set&.id
|
40
28
|
end
|
41
29
|
|
42
30
|
# Starts all SubscriptionRunners. This is only available if SubscriptionFeeder's runner is alive.
|
@@ -57,20 +45,6 @@ module PgEventstore
|
|
57
45
|
self
|
58
46
|
end
|
59
47
|
|
60
|
-
# Produces a copy of currently running Subscriptions. This is needed, because original Subscriptions objects are
|
61
|
-
# dangerous to use - users may incidentally break their state.
|
62
|
-
# @return [Array<PgEventstore::Subscription>]
|
63
|
-
def read_only_subscriptions
|
64
|
-
@subscriptions_lifecycle.subscriptions.map(&:dup)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Produces a copy of current SubscriptionsSet. This is needed, because original SubscriptionsSet object is
|
68
|
-
# dangerous to use - users may incidentally break its state.
|
69
|
-
# @return [PgEventstore::SubscriptionsSet, nil]
|
70
|
-
def read_only_subscriptions_set
|
71
|
-
@subscriptions_set_lifecycle.subscriptions_set&.dup
|
72
|
-
end
|
73
|
-
|
74
48
|
private
|
75
49
|
|
76
50
|
# @return [void]
|
@@ -133,19 +107,5 @@ module PgEventstore
|
|
133
107
|
SubscriptionFeederHandlers.setup_handler(:update_subscriptions_set_restarts, @subscriptions_set_lifecycle)
|
134
108
|
)
|
135
109
|
end
|
136
|
-
|
137
|
-
# This method helps to ensure that no Subscription is added after SubscriptionFeeder's runner is working
|
138
|
-
# @return [void]
|
139
|
-
# @raise [RuntimeError]
|
140
|
-
def assert_proper_state!
|
141
|
-
return if @basic_runner.initial? || @basic_runner.stopped?
|
142
|
-
subscriptions_set = @subscriptions_set_lifecycle.persisted_subscriptions_set
|
143
|
-
|
144
|
-
error_message = <<~TEXT
|
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.
|
147
|
-
TEXT
|
148
|
-
raise error_message
|
149
|
-
end
|
150
110
|
end
|
151
111
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module SubscriptionFeederCommands
|
5
|
+
# @!visibility private
|
6
|
+
class Ping < Base
|
7
|
+
# @param subscription_feeder [PgEventstore::SubscriptionFeeder]
|
8
|
+
# @return [void]
|
9
|
+
def exec_cmd(subscription_feeder)
|
10
|
+
queries(subscription_feeder.config_name).update(subscriptions_set_id, {})
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# @param config_name [Symbol]
|
16
|
+
# @return [PgEventstore::SubscriptionsSetQueries]
|
17
|
+
def queries(config_name)
|
18
|
+
SubscriptionsSetQueries.new(PgEventstore.connection(config_name))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'subscription_feeder_commands/base'
|
4
|
+
require_relative 'subscription_feeder_commands/ping'
|
4
5
|
require_relative 'subscription_feeder_commands/restore'
|
5
6
|
require_relative 'subscription_feeder_commands/start_all'
|
6
7
|
require_relative 'subscription_feeder_commands/stop'
|
@@ -13,12 +13,12 @@ module PgEventstore
|
|
13
13
|
|
14
14
|
# @param config_name [Symbol]
|
15
15
|
# @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
|
16
|
-
def initialize(config_name, subscriptions_set_lifecycle)
|
16
|
+
def initialize(config_name, subscriptions_set_lifecycle, force_lock: false)
|
17
17
|
@config_name = config_name
|
18
18
|
@subscriptions_set_lifecycle = subscriptions_set_lifecycle
|
19
19
|
@runners = []
|
20
20
|
@subscriptions_pinged_at = Time.at(0)
|
21
|
-
@force_lock =
|
21
|
+
@force_lock = force_lock
|
22
22
|
end
|
23
23
|
|
24
24
|
# Locks all Subscriptions behind the current SubscriptionsSet
|
@@ -59,6 +59,11 @@ module PgEventstore
|
|
59
59
|
# locked by the new SubscriptionsSet.
|
60
60
|
# @return [void]
|
61
61
|
def force_lock!
|
62
|
+
message = <<~TEXT
|
63
|
+
Usage of #force_lock! is deprecated and will be removed in v2. Please pass :force_lock keyword argument when \
|
64
|
+
initializing SubscriptionsManager. Example: PgEventstore.subscriptions_manager(force_lock: true)
|
65
|
+
TEXT
|
66
|
+
Utils.deprecation_warning(message)
|
62
67
|
@force_lock = true
|
63
68
|
end
|
64
69
|
|