onstomp 1.0.3 → 1.0.4
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.
- data/.rspec +1 -1
- data/CHANGELOG.md +10 -0
- data/Gemfile +5 -1
- data/lib/onstomp/client.rb +13 -1
- data/lib/onstomp/connections.rb +9 -5
- data/lib/onstomp/connections/base.rb +144 -38
- data/lib/onstomp/connections/heartbeating.rb +0 -14
- data/lib/onstomp/failover/buffers.rb +1 -0
- data/lib/onstomp/failover/buffers/base.rb +59 -0
- data/lib/onstomp/failover/buffers/receipts.rb +16 -69
- data/lib/onstomp/failover/buffers/written.rb +18 -70
- data/lib/onstomp/failover/failover_configurable.rb +0 -7
- data/lib/onstomp/failover/uri.rb +3 -1
- data/lib/onstomp/interfaces/client_events.rb +1 -1
- data/lib/onstomp/interfaces/connection_events.rb +8 -0
- data/lib/onstomp/interfaces/uri_configurable.rb +7 -0
- data/lib/onstomp/version.rb +1 -1
- data/spec/onstomp/client_spec.rb +13 -1
- data/spec/onstomp/components/scopes/transaction_scope_spec.rb +7 -7
- data/spec/onstomp/connections/base_spec.rb +194 -12
- data/spec/onstomp/connections/heartbeating_spec.rb +0 -24
- data/spec/onstomp/connections_spec.rb +19 -7
- data/spec/onstomp/full_stacks/failover_spec.rb +49 -33
- data/spec/onstomp/full_stacks/onstomp_spec.rb +45 -4
- data/spec/onstomp/full_stacks/onstomp_ssh_spec.rb +10 -2
- data/spec/onstomp/full_stacks/test_broker.rb +21 -18
- metadata +2 -1
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
--colour
|
2
|
-
--format
|
2
|
+
--format p
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 1.0.4
|
4
|
+
* fixed major oversight when using ruby 1.8.7 / jruby with an SSL connection.
|
5
|
+
it will use blocking read/write methods if nonblocking methods are unavailable
|
6
|
+
* added write and read block timeouts. if there is no write activity for a period
|
7
|
+
of time (default of 120 seconds), the connection is assumed to be dead. read
|
8
|
+
activity is handled similarly, but only when performing the CONNECT/CONNECTED
|
9
|
+
exchange, after that we have no way of knowing if the broker just doesn't
|
10
|
+
have any data for us (unless using a STOMP 1.1 connection with heartbeating)
|
11
|
+
* refactored common failover buffer code into lib/onstomp/failover/buffers/base
|
12
|
+
|
3
13
|
## 1.0.3
|
4
14
|
* change how failover spawns new connections when failing over.
|
5
15
|
|
data/Gemfile
CHANGED
data/lib/onstomp/client.rb
CHANGED
@@ -47,6 +47,17 @@ class OnStomp::Client
|
|
47
47
|
# @return [Class]
|
48
48
|
attr_configurable_processor :processor
|
49
49
|
|
50
|
+
# The number of seconds to wait before a write-blocked connection is
|
51
|
+
# considered dead. Defaults to 120 seconds.
|
52
|
+
# @return [Fixnum]
|
53
|
+
attr_configurable_int :write_timeout, :default => 120
|
54
|
+
|
55
|
+
# The number of seconds to wait before a connection that is read-blocked
|
56
|
+
# during the {OnStomp::Connections::Base#connect connect} phase is
|
57
|
+
# considered dead. Defaults to 120 seconds.
|
58
|
+
# @return [Fixnum]
|
59
|
+
attr_configurable_int :read_timeout, :default => 120
|
60
|
+
|
50
61
|
# @api gem:1 STOMP:1.0,1.1
|
51
62
|
# Creates a new client for the specified uri and optional hash of options.
|
52
63
|
# @param [String,URI] uri
|
@@ -71,7 +82,8 @@ class OnStomp::Client
|
|
71
82
|
@connection = OnStomp::Connections.connect self, headers,
|
72
83
|
{ :'accept-version' => @versions.join(','), :host => @host,
|
73
84
|
:'heart-beat' => @heartbeats.join(','), :login => @login,
|
74
|
-
:passcode => @passcode }, pending_connection_events
|
85
|
+
:passcode => @passcode }, pending_connection_events,
|
86
|
+
read_timeout, write_timeout
|
75
87
|
processor_inst.start
|
76
88
|
self
|
77
89
|
end
|
data/lib/onstomp/connections.rb
CHANGED
@@ -44,8 +44,8 @@ module OnStomp::Connections
|
|
44
44
|
# negotiated protocol version
|
45
45
|
# @raise [OnStomp::OnStompError] if negotiating the connection raises an
|
46
46
|
# such an error.
|
47
|
-
def self.connect client, u_head, c_head, con_cbs
|
48
|
-
init_con = create_connection('1.0', nil, client)
|
47
|
+
def self.connect client, u_head, c_head, con_cbs, r_time, w_time
|
48
|
+
init_con = create_connection('1.0', nil, client, r_time, w_time)
|
49
49
|
ver, connected = init_con.connect client, u_head, c_head
|
50
50
|
begin
|
51
51
|
negotiate_connection(ver, init_con, client).tap do |final_con|
|
@@ -61,21 +61,25 @@ module OnStomp::Connections
|
|
61
61
|
private
|
62
62
|
def self.negotiate_connection vers, con, client
|
63
63
|
supports_protocol?(vers,con) ? con :
|
64
|
-
create_connection(vers, con.socket, client
|
64
|
+
create_connection(vers, con.socket, client, con.read_timeout,
|
65
|
+
con.write_timeout)
|
65
66
|
end
|
66
67
|
|
67
68
|
def self.supports_protocol? ver, con
|
68
69
|
con.is_a? PROTOCOL_VERSIONS[ver]
|
69
70
|
end
|
70
71
|
|
71
|
-
def self.create_connection ver, sock, client
|
72
|
+
def self.create_connection ver, sock, client, rt, wt
|
72
73
|
unless sock
|
73
74
|
meth = client.ssl ? :ssl :
|
74
75
|
client.uri.respond_to?(:onstomp_socket_type) ?
|
75
76
|
client.uri.onstomp_socket_type : :tcp
|
76
77
|
sock = __send__(:"create_socket_#{meth}", client)
|
77
78
|
end
|
78
|
-
PROTOCOL_VERSIONS[ver].new
|
79
|
+
PROTOCOL_VERSIONS[ver].new(sock, client).tap do |con|
|
80
|
+
con.read_timeout = rt
|
81
|
+
con.write_timeout = wt
|
82
|
+
end
|
79
83
|
end
|
80
84
|
|
81
85
|
def self.create_socket_tcp client
|
@@ -5,6 +5,7 @@ class OnStomp::Connections::Base
|
|
5
5
|
include OnStomp::Interfaces::ConnectionEvents
|
6
6
|
attr_reader :version, :socket, :client
|
7
7
|
attr_reader :last_transmitted_at, :last_received_at
|
8
|
+
attr_accessor :write_timeout, :read_timeout
|
8
9
|
|
9
10
|
# The approximate maximum number of bytes to write per call to
|
10
11
|
# {#io_process_write}.
|
@@ -26,6 +27,9 @@ class OnStomp::Connections::Base
|
|
26
27
|
@read_buffer = []
|
27
28
|
@client = client
|
28
29
|
@connection_up = false
|
30
|
+
@write_timeout = nil
|
31
|
+
@read_timeout = nil
|
32
|
+
setup_non_blocking_methods
|
29
33
|
end
|
30
34
|
|
31
35
|
# Performs any necessary configuration of the connection from the CONNECTED
|
@@ -71,9 +75,10 @@ class OnStomp::Connections::Base
|
|
71
75
|
until client_con
|
72
76
|
io_process_write { |f| client_con ||= f }
|
73
77
|
end
|
78
|
+
@last_received_at = Time.now
|
74
79
|
broker_con = nil
|
75
80
|
until broker_con
|
76
|
-
io_process_read { |f| broker_con ||= f }
|
81
|
+
io_process_read(true) { |f| broker_con ||= f }
|
77
82
|
end
|
78
83
|
raise OnStomp::ConnectFailedError if broker_con.command != 'CONNECTED'
|
79
84
|
vers = broker_con.header?(:version) ? broker_con[:version] : '1.0'
|
@@ -93,6 +98,20 @@ class OnStomp::Connections::Base
|
|
93
98
|
end
|
94
99
|
end
|
95
100
|
|
101
|
+
# Number of milliseconds since data was last transmitted to the broker or
|
102
|
+
# `nil` if no data has been transmitted when the method is called.
|
103
|
+
# @return [Fixnum, nil]
|
104
|
+
def duration_since_transmitted
|
105
|
+
last_transmitted_at && ((Time.now - last_transmitted_at)*1000).to_i
|
106
|
+
end
|
107
|
+
|
108
|
+
# Number of milliseconds since data was last received from the broker or
|
109
|
+
# `nil` if no data has been received when the method is called.
|
110
|
+
# @return [Fixnum, nil]
|
111
|
+
def duration_since_received
|
112
|
+
last_received_at && ((Time.now - last_received_at)*1000).to_i
|
113
|
+
end
|
114
|
+
|
96
115
|
# Flushes the write buffer by invoking {#io_process_write} until the
|
97
116
|
# buffer is empty.
|
98
117
|
def flush_write_buffer
|
@@ -122,6 +141,7 @@ class OnStomp::Connections::Base
|
|
122
141
|
# @param [OnStomp::Components::Frame]
|
123
142
|
def push_write_buffer data, frame
|
124
143
|
@write_mutex.synchronize {
|
144
|
+
@last_write_activity = Time.now if @write_buffer.empty?
|
125
145
|
@write_buffer << [data, frame] unless @closing
|
126
146
|
}
|
127
147
|
end
|
@@ -146,34 +166,34 @@ class OnStomp::Connections::Base
|
|
146
166
|
# sent to the head of the write buffer to be processed first the next time
|
147
167
|
# this method is invoked.
|
148
168
|
def io_process_write
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
169
|
+
if ready_for_write?
|
170
|
+
to_shift = @write_buffer.length / 3
|
171
|
+
written = 0
|
172
|
+
while written < MAX_BYTES_PER_WRITE
|
173
|
+
data, frame = shift_write_buffer
|
174
|
+
break unless data && connected?
|
175
|
+
begin
|
176
|
+
w = write_nonblock data
|
177
|
+
rescue Errno::EINTR, Errno::EAGAIN, Errno::EWOULDBLOCK
|
178
|
+
# writing will either block, or cannot otherwise be completed,
|
179
|
+
# put data back and try again some other day
|
180
|
+
unshift_write_buffer data, frame
|
181
|
+
break
|
182
|
+
rescue Exception
|
183
|
+
triggered_close $!.message, :terminated
|
184
|
+
raise
|
185
|
+
end
|
186
|
+
written += w
|
187
|
+
@last_write_activity = @last_transmitted_at = Time.now
|
188
|
+
if w < data.length
|
189
|
+
unshift_write_buffer data[w..-1], frame
|
190
|
+
else
|
191
|
+
yield frame if block_given?
|
192
|
+
client.dispatch_transmitted frame
|
172
193
|
end
|
173
194
|
end
|
174
|
-
|
175
|
-
triggered_close
|
176
|
-
raise
|
195
|
+
elsif write_timeout_exceeded?
|
196
|
+
triggered_close 'write blocked', :blocked
|
177
197
|
end
|
178
198
|
if @write_buffer.empty? && @closing
|
179
199
|
triggered_close 'client disconnected'
|
@@ -184,37 +204,123 @@ class OnStomp::Connections::Base
|
|
184
204
|
# and the socket is ready for reading. The received data will be pushed
|
185
205
|
# to the end of a read buffer, which is then sent to the connection's
|
186
206
|
# {OnStomp::Connections::Serializers serializer} for processing.
|
187
|
-
def io_process_read
|
188
|
-
|
189
|
-
|
190
|
-
if data =
|
207
|
+
def io_process_read(check_timeout=false)
|
208
|
+
if ready_for_read?
|
209
|
+
begin
|
210
|
+
if data = read_nonblock
|
191
211
|
@read_buffer << data
|
192
212
|
@last_received_at = Time.now
|
193
213
|
serializer.bytes_to_frame(@read_buffer) do |frame|
|
194
214
|
yield frame if block_given?
|
195
215
|
client.dispatch_received frame
|
196
216
|
end
|
197
|
-
else
|
198
|
-
triggered_close $!.message, :terminated
|
199
217
|
end
|
218
|
+
rescue Errno::EINTR, Errno::EAGAIN, Errno::EWOULDBLOCK
|
219
|
+
# do not
|
220
|
+
rescue EOFError
|
221
|
+
triggered_close $!.message
|
222
|
+
rescue Exception
|
223
|
+
triggered_close $!.message, :terminated
|
224
|
+
raise
|
200
225
|
end
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
226
|
+
elsif check_timeout && read_timeout_exceeded?
|
227
|
+
triggered_close 'read blocked', :blocked
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
def duration_since_write_activity
|
233
|
+
Time.now - @last_write_activity
|
234
|
+
end
|
235
|
+
|
236
|
+
# Returns true if the connection has buffered data to write and the
|
237
|
+
# socket is ready to be written to. If checking the socket's state raises
|
238
|
+
# an exception, the connection will be closed (triggering an
|
239
|
+
# `on_terminated` event) and the error will be re-raised.
|
240
|
+
def ready_for_write?
|
241
|
+
begin
|
242
|
+
@write_buffer.length > 0 && IO.select(nil, [socket], nil, 0.1)
|
205
243
|
rescue Exception
|
206
244
|
triggered_close $!.message, :terminated
|
207
245
|
raise
|
208
246
|
end
|
209
247
|
end
|
210
248
|
|
211
|
-
|
249
|
+
# Returns true if the connection has buffered data to write and the
|
250
|
+
# socket is ready to be written to. If checking the socket's state raises
|
251
|
+
# an exception, the connection will be closed (triggering an
|
252
|
+
# `on_terminated` event) and the error will be re-raised.
|
253
|
+
def ready_for_read?
|
254
|
+
begin
|
255
|
+
connected? && IO.select([socket], nil, nil, 0.1)
|
256
|
+
rescue Exception
|
257
|
+
triggered_close $!.message, :terminated
|
258
|
+
raise
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Returns true if a `write_timeout` has been set, the connection has buffered
|
263
|
+
# data to write, and `duration_since_transmitted` is greater than
|
264
|
+
# `write_timeout`
|
265
|
+
def write_timeout_exceeded?
|
266
|
+
@write_timeout && @write_buffer.length > 0 &&
|
267
|
+
duration_since_write_activity > @write_timeout
|
268
|
+
end
|
269
|
+
|
270
|
+
# Returns true if a `read_timeout` has been set and
|
271
|
+
# `duration_since_received` is greater than `read_timeout`
|
272
|
+
# This is only used when establishing the connection through the CONNECT/
|
273
|
+
# CONNECTED handshake. After that, it is up to heart-beating.
|
274
|
+
def read_timeout_exceeded?
|
275
|
+
@read_timeout && duration_since_received > (@read_timeout*1000)
|
276
|
+
end
|
277
|
+
|
212
278
|
def triggered_close msg, *evs
|
213
279
|
@connection_up = false
|
214
280
|
@closing = false
|
281
|
+
# unless socket.closed?
|
282
|
+
# socket.to_io.shutdown(2) rescue nil
|
283
|
+
#
|
284
|
+
# end
|
215
285
|
socket.close rescue nil
|
216
286
|
evs.each { |ev| trigger_connection_event ev, msg }
|
217
287
|
trigger_connection_event :closed, msg
|
218
288
|
@write_buffer.clear
|
219
289
|
end
|
290
|
+
|
291
|
+
# OpenSSL sockets in Ruby 1.8.7 and JRuby (as of jruby-openssl 0.7.3)
|
292
|
+
# do NOT support non-blocking IO natively. Such a hack, and such a huge
|
293
|
+
# oversight on my part. We define some methods on this instance to use
|
294
|
+
# the right read/write operations. Fortunately, this gets done at
|
295
|
+
# initialization and only has to happen once.
|
296
|
+
def setup_non_blocking_methods
|
297
|
+
read_mod = @socket.respond_to?(:read_nonblock) ? NonblockingRead :
|
298
|
+
BlockingRead
|
299
|
+
write_mod = @socket.respond_to?(:write_nonblock) ? NonblockingWrite :
|
300
|
+
BlockingWrite
|
301
|
+
self.extend read_mod
|
302
|
+
self.extend write_mod
|
303
|
+
end
|
304
|
+
|
305
|
+
module NonblockingRead
|
306
|
+
def read_nonblock
|
307
|
+
socket.read_nonblock MAX_BYTES_PER_READ
|
308
|
+
end
|
309
|
+
end
|
310
|
+
module NonblockingWrite
|
311
|
+
def write_nonblock data
|
312
|
+
socket.write_nonblock data
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
module BlockingRead
|
317
|
+
def read_nonblock
|
318
|
+
socket.readpartial MAX_BYTES_PER_READ
|
319
|
+
end
|
320
|
+
end
|
321
|
+
module BlockingWrite
|
322
|
+
def write_nonblock data
|
323
|
+
socket.write data
|
324
|
+
end
|
325
|
+
end
|
220
326
|
end
|
@@ -51,20 +51,6 @@ module OnStomp::Connections::Heartbeating
|
|
51
51
|
end
|
52
52
|
@heartbeat_broker_limit
|
53
53
|
end
|
54
|
-
|
55
|
-
# Number of milliseconds since data was last transmitted to the broker or
|
56
|
-
# `nil` if no data has been transmitted when the method is called.
|
57
|
-
# @return [Fixnum, nil]
|
58
|
-
def duration_since_transmitted
|
59
|
-
last_transmitted_at && ((Time.now - last_transmitted_at)*1000).to_i
|
60
|
-
end
|
61
|
-
|
62
|
-
# Number of milliseconds since data was last received from the broker or
|
63
|
-
# `nil` if no data has been received when the method is called.
|
64
|
-
# @return [Fixnum, nil]
|
65
|
-
def duration_since_received
|
66
|
-
last_received_at && ((Time.now - last_received_at)*1000).to_i
|
67
|
-
end
|
68
54
|
|
69
55
|
# Returns true if client-side heartbeating is disabled, or
|
70
56
|
# {#duration_since_transmitted} has not exceeded {#heartbeat_client_limit}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# The base class for all buffers. This class exists mostly as a factoring
|
4
|
+
# out of the code shared between the {OnStomp::Failover::Buffers::Written}
|
5
|
+
# and {OnStomp::Failover::Buffers::Receipts} buffers.
|
6
|
+
class OnStomp::Failover::Buffers::Base
|
7
|
+
def initialize failover
|
8
|
+
@failover = failover
|
9
|
+
@buffer_mutex = Mutex.new
|
10
|
+
@buffer = []
|
11
|
+
@txs = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the number of frames currently sitting in the buffer.
|
15
|
+
# @return [Fixnum]
|
16
|
+
def buffered
|
17
|
+
@buffer.length
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def add_to_buffer f, heads={}
|
22
|
+
@buffer_mutex.synchronize do
|
23
|
+
unless f.header? :'x-onstomp-failover-replay'
|
24
|
+
f.headers.reverse_merge! heads
|
25
|
+
@buffer << f
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_to_transactions f, heads={}
|
31
|
+
@txs[f[:transaction]] = true
|
32
|
+
add_to_buffer f, heads
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_from_transactions f
|
36
|
+
tx = f[:transaction]
|
37
|
+
if @txs.delete tx
|
38
|
+
@buffer_mutex.synchronize do
|
39
|
+
@buffer.reject! { |bf| bf[:transaction] == tx }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove_subscribe_from_buffer f
|
45
|
+
@buffer_mutex.synchronize do
|
46
|
+
@buffer.reject! { |bf| bf.command == 'SUBSCRIBE' && bf[:id] == f[:id] }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def replay_buffer client
|
51
|
+
replay_frames = @buffer_mutex.synchronize do
|
52
|
+
@buffer.select { |f| f[:'x-onstomp-failover-replay'] = '1'; true }
|
53
|
+
end
|
54
|
+
|
55
|
+
replay_frames.each do |f|
|
56
|
+
client.transmit f
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -6,79 +6,38 @@
|
|
6
6
|
# {OnStomp::Failover::Client failover} client reconnects.
|
7
7
|
# @todo Quite a lot of this code is shared between Written and Receipts,
|
8
8
|
# we'll want to factor the common stuff out.
|
9
|
-
class OnStomp::Failover::Buffers::Receipts
|
9
|
+
class OnStomp::Failover::Buffers::Receipts < OnStomp::Failover::Buffers::Base
|
10
10
|
def initialize failover
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
failover.
|
18
|
-
|
19
|
-
|
20
|
-
failover.before_begin &method(:buffer_transaction)
|
11
|
+
super
|
12
|
+
[:send, :commit, :abort, :subscribe].each do |ev|
|
13
|
+
failover.__send__(:"before_#{ev}") do |f, *_|
|
14
|
+
add_to_buffer f, {:receipt => OnStomp.next_serial}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
failover.before_begin do |f, *_|
|
18
|
+
add_to_transactions f, {:receipt => OnStomp.next_serial}
|
19
|
+
end
|
21
20
|
# We can scrub the subscription before UNSUBSCRIBE is fully written
|
22
21
|
# because if we replay before UNSUBSCRIBE was sent, we still don't
|
23
22
|
# want to be subscribed when we reconnect.
|
24
|
-
failover.before_unsubscribe
|
25
|
-
|
26
|
-
|
27
|
-
failover.on_failover_connected &method(:replay)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Adds a frame to a buffer so that it may be replayed if the
|
31
|
-
# {OnStomp::Failover::Client failover} client re-connects
|
32
|
-
def buffer_frame f, *_
|
33
|
-
@buffer_mutex.synchronize do
|
34
|
-
# Don't re-buffer frames that are being replayed.
|
35
|
-
unless f.header? :'x-onstomp-failover-replay'
|
36
|
-
# Create a receipt header, unless the frame already has one.
|
37
|
-
f[:receipt] = OnStomp.next_serial unless f.header?(:receipt)
|
38
|
-
@buffer << f
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Records the start of a transaction so that it may be replayed if the
|
44
|
-
# {OnStomp::Failover::Client failover} client re-connects
|
45
|
-
def buffer_transaction f, *_
|
46
|
-
@txs[f[:transaction]] = true
|
47
|
-
buffer_frame f
|
48
|
-
end
|
49
|
-
|
50
|
-
# Removes the recorded transaction from the buffer after it has been
|
51
|
-
# written the broker socket so that it will not be replayed when the
|
52
|
-
# {OnStomp::Failover::Client failover} client re-connects
|
53
|
-
def debuffer_transaction f
|
54
|
-
tx = f[:transaction]
|
55
|
-
if @txs.delete tx
|
56
|
-
@buffer_mutex.synchronize do
|
57
|
-
@buffer.reject! { |bf| bf[:transaction] == tx }
|
58
|
-
end
|
23
|
+
failover.before_unsubscribe do |f, *_|
|
24
|
+
remove_subscribe_from_buffer f
|
59
25
|
end
|
26
|
+
failover.on_receipt { |r, *_| debuffer_frame r }
|
27
|
+
failover.on_failover_connected { |f,c,*_| replay_buffer c }
|
60
28
|
end
|
61
29
|
|
62
|
-
# Removes the matching SUBSCRIBE frame from the buffer after the
|
63
|
-
# UNSUBSCRIBE has been added to the connection's write buffer
|
64
|
-
# so that it will not be replayed when the
|
65
|
-
# {OnStomp::Failover::Client failover} client re-connects
|
66
|
-
def debuffer_subscription f, *_
|
67
|
-
@buffer_mutex.synchronize do
|
68
|
-
@buffer.reject! { |bf| bf.command == 'SUBSCRIBE' && bf[:id] == f[:id] }
|
69
|
-
end
|
70
|
-
end
|
71
30
|
|
72
31
|
# Removes frames that neither transactional nor SUBSCRIBEs from the buffer
|
73
32
|
# by looking the buffered frames up by their `receipt` header.
|
74
|
-
def debuffer_frame r
|
33
|
+
def debuffer_frame r
|
75
34
|
orig = @buffer_mutex.synchronize do
|
76
35
|
@buffer.detect { |f| f[:receipt] == r[:'receipt-id'] }
|
77
36
|
end
|
78
37
|
if orig
|
79
38
|
# COMMIT and ABORT debuffer the whole transaction sequence
|
80
39
|
if ['COMMIT', 'ABORT'].include? orig.command
|
81
|
-
|
40
|
+
remove_from_transactions orig
|
82
41
|
# Otherwise, if this isn't part of a transaction, debuffer the
|
83
42
|
# particular frame (if it's not a SUBSCRIBE)
|
84
43
|
elsif orig.command != 'SUBSCRIBE' && !orig.header?(:transaction)
|
@@ -86,16 +45,4 @@ class OnStomp::Failover::Buffers::Receipts
|
|
86
45
|
end
|
87
46
|
end
|
88
47
|
end
|
89
|
-
|
90
|
-
# Called when the {OnStomp::Failover::Client failover} client triggers
|
91
|
-
# `on_failover_connected` to start replaying any frames in the buffer.
|
92
|
-
def replay fail, client, *_
|
93
|
-
replay_frames = @buffer_mutex.synchronize do
|
94
|
-
@buffer.select { |f| f[:'x-onstomp-failover-replay'] = '1'; true }
|
95
|
-
end
|
96
|
-
|
97
|
-
replay_frames.each do |f|
|
98
|
-
client.transmit f
|
99
|
-
end
|
100
|
-
end
|
101
48
|
end
|