forest_admin_rpc_agent 1.16.4 → 1.16.5

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: 9fc96b913c73d5ac4922e3c2366a73d56a7a65c1eee60428f49d2f19a770f05a
4
- data.tar.gz: 8e1417b461af67e525b8e1af06483d001b37ad11ec9e7a0c4ce12e452706fafd
3
+ metadata.gz: 61c267e0f4478a14bb31888e27d27c667074664e3edc3f63d60e71d3a64b84d8
4
+ data.tar.gz: 8dcfa34e4f3be0c81434999b363036bd0806fc210a07f1f833fa643ce99b6efe
5
5
  SHA512:
6
- metadata.gz: 9ac705763d0e5afa2357e3701616ea8693f65902c904c584303726f2610b4a2c5b9b2ececd6c8bb866c5acd116bc6923cf2d6183aeb3b6a400519a7c4391e849
7
- data.tar.gz: baa4798d78bfff90cb23946253bf4bed7e73959aa6da59902fcee836ccf95a5eb9ad1f316bed51dd3e0788f54d08d6b0bba0347fd9fcef96410d3cf38c1cc95a
6
+ metadata.gz: ed2f6d0a8470a07fd3ba641f4ec53ddf2ed2dab751bdeaed8cfa48a4a897891f61584c5e3117e7cc7acb0d56a04516758f5f3281f55d5ae50b2bc9872868ebe2
7
+ data.tar.gz: 6e69218d6db3390e6f86fd0c1ac39d9792bae33768fa4f42389ca09d82ec1eaa6c332de6423e483904340c56bfdc7d06fb8b8299d6910af5351b2dc153115dbb
@@ -3,7 +3,7 @@ require 'jsonapi-serializers'
3
3
  module ForestAdminRpcAgent
4
4
  module Routes
5
5
  class Sse
6
- DEFAULT_HEARTBEAT_INTERVAL = 1
6
+ DEFAULT_HEARTBEAT_INTERVAL = 10
7
7
 
8
8
  def initialize(url = 'sse', method = 'get', name = 'rpc_sse', heartbeat_interval: DEFAULT_HEARTBEAT_INTERVAL)
9
9
  @url = url
@@ -37,11 +37,13 @@ module ForestAdminRpcAgent
37
37
  'X-Accel-Buffering' => 'no'
38
38
 
39
39
  stream(:keep_open) do |out|
40
- should_continue = true
40
+ # Register this connection; any previous connection will be terminated
41
+ connection = ForestAdminRpcAgent::SseConnectionManager.register_connection
42
+
41
43
  server_stopped = false
42
44
  received_signal = nil
43
45
  stop_proc = proc do |sig|
44
- should_continue = false
46
+ connection.terminate
45
47
  server_stopped = true
46
48
  received_signal = sig
47
49
  end
@@ -51,7 +53,7 @@ module ForestAdminRpcAgent
51
53
  begin
52
54
  streamer = SseStreamer.new(out)
53
55
 
54
- while should_continue
56
+ while connection.active?
55
57
  streamer.write('', event: 'heartbeat')
56
58
  sleep route_instance.instance_variable_get(:@heartbeat_interval)
57
59
  end
@@ -71,6 +73,7 @@ module ForestAdminRpcAgent
71
73
  ensure
72
74
  trap('INT', original_int_handler)
73
75
  trap('TERM', original_term_handler)
76
+ ForestAdminRpcAgent::SseConnectionManager.unregister_connection(connection)
74
77
  out.close if out.respond_to?(:close)
75
78
 
76
79
  # Re-send the signal to allow proper server shutdown
@@ -95,11 +98,13 @@ module ForestAdminRpcAgent
95
98
  'X-Accel-Buffering' => 'no'
96
99
  }
97
100
 
98
- should_continue = true
101
+ # Register this connection; any previous connection will be terminated
102
+ connection = ForestAdminRpcAgent::SseConnectionManager.register_connection
103
+
99
104
  server_stopped = false
100
105
  received_signal = nil
101
106
  stop_proc = proc do |sig|
102
- should_continue = false
107
+ connection.terminate
103
108
  server_stopped = true
104
109
  received_signal = sig
105
110
  end
@@ -112,7 +117,7 @@ module ForestAdminRpcAgent
112
117
  begin
113
118
  ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', '[SSE] Starting stream')
114
119
 
115
- while should_continue
120
+ while connection.active?
116
121
  stream.write('', event: 'heartbeat')
117
122
  sleep route_instance.instance_variable_get(:@heartbeat_interval)
118
123
  end
@@ -135,6 +140,7 @@ module ForestAdminRpcAgent
135
140
  ensure
136
141
  trap('INT', original_int_handler)
137
142
  trap('TERM', original_term_handler)
143
+ ForestAdminRpcAgent::SseConnectionManager.unregister_connection(connection)
138
144
  ForestAdminRpcAgent::Facades::Container.logger&.log('Debug', '[SSE] Stream stopped')
139
145
 
140
146
  # Re-send the signal to allow proper server shutdown
@@ -0,0 +1,81 @@
1
+ module ForestAdminRpcAgent
2
+ # Manages SSE connections to ensure only one active connection at a time.
3
+ # When a new connection is established, the previous one is terminated.
4
+ # This prevents zombie loops when the master restarts and reconnects.
5
+ class SseConnectionManager
6
+ @mutex = Mutex.new
7
+ @current_connection = nil
8
+
9
+ class << self
10
+ # Registers a new SSE connection and terminates any existing one.
11
+ # Returns a connection object that can be used to check if the connection is still active.
12
+ def register_connection
13
+ connection = Connection.new
14
+
15
+ @mutex.synchronize do
16
+ # Terminate the previous connection if it exists
17
+ if @current_connection
18
+ ForestAdminRpcAgent::Facades::Container.logger&.log(
19
+ 'Debug',
20
+ '[SSE ConnectionManager] Terminating previous connection'
21
+ )
22
+ @current_connection.terminate
23
+ end
24
+
25
+ @current_connection = connection
26
+ ForestAdminRpcAgent::Facades::Container.logger&.log(
27
+ 'Debug',
28
+ "[SSE ConnectionManager] New connection registered (id: #{connection.id})"
29
+ )
30
+ end
31
+
32
+ connection
33
+ end
34
+
35
+ # Unregisters a connection when it's closed normally.
36
+ def unregister_connection(connection)
37
+ @mutex.synchronize do
38
+ if @current_connection&.id == connection.id
39
+ @current_connection = nil
40
+ ForestAdminRpcAgent::Facades::Container.logger&.log(
41
+ 'Debug',
42
+ "[SSE ConnectionManager] Connection unregistered (id: #{connection.id})"
43
+ )
44
+ end
45
+ end
46
+ end
47
+
48
+ # Returns the current active connection (for testing purposes)
49
+ def current_connection
50
+ @mutex.synchronize { @current_connection }
51
+ end
52
+
53
+ # Resets the manager state (for testing purposes)
54
+ def reset!
55
+ @mutex.synchronize do
56
+ @current_connection&.terminate
57
+ @current_connection = nil
58
+ end
59
+ end
60
+ end
61
+
62
+ # Represents an individual SSE connection
63
+ class Connection
64
+ attr_reader :id
65
+
66
+ def initialize
67
+ @id = SecureRandom.uuid
68
+ @active = true
69
+ @mutex = Mutex.new
70
+ end
71
+
72
+ def active?
73
+ @mutex.synchronize { @active }
74
+ end
75
+
76
+ def terminate
77
+ @mutex.synchronize { @active = false }
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,3 +1,3 @@
1
1
  module ForestAdminRpcAgent
2
- VERSION = "1.16.4"
2
+ VERSION = "1.16.5"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_admin_rpc_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.16.4
4
+ version: 1.16.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu
@@ -178,6 +178,7 @@ files:
178
178
  - lib/forest_admin_rpc_agent/routes/schema.rb
179
179
  - lib/forest_admin_rpc_agent/routes/sse.rb
180
180
  - lib/forest_admin_rpc_agent/routes/update.rb
181
+ - lib/forest_admin_rpc_agent/sse_connection_manager.rb
181
182
  - lib/forest_admin_rpc_agent/sse_streamer.rb
182
183
  - lib/forest_admin_rpc_agent/thor/install.rb
183
184
  - lib/forest_admin_rpc_agent/version.rb