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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +4 -0
  4. data/docs/subscriptions.md +14 -56
  5. data/exe/pg-eventstore +16 -0
  6. data/lib/pg_eventstore/callbacks.rb +16 -0
  7. data/lib/pg_eventstore/cli/commands/base_command.rb +45 -0
  8. data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +38 -0
  9. data/lib/pg_eventstore/cli/commands/help_command.rb +22 -0
  10. data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +96 -0
  11. data/lib/pg_eventstore/cli/commands/stop_subscriptions_command.rb +22 -0
  12. data/lib/pg_eventstore/cli/commands.rb +6 -0
  13. data/lib/pg_eventstore/cli/exit_codes.rb +12 -0
  14. data/lib/pg_eventstore/cli/parser_options/base_options.rb +46 -0
  15. data/lib/pg_eventstore/cli/parser_options/default_options.rb +10 -0
  16. data/lib/pg_eventstore/cli/parser_options/metadata.rb +34 -0
  17. data/lib/pg_eventstore/cli/parser_options/subscription_options.rb +31 -0
  18. data/lib/pg_eventstore/cli/parser_options.rb +6 -0
  19. data/lib/pg_eventstore/cli/parsers/base_parser.rb +33 -0
  20. data/lib/pg_eventstore/cli/parsers/default_parser.rb +24 -0
  21. data/lib/pg_eventstore/cli/parsers/subscription_parser.rb +24 -0
  22. data/lib/pg_eventstore/cli/parsers.rb +5 -0
  23. data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +75 -0
  24. data/lib/pg_eventstore/cli/try_unlock_subscriptions_set.rb +16 -0
  25. data/lib/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rb +67 -0
  26. data/lib/pg_eventstore/cli.rb +42 -0
  27. data/lib/pg_eventstore/extensions/options_extension.rb +53 -8
  28. data/lib/pg_eventstore/rspec/has_option_matcher.rb +38 -13
  29. data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +13 -1
  30. data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +1 -1
  31. data/lib/pg_eventstore/subscriptions/queries/subscription_command_queries.rb +1 -1
  32. data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +3 -1
  33. data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +10 -50
  34. data/lib/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rb +22 -0
  35. data/lib/pg_eventstore/subscriptions/subscription_feeder_commands.rb +1 -0
  36. data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +7 -2
  37. data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +44 -10
  38. data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +1 -1
  39. data/lib/pg_eventstore/utils.rb +32 -0
  40. data/lib/pg_eventstore/version.rb +1 -1
  41. data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +1 -1
  42. data/lib/pg_eventstore/web/views/home/partials/events.erb +5 -5
  43. data/lib/pg_eventstore/web/views/home/partials/stream_filter.erb +3 -3
  44. data/lib/pg_eventstore/web/views/layouts/application.erb +3 -3
  45. data/lib/pg_eventstore/web/views/subscriptions/index.erb +6 -6
  46. data/lib/pg_eventstore.rb +5 -2
  47. data/sig/pg_eventstore/callbacks.rbs +4 -0
  48. data/sig/pg_eventstore/cli/commands/base_command.rbs +13 -0
  49. data/sig/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rbs +14 -0
  50. data/sig/pg_eventstore/cli/commands/help_command.rbs +13 -0
  51. data/sig/pg_eventstore/cli/commands/start_subscriptions_command.rbs +28 -0
  52. data/sig/pg_eventstore/cli/commands/stop_subscriptions_command.rbs +11 -0
  53. data/sig/pg_eventstore/cli/exit_codes.rbs +8 -0
  54. data/sig/pg_eventstore/cli/parser_options/base_options.rbs +15 -0
  55. data/sig/pg_eventstore/cli/parser_options/default_options.rbs +8 -0
  56. data/sig/pg_eventstore/cli/parser_options/metadata.rbs +11 -0
  57. data/sig/pg_eventstore/cli/parser_options/subscription_options.rbs +9 -0
  58. data/sig/pg_eventstore/cli/parsers/base_parser.rbs +18 -0
  59. data/sig/pg_eventstore/cli/parsers/default_parser.rbs +9 -0
  60. data/sig/pg_eventstore/cli/parsers/subscription_parser.rbs +9 -0
  61. data/sig/pg_eventstore/cli/try_to_delete_subscriptions_set.rbs +24 -0
  62. data/sig/pg_eventstore/cli/try_unlock_subscriptions_set.rbs +7 -0
  63. data/sig/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rbs +26 -0
  64. data/sig/pg_eventstore/cli.rbs +10 -0
  65. data/sig/pg_eventstore/extensions/options_extension.rbs +18 -1
  66. data/sig/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rbs +8 -0
  67. data/sig/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rbs +7 -1
  68. data/sig/pg_eventstore/subscriptions/queries/subscription_command_queries.rbs +1 -1
  69. data/sig/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rbs +1 -1
  70. data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +10 -11
  71. data/sig/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rbs +11 -0
  72. data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +1 -1
  73. data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +13 -1
  74. data/sig/pg_eventstore/utils.rbs +2 -0
  75. data/sig/pg_eventstore.rbs +2 -8
  76. 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 + Set.new([opt_name])).freeze
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 = Set.new(options).freeze
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 = Set.new.freeze
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
- option_presence = obj.class.respond_to?(:options) && obj.class.options.include?(option_name)
49
- if @default_value
50
- option_presence && RSpec::Matchers::BuiltIn::Match.new(@default_value).matches?(obj.class.allocate.public_send(option_name))
51
- else
52
- option_presence
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
- option_presence = obj.class.respond_to?(:options) && obj.class.options.include?(option_name)
58
- if option_presence && @default_value
59
- msg = "Expected #{obj.class} to have `#{option_name.inspect}' option with #{@default_value.inspect}"
60
- msg += ' default value, but default value is'
61
- msg += " #{obj.class.allocate.public_send(option_name).inspect}"
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
- msg = "Expected #{obj} to have `#{option_name.inspect}' option."
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
- queries.find_commands(@subscription_feeder.id).each do |command|
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
- def_delegators :@basic_runner, :start, :stop, :restore, :state, :wait_for_finish, :stop_async
10
- def_delegators :@subscriptions_lifecycle, :force_lock!
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 set_name [String]
14
- # @param max_retries [Integer] max number of retries of failed SubscriptionsSet
15
- # @param retries_interval [Integer] a delay between retries of failed SubscriptionsSet
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 = 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)
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.persisted_subscriptions_set.id
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 = false
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