pg_eventstore 1.5.0 → 1.6.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 +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
|
|