artery 1.4.1 → 1.4.3

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: 1a24ddb4d77a16718ba5a3f736d54decefc74e698d2d5dad2564450845e4848c
4
- data.tar.gz: eb438118c9b36740988e7c17d00a8c242b6bd836cbe0204a81bf923ed3ab42f5
3
+ metadata.gz: 6fc6ee867a6031d7ad94fa40630261f079c23664833cb79a40d7100445967d27
4
+ data.tar.gz: 38bc318bbc6d76c4ac86138c0fa60e2f61001cc9e869a36f2931243bac208780
5
5
  SHA512:
6
- metadata.gz: fb73eef213f1316758158ff5282c854fcc9b1dd602281921933ecc3c8fa4c9d79f4a1a731a7f2e26581fff789425e8e7b0dd96a5bb82a00fd734e21415fc3b39
7
- data.tar.gz: 9b62f659e797ab808df25ed4c719d94a9d0c0f3ef80d445f432fbe810bba6901d6c7cfe4fea4d58584de5f0d6d836919ee46b52cff7c2a559fb2d7ff4dbd4bef
6
+ metadata.gz: '09d04bc9c56091cb5f9092c882b9127e5362092ccee797cba608dc3dd59131277a283dc85a852bd5e1ff5bdebb6276c5f1c415417cfc1eb3679d854c8754b8d7'
7
+ data.tar.gz: dcd7b59c4288f0669aff6141b21e46aaaef2e7e5467b41d98d1d108a5f83729d6aea1bbe96c9e2d6c1cec501cd318cd59bd09c00c979234dfb8f598133118695
@@ -20,7 +20,10 @@ module Artery
20
20
  end
21
21
 
22
22
  def latest_index(model)
23
- where(model: model).maximum(:id).to_i
23
+ last_message_id = where(model: model).maximum(:id).to_i
24
+ return last_message_id unless last_message_id.zero?
25
+
26
+ Artery.model_info_class.last_published_id_for(model)
24
27
  end
25
28
 
26
29
  def delete_old
@@ -31,6 +31,10 @@ module Artery
31
31
 
32
32
  row
33
33
  end
34
+
35
+ def self.last_published_id_for(model_name)
36
+ where(model: model_name).limit(1).pluck(:last_published_id).first.to_i
37
+ end
34
38
  end
35
39
  end
36
40
  end
@@ -48,7 +48,22 @@ module Artery
48
48
  model ||= artery_model_name
49
49
  action ||= '*'
50
50
 
51
- artery_add_subscription Routing.uri(service: service, model: model, action: action), kwargs, &blk
51
+ artery_add_subscription Routing.uri(service: service, model: model, action: action), **kwargs, client: true,
52
+ &blk
53
+ end
54
+
55
+ def artery_rewind_message_counter!(service: nil) # rubocop:disable Naming/PredicateMethod
56
+ return false if artery_source_model?
57
+
58
+ subscriptions = artery[:subscriptions]&.select do |subscription|
59
+ next false unless subscription.rewindable?
60
+
61
+ service.nil? || subscription.uri.service.to_sym == service.to_sym
62
+ end
63
+ return false if subscriptions.blank?
64
+
65
+ subscriptions.each(&:rewind_message_counter!)
66
+ true
52
67
  end
53
68
 
54
69
  # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
@@ -4,7 +4,7 @@ require 'concurrent'
4
4
 
5
5
  module Artery
6
6
  class Publisher
7
- DISCOVERY_INTERVAL = 5
7
+ DISCOVERY_INTERVAL = 30
8
8
  POLL_INTERVAL = 0.5
9
9
  BATCH_SIZE = 100
10
10
 
@@ -27,44 +27,64 @@ module Artery
27
27
  max_queue: 0,
28
28
  fallback_policy: :caller_runs
29
29
  )
30
- @running_models = Concurrent::Set.new
30
+ @known_models = Concurrent::Set.new
31
+ @busy_models = Concurrent::Set.new
32
+ @last_discovery = Time.at(0)
31
33
 
32
34
  Instrumentation.instrument(:publisher, action: :started)
33
35
 
34
36
  loop do
35
- models = Artery.model_info_class.pluck(:model)
37
+ discover_models if discovery_due?
36
38
 
37
- models.each do |model|
38
- next if @running_models.include?(model)
39
+ @known_models.each do |model|
40
+ next if @busy_models.include?(model)
39
41
 
40
- @running_models.add(model)
41
- @pool.post { model_loop(model) }
42
+ @busy_models.add(model)
43
+ @pool.post { process_model(model) }
42
44
  end
43
45
 
44
- sleep DISCOVERY_INTERVAL
46
+ sleep POLL_INTERVAL
45
47
  end
46
48
  end
47
49
 
48
- def model_loop(model)
49
- Artery.logger.tagged('Publisher', model) do
50
+ def discover_models
51
+ current = Artery.model_info_class.pluck(:model)
52
+
53
+ (@known_models - current).each do |removed|
54
+ @known_models.delete(removed)
55
+ Instrumentation.instrument(:publisher, action: :model_removed, model: removed)
56
+ end
57
+
58
+ current.each do |model|
59
+ next if @known_models.include?(model)
60
+
61
+ @known_models.add(model)
50
62
  Artery.model_info_class.ensure_initialized!(model)
51
63
  Instrumentation.instrument(:publisher, action: :model_started, model: model)
64
+ end
52
65
 
66
+ @last_discovery = Time.now
67
+ end
68
+
69
+ def discovery_due?
70
+ Time.now - @last_discovery >= DISCOVERY_INTERVAL
71
+ end
72
+
73
+ def process_model(model)
74
+ Artery.logger.tagged('Publisher', model) do
53
75
  loop do
54
76
  published = publish_batch(model)
55
- sleep POLL_INTERVAL if published < BATCH_SIZE
77
+ break if published < BATCH_SIZE
56
78
  end
57
- rescue StandardError => e
58
- Instrumentation.instrument(:publisher, action: :error, model: model, error: e.message)
59
- Artery.handle_error Error.new(
60
- "Publisher error for #{model}: #{e.message}",
61
- original_exception: e
62
- )
63
- sleep POLL_INTERVAL
64
- retry
65
79
  end
80
+ rescue StandardError => e
81
+ Instrumentation.instrument(:publisher, action: :error, model: model, error: e.message)
82
+ Artery.handle_error Error.new(
83
+ "Publisher error for #{model}: #{e.message}",
84
+ original_exception: e
85
+ )
66
86
  ensure
67
- @running_models.delete(model)
87
+ @busy_models&.delete(model)
68
88
  end
69
89
 
70
90
  def publish_batch(model)
@@ -97,8 +97,54 @@ module Artery
97
97
  end
98
98
  end
99
99
 
100
+ def rewind_message_counter! # rubocop:disable Naming/PredicateMethod
101
+ return false unless rewindable?
102
+
103
+ local_index = latest_message_index
104
+ return false unless local_index.positive?
105
+
106
+ remote_index = request_publisher_latest_index!
107
+ return false unless remote_index.positive?
108
+ return false if remote_index >= local_index
109
+
110
+ Artery.logger.info(
111
+ "Rewinding #{uri} latest_index from #{local_index} to #{remote_index}"
112
+ )
113
+ info.update!(latest_index: remote_index)
114
+ true
115
+ end
116
+
100
117
  private
101
118
 
119
+ def request_publisher_latest_index!
120
+ route = Routing.uri(
121
+ service: uri.service,
122
+ model: uri.model,
123
+ plural: true,
124
+ action: :get_all
125
+ ).to_route
126
+
127
+ remote_index = nil
128
+
129
+ Artery.request(
130
+ route,
131
+ representation: representation_name,
132
+ scope: synchronize_updates_scope,
133
+ page: 0,
134
+ per_page: 1
135
+ ) do |on|
136
+ on.success do |data|
137
+ remote_index = data[:_index].to_i
138
+ end
139
+
140
+ on.error do |error|
141
+ raise Error, "Failed to fetch artery index for #{uri}: #{error.message}"
142
+ end
143
+ end
144
+
145
+ remote_index.to_i
146
+ end
147
+
102
148
  def run_synchronization_heartbeat
103
149
  return if @heartbeat_thread
104
150
 
@@ -28,6 +28,11 @@ module Artery
28
28
  @info ||= Artery.subscription_info_class.find_for_subscription(self)
29
29
  end
30
30
 
31
+ def reset_info!
32
+ @info = nil
33
+ self
34
+ end
35
+
31
36
  def representation_name
32
37
  options[:representation]
33
38
  end
@@ -40,6 +45,14 @@ module Artery
40
45
  @subscriber.artery[:source]
41
46
  end
42
47
 
48
+ def client?
49
+ options[:client]
50
+ end
51
+
52
+ def rewindable?
53
+ client? && synchronize_updates?
54
+ end
55
+
43
56
  def latest_outgoing_message_index
44
57
  return unless source?
45
58
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Artery
4
- VERSION = '1.4.1'
4
+ VERSION = '1.4.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: artery
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Gnuskov