pg_eventstore 3.1.1 → 3.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9181ca6342afc352a02779065731ea21bb9e8ce6632b924a25342e9726e88f3c
4
- data.tar.gz: 6c80c69802d7efcca8be4440d22f2dac1621f379cb710520d755335378a94725
3
+ metadata.gz: b54fbf71f1dd082e5895ab8b1431f2a92442c0d0865e656b4fe65430503151b5
4
+ data.tar.gz: 45a0bfd8a606466495643cb41f503643bd6151f915d1e36cdf374eded7dab8b3
5
5
  SHA512:
6
- metadata.gz: a3d07d058fd23c8b6d19c09b2de3a5c600683205d6bd21df9a304dd0bc4e5428fd34f33dcbeea7cdd95415f8593fb99bac1801e1d08ce506c05b748d9f78ff78
7
- data.tar.gz: c09027974c15266d0771c1375fbd7dedcc78841f7d36d9bb0c47f6f619b14922eb9d1561b9e1240a692c32aadef59d55ec3526479befab06e2c6c461e81d5680
6
+ metadata.gz: 59028bfa802198da04912ba0f339a165eb3d9c35e0cb940b9fb0437ae42909f3570b2f4bbda88d5234fb622420d4ff7425dd1fafa4a8951b4092879ca33fcf77
7
+ data.tar.gz: 671e391234a4f8b6a008b19bdbc533c94a7e3cc562fab9cdfd4dbef77968fd2ed03814f7ff132a8326aee859cc2e65db04ef63a8667e2e67b18615b102e08102
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [3.1.2]
4
+
5
+ - Fix subscriptions skipping events when the estimated number of events to fetch drops to 0 during the pull
6
+
3
7
  ## [3.1.1]
4
8
 
5
9
  - Prevent arbitrary order in replica queries
@@ -64,9 +64,8 @@ module PgEventstore
64
64
 
65
65
  # @return [void]
66
66
  def resolve_indexes
67
- indexes_to_resolve = @indexes.slice!(
68
- Utils.range_to_slice(@indexes.map(&:event_type_partition_id), MAX_PARTITIONS_TO_RESOLVE_PER_CALL)
69
- )
67
+ range = Utils.range_to_slice(@indexes.map(&:event_type_partition_id), MAX_PARTITIONS_TO_RESOLVE_PER_CALL)
68
+ indexes_to_resolve = @indexes[range]
70
69
  global_to_sub_position_map = indexes_to_resolve.to_h { [_1.global_position, _1.subscription_position] }
71
70
  raw_events = events_global_index_queries.resolve_indexes(
72
71
  indexes_to_resolve,
@@ -80,12 +79,10 @@ module PgEventstore
80
79
  )
81
80
  end
82
81
  raw_events = raw_events.sort_by(&:subscription_position)
82
+ # Remove indexes only after their events are resolved, so #size keeps counting them while the query runs
83
+ @indexes.slice!(range)
83
84
  @raw_events.push(*raw_events)
84
85
  @resolved = @indexes.empty?
85
- rescue => exception
86
- @indexes.unshift(*indexes_to_resolve)
87
- @resolved = false
88
- raise exception
89
86
  end
90
87
 
91
88
  # @return [PgEventstore::EventsGlobalIndexQueries]
@@ -9,23 +9,25 @@ module PgEventstore
9
9
  DEFAULT_SUBSCRIPTIONS_NUM_PER_QUERY = 10
10
10
 
11
11
  class << self
12
- # @param runners [Array<PgEventstore::SubscriptionRunner>]
12
+ # @param runners_query_options [Hash{PgEventstore::SubscriptionRunner => Hash}]
13
13
  # @param connection [PgEventstore::Connection]
14
14
  # @param query_strategy [PgEventstore::QueryStrategy]
15
15
  # @param subscriptions_per_query [Integer]
16
16
  # @return [PgEventstore::SubscriptionFeedStrategy::Collection]
17
- def create(runners, connection, query_strategy, subscriptions_per_query: DEFAULT_SUBSCRIPTIONS_NUM_PER_QUERY)
17
+ def create(runners_query_options, connection, query_strategy,
18
+ subscriptions_per_query: DEFAULT_SUBSCRIPTIONS_NUM_PER_QUERY)
18
19
  instance = new
19
- replica_runners = runners.grep(ReplicaSubscriptionRunner)
20
- common_runners = runners - replica_runners
21
- common_runners.each_slice(subscriptions_per_query).each do |runners_slice|
20
+ replica_runners, common_runners = runners_query_options.partition do |runner, _|
21
+ runner.is_a?(ReplicaSubscriptionRunner)
22
+ end
23
+ common_runners.each_slice(subscriptions_per_query) do |runners_slice|
22
24
  strategy = IndexReadStrategy.new(connection, query_strategy)
23
- strategy.add(*runners_slice)
25
+ runners_slice.each { |runner, query_options| strategy.add(runner, query_options) }
24
26
  instance.push(strategy)
25
27
  end
26
28
  replica_runners.each_slice(subscriptions_per_query) do |runners_slice|
27
29
  strategy = ReplicationStrategy.new(connection, query_strategy)
28
- strategy.add(*runners_slice)
30
+ runners_slice.each { |runner, query_options| strategy.add(runner, query_options) }
29
31
  instance.push(strategy)
30
32
  end
31
33
  instance
@@ -18,45 +18,45 @@ module PgEventstore
18
18
  def initialize(connection, query_strategy)
19
19
  @connection = connection
20
20
  @query_strategy = query_strategy
21
- @runners = []
21
+ @runners_query_options = {}
22
22
  end
23
23
 
24
- # @param runners [Array<PgEventstore::SubscriptionRunner>]
25
- # @return [Array<PgEventstore::SubscriptionRunner>]
26
- def add(*runners)
27
- @runners.push(*runners)
24
+ # @param runner [PgEventstore::SubscriptionRunner]
25
+ # @param query_options [Hash]
26
+ # @return [void]
27
+ def add(runner, query_options)
28
+ @runners_query_options[runner] = query_options
28
29
  end
29
30
 
30
31
  # @return [Integer]
31
32
  def size
32
- @runners.size
33
+ @runners_query_options.size
33
34
  end
34
35
 
35
36
  # @return [Boolean]
36
37
  def any?
37
- @runners.any?
38
+ @runners_query_options.any?
38
39
  end
39
40
 
40
41
  # @return [void]
41
42
  def feed
42
- runners_query_options = @runners.to_h do |runner|
43
- next_chunk_query_opts = runner.next_chunk_query_opts
44
- next_chunk_query_opts[:to_position] =
45
- [next_chunk_query_opts[:from_position] + INDEX_LOOK_UP_DISTANCE, safe_position].min
46
- [runner.id, next_chunk_query_opts]
43
+ @runners_query_options.each_value do |query_options|
44
+ query_options[:to_position] = [query_options[:from_position] + INDEX_LOOK_UP_DISTANCE, safe_position].min
47
45
  end
48
- grouped_indexes = events_global_index_queries.fetch_indexes_for_subscriptions(runners_query_options)
49
- @runners.each do |runner|
46
+ grouped_indexes = events_global_index_queries.fetch_indexes_for_subscriptions(
47
+ @runners_query_options.transform_keys(&:id)
48
+ )
49
+ @runners_query_options.each do |runner, query_options|
50
50
  if grouped_indexes[runner.id]
51
51
  chunk = Chunks::SubscriptionEventsIndexChunk.new(
52
52
  grouped_indexes[runner.id],
53
53
  @connection,
54
54
  QueryStrategy::Foreground.new(@connection),
55
- runners_query_options[runner.id][:resolve_link_tos]
55
+ query_options[:resolve_link_tos]
56
56
  )
57
57
  runner.feed(chunk)
58
58
  else
59
- runner.feed(Chunks::SubscriptionCheckpointChunk.new(runners_query_options[runner.id][:to_position]))
59
+ runner.feed(Chunks::SubscriptionCheckpointChunk.new(query_options[:to_position]))
60
60
  end
61
61
  end
62
62
  end
@@ -16,40 +16,40 @@ module PgEventstore
16
16
  def initialize(connection, query_strategy)
17
17
  @connection = connection
18
18
  @query_strategy = query_strategy
19
- @runners = []
19
+ @runners_query_options = {}
20
20
  end
21
21
 
22
- # @param runners [Array<PgEventstore::ReplicaSubscriptionRunner>]
23
- # @return [Array<PgEventstore::ReplicaSubscriptionRunner>]
24
- def add(*runners)
25
- @runners.push(*runners)
22
+ # @param runner [PgEventstore::ReplicaSubscriptionRunner]
23
+ # @param query_options [Hash]
24
+ # @return [void]
25
+ def add(runner, query_options)
26
+ @runners_query_options[runner] = query_options
26
27
  end
27
28
 
28
29
  # @return [Integer]
29
30
  def size
30
- @runners.size
31
+ @runners_query_options.size
31
32
  end
32
33
 
33
34
  # @return [Boolean]
34
35
  def any?
35
- @runners.any?
36
+ @runners_query_options.any?
36
37
  end
37
38
 
38
39
  # @return [void]
39
40
  def feed
40
- runners_query_options = @runners.to_h do |runner|
41
- next_chunk_query_opts = runner.next_chunk_query_opts
42
- next_chunk_query_opts[:to_position] =
43
- [next_chunk_query_opts[:from_position] + INDEX_LOOK_UP_DISTANCE, safe_position].min
44
- [runner.id, next_chunk_query_opts]
41
+ @runners_query_options.each_value do |query_options|
42
+ query_options[:to_position] = [query_options[:from_position] + INDEX_LOOK_UP_DISTANCE, safe_position].min
45
43
  end
46
- grouped_indexes = events_global_index_queries.fetch_indexes_for_subscriptions(runners_query_options)
47
- @runners.each do |runner|
44
+ grouped_indexes = events_global_index_queries.fetch_indexes_for_subscriptions(
45
+ @runners_query_options.transform_keys(&:id)
46
+ )
47
+ @runners_query_options.each do |runner, query_options|
48
48
  if grouped_indexes[runner.id]
49
49
  chunk = Chunks::ReplicaEventsIndexChunk.new(grouped_indexes[runner.id])
50
50
  runner.feed(chunk)
51
51
  else
52
- runner.feed(Chunks::SubscriptionCheckpointChunk.new(runners_query_options[runner.id][:to_position]))
52
+ runner.feed(Chunks::SubscriptionCheckpointChunk.new(query_options[:to_position]))
53
53
  end
54
54
  end
55
55
  end
@@ -3,9 +3,10 @@
3
3
  module PgEventstore
4
4
  # @!visibility private
5
5
  module SubscriptionFeedStrategy
6
- # @param runners [PgEventstore::SubscriptionRunner, Array<PgEventstore::SubscriptionRunner>]
7
- # @return [Array<PgEventstore::SubscriptionRunner>]
8
- def add(*runners)
6
+ # @param runner [PgEventstore::SubscriptionRunner]
7
+ # @param query_options [Hash]
8
+ # @return [void]
9
+ def add(runner, query_options)
9
10
  raise NotImplementedError
10
11
  end
11
12
 
@@ -50,11 +50,6 @@ module PgEventstore
50
50
  }
51
51
  end
52
52
 
53
- # @return [Boolean]
54
- def time_to_feed?
55
- estimate_events_number > 0 && @subscription.last_chunk_fed_at + @subscription.chunk_query_interval <= Time.now.utc
56
- end
57
-
58
53
  private
59
54
 
60
55
  # @return [Integer]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgEventstore
4
- # This class pulls events from db and feeds given SubscriptionRunners
4
+ # This class decides which SubscriptionRunners to feed, pulls events from db and feeds them
5
5
  # @!visibility private
6
6
  class SubscriptionRunnersFeeder
7
7
  # @param config_name [Symbol]
@@ -12,11 +12,15 @@ module PgEventstore
12
12
  # @param runners [Array<PgEventstore::SubscriptionRunner>]
13
13
  # @return [void]
14
14
  def feed(runners)
15
- runners = runners.select(&:running?).select(&:time_to_feed?)
16
- return if runners.empty?
15
+ runners_query_options = runners.select { |runner| runner.running? && time_to_feed?(runner) }.to_h do |runner|
16
+ [runner, runner.next_chunk_query_opts]
17
+ end
18
+ # A runner which has enough events in its queue estimates the number of events to fetch to 0
19
+ runners_query_options = runners_query_options.select { |_, query_options| query_options[:max_count] > 0 }
20
+ return if runners_query_options.empty?
17
21
 
18
22
  feed_strategies_collection = SubscriptionFeedStrategy::Collection.create(
19
- runners,
23
+ runners_query_options,
20
24
  connection,
21
25
  QueryStrategy::Async.new(connection)
22
26
  )
@@ -29,6 +33,13 @@ module PgEventstore
29
33
 
30
34
  private
31
35
 
36
+ # @param runner [PgEventstore::SubscriptionRunner]
37
+ # @return [Boolean]
38
+ def time_to_feed?(runner)
39
+ subscription = runner.subscription
40
+ subscription.last_chunk_fed_at + subscription.chunk_query_interval <= Time.now.utc
41
+ end
42
+
32
43
  # @return [PgEventstore::Connection]
33
44
  def connection
34
45
  PgEventstore.connection(@config_name)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PgEventstore
4
4
  # @return [String]
5
- VERSION = '3.1.1'
5
+ VERSION = '3.1.2'
6
6
  end
@@ -4,7 +4,7 @@ module PgEventstore
4
4
  DEFAULT_SUBSCRIPTIONS_NUM_PER_QUERY: Integer
5
5
 
6
6
  def self.create: (
7
- Array[SubscriptionRunner | ReplicaSubscriptionRunner] runners,
7
+ Hash[SubscriptionRunner | ReplicaSubscriptionRunner, Hash[Symbol, untyped]] runners_query_options,
8
8
  Connection connection,
9
9
  QueryStrategy query_strategy,
10
10
  ?subscriptions_per_query: Integer
@@ -7,7 +7,7 @@ module PgEventstore
7
7
 
8
8
  @connection: Connection
9
9
  @query_strategy: QueryStrategy
10
- @runners: Array[SubscriptionRunner]
10
+ @runners_query_options: Hash[SubscriptionRunner, Hash[Symbol, untyped]]
11
11
 
12
12
  def initialize: (Connection connection, QueryStrategy query_strategy) -> void
13
13
 
@@ -7,7 +7,7 @@ module PgEventstore
7
7
 
8
8
  @connection: Connection
9
9
  @query_strategy: QueryStrategy
10
- @runners: Array[ReplicaSubscriptionRunner]
10
+ @runners_query_options: Hash[ReplicaSubscriptionRunner, Hash[Symbol, untyped]]
11
11
 
12
12
  def initialize: (Connection connection, QueryStrategy query_strategy) -> void
13
13
 
@@ -1,6 +1,6 @@
1
1
  module PgEventstore
2
2
  module SubscriptionFeedStrategy[unchecked Runner]
3
- def add: (*Runner runners) -> Array[Runner]
3
+ def add: (Runner runner, Hash[Symbol, untyped] query_options) -> void
4
4
 
5
5
  def any?: -> bool
6
6
 
@@ -23,8 +23,6 @@ module PgEventstore
23
23
 
24
24
  def next_chunk_query_opts: () -> ::Hash[untyped, untyped]
25
25
 
26
- def time_to_feed?: () -> bool
27
-
28
26
  def next_chunk_global_position: () -> Integer
29
27
 
30
28
  def estimate_events_number: () -> Integer
@@ -8,6 +8,8 @@ module PgEventstore
8
8
 
9
9
  private
10
10
 
11
+ def time_to_feed?: (SubscriptionRunner runner) -> bool
12
+
11
13
  def connection: -> Connection
12
14
  end
13
15
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_eventstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Dzyzenko