onstomp 1.0.0pre1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/README.md +1 -1
  2. data/Rakefile +8 -0
  3. data/examples/openuri.rb +36 -0
  4. data/lib/onstomp.rb +4 -0
  5. data/lib/onstomp/client.rb +6 -5
  6. data/lib/onstomp/components.rb +0 -1
  7. data/lib/onstomp/components/frame_headers.rb +35 -38
  8. data/lib/onstomp/components/threaded_processor.rb +13 -0
  9. data/lib/onstomp/connections/base.rb +15 -8
  10. data/lib/onstomp/connections/stomp_1.rb +0 -6
  11. data/lib/onstomp/connections/stomp_1_0.rb +8 -0
  12. data/lib/onstomp/connections/stomp_1_1.rb +8 -0
  13. data/lib/onstomp/failover.rb +16 -0
  14. data/lib/onstomp/failover/buffers.rb +8 -0
  15. data/lib/onstomp/failover/buffers/written.rb +91 -0
  16. data/lib/onstomp/failover/client.rb +127 -0
  17. data/lib/onstomp/failover/failover_configurable.rb +63 -0
  18. data/lib/onstomp/failover/failover_events.rb +96 -0
  19. data/lib/onstomp/failover/new_with_failover.rb +20 -0
  20. data/lib/onstomp/failover/pools.rb +8 -0
  21. data/lib/onstomp/failover/pools/base.rb +39 -0
  22. data/lib/onstomp/failover/pools/round_robin.rb +17 -0
  23. data/lib/onstomp/failover/uri.rb +34 -0
  24. data/lib/onstomp/interfaces/client_configurable.rb +2 -6
  25. data/lib/onstomp/interfaces/client_events.rb +4 -0
  26. data/lib/onstomp/interfaces/connection_events.rb +3 -3
  27. data/lib/onstomp/interfaces/event_manager.rb +8 -0
  28. data/lib/onstomp/interfaces/uri_configurable.rb +7 -7
  29. data/lib/onstomp/open-uri.rb +37 -0
  30. data/lib/onstomp/open-uri/client_extensions.rb +88 -0
  31. data/lib/onstomp/open-uri/message_queue.rb +38 -0
  32. data/lib/onstomp/version.rb +1 -1
  33. data/spec/onstomp/client_spec.rb +1 -4
  34. data/spec/onstomp/components/frame_headers_spec.rb +2 -5
  35. data/spec/onstomp/connections/stomp_1_0_spec.rb +22 -0
  36. data/spec/onstomp/connections/stomp_1_1_spec.rb +22 -0
  37. data/spec/onstomp/connections/stomp_1_spec.rb +2 -19
  38. data/spec/onstomp/connections_spec.rb +4 -0
  39. data/spec/onstomp/failover/buffers/written_spec.rb +8 -0
  40. data/spec/onstomp/failover/client_spec.rb +38 -0
  41. data/spec/onstomp/failover/failover_events_spec.rb +75 -0
  42. data/spec/onstomp/failover/new_with_failover_spec.rb +16 -0
  43. data/spec/onstomp/failover/pools/base_spec.rb +54 -0
  44. data/spec/onstomp/failover/pools/round_robin_spec.rb +27 -0
  45. data/spec/onstomp/failover/uri_spec.rb +21 -0
  46. data/spec/onstomp/full_stacks/failover_spec.rb +55 -0
  47. data/spec/onstomp/full_stacks/onstomp_spec.rb +15 -0
  48. data/spec/onstomp/full_stacks/open-uri_spec.rb +40 -0
  49. data/spec/onstomp/full_stacks/ssl/README +6 -0
  50. data/spec/onstomp/full_stacks/ssl/broker_cert.csr +17 -0
  51. data/spec/onstomp/full_stacks/ssl/broker_cert.pem +72 -0
  52. data/spec/onstomp/full_stacks/ssl/broker_key.pem +27 -0
  53. data/spec/onstomp/full_stacks/ssl/client_cert.csr +17 -0
  54. data/spec/onstomp/full_stacks/ssl/client_cert.pem +72 -0
  55. data/spec/onstomp/full_stacks/ssl/client_key.pem +27 -0
  56. data/spec/onstomp/full_stacks/ssl/demoCA/cacert.pem +17 -0
  57. data/spec/onstomp/full_stacks/ssl/demoCA/index.txt +2 -0
  58. data/spec/onstomp/full_stacks/ssl/demoCA/index.txt.attr +1 -0
  59. data/spec/onstomp/full_stacks/ssl/demoCA/index.txt.attr.old +1 -0
  60. data/spec/onstomp/full_stacks/ssl/demoCA/index.txt.old +1 -0
  61. data/spec/onstomp/full_stacks/ssl/demoCA/newcerts/01.pem +72 -0
  62. data/spec/onstomp/full_stacks/ssl/demoCA/newcerts/02.pem +72 -0
  63. data/spec/onstomp/full_stacks/ssl/demoCA/private/cakey.pem +17 -0
  64. data/spec/onstomp/full_stacks/ssl/demoCA/serial +1 -0
  65. data/spec/onstomp/full_stacks/ssl/demoCA/serial.old +1 -0
  66. data/spec/onstomp/full_stacks/test_broker.rb +251 -0
  67. data/spec/onstomp/interfaces/connection_events_spec.rb +3 -1
  68. data/spec/onstomp/open-uri/client_extensions_spec.rb +113 -0
  69. data/spec/onstomp/open-uri/message_queue_spec.rb +29 -0
  70. data/spec/onstomp/open-uri_spec.rb +43 -0
  71. data/spec/spec_helper.rb +2 -0
  72. data/yard_extensions.rb +5 -1
  73. metadata +82 -8
  74. data/lib/onstomp/components/nil_processor.rb +0 -20
  75. data/spec/onstomp/components/nil_processor_spec.rb +0 -32
data/README.md CHANGED
@@ -62,7 +62,7 @@ See the full {file:docs/LICENSE.md LICENSE} for details.
62
62
  There are a few people/groups I'd like to thank for helping me with the
63
63
  creation of this gem.
64
64
 
65
- * Lionel Cons the good suggestions while I was implementing support for the
65
+ * Lionel Cons for the good suggestions while I was implementing support for the
66
66
  STOMP 1.1 spec. Check out his Perl client [Net::STOMP::Client](http://search.cpan.org/~lcons/Net-STOMP-Client-0.9.5/lib/Net/STOMP/Client.pm)
67
67
  * Brian McCallister, Johan Sørensen, Guy M. Allard and Thiago Morello for
68
68
  their work on the `stomp` gem which introduced me to the STOMP protocol.
data/Rakefile CHANGED
@@ -4,3 +4,11 @@ Bundler::GemHelper.install_tasks
4
4
  require 'yard'
5
5
  require File.expand_path("../yard_extensions", __FILE__)
6
6
  YARD::Rake::YardocTask.new
7
+
8
+ require 'rspec/core/rake_task'
9
+ desc "Run specs"
10
+ RSpec::Core::RakeTask.new do |t|
11
+ t.verbose = false
12
+ end
13
+
14
+ task :default => :spec
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.unshift(File.expand_path('../../lib', __FILE__))
3
+ require 'onstomp'
4
+ require 'onstomp/open-uri'
5
+
6
+ open('stomp://localhost/queue/onstomp/openuri') do |c|
7
+ c.send "hello world!"
8
+ c.send "a fine day to you"
9
+ c.send "what rhymes with you?"
10
+ c.puts "may the rats eat your eyes"
11
+ c.send "I am now lost to your cause"
12
+ c.puts "the inquisition is here for a reason"
13
+ end
14
+
15
+ open('stomp://localhost/queue/onstomp/openuri') do |c|
16
+ c.each do |m|
17
+ puts "Got a message: #{m.body}"
18
+ break if m.body == 'what rhymes with you?'
19
+ end
20
+
21
+ c.take(2).each { |m| puts "From take(2): #{m.body}" }
22
+
23
+ puts "And finally: #{c.first.body}"
24
+ end
25
+
26
+ puts "Done with open-uri examples!"
27
+
28
+ # Example output:
29
+ #
30
+ # Got a message: hello world!
31
+ # Got a message: a fine day to you
32
+ # Got a message: what rhymes with you?
33
+ # From take(2): may the rats eat your eyes
34
+ # From take(2): I am now lost to your cause
35
+ # And finally: the inquisition is here for a reason
36
+ # Done with open-uri examples!
@@ -33,6 +33,10 @@ require 'monitor'
33
33
 
34
34
  # Primary namespace for the +onstomp+ gem
35
35
  module OnStomp
36
+ # Class to use for creating enumerator objects, which depends upon the
37
+ # version of Ruby being used.
38
+ ENUMERATOR_KLASS = (RUBY_VERSION >= '1.9') ? Enumerator : Enumerable::Enumerator
39
+
36
40
  # A common base class for errors raised by the OnStomp gem
37
41
  # @abstract
38
42
  class OnStompError < StandardError; end
@@ -50,7 +50,7 @@ class OnStomp::Client
50
50
  # Creates a new client for the specified uri and optional hash of options.
51
51
  # @param [String,URI] uri
52
52
  # @param [{Symbol => Object}] options
53
- def initialize(uri, options={})
53
+ def initialize uri, options={}
54
54
  @uri = uri.is_a?(::URI) ? uri : ::URI.parse(uri)
55
55
  @ssl = options.delete(:ssl)
56
56
  configure_configurable options
@@ -65,7 +65,7 @@ class OnStomp::Client
65
65
  # headers in the CONNECT frame, if specified.
66
66
  # @param [{#to_sym => #to_s}] headers
67
67
  # @return [self]
68
- def connect(headers={})
68
+ def connect headers={}
69
69
  @connection = OnStomp::Connections.connect self, headers,
70
70
  { :'accept-version' => @versions.join(','), :host => @host,
71
71
  :'heart-beat' => @heartbeats.join(','), :login => @login,
@@ -80,7 +80,8 @@ class OnStomp::Client
80
80
  # the broker will get processed barring any IO exceptions.
81
81
  # @param [{#to_sym => #to_s}] headers
82
82
  # @return [OnStomp::Components::Frame] transmitted DISCONNECT frame
83
- def disconnect_with_flush(headers={})
83
+ def disconnect_with_flush headers={}
84
+ processor_inst.prepare_to_close
84
85
  disconnect_without_flush(headers).tap do
85
86
  processor_inst.join
86
87
  end
@@ -111,11 +112,11 @@ class OnStomp::Client
111
112
  # This method should not be invoked directly. Use the frame methods provided
112
113
  # by the {OnStomp::Interfaces:FrameMethod} interface.
113
114
  # @return [OnStomp::Components::Frame]
114
- def transmit(frame, cbs={})
115
+ def transmit frame, cbs={}
115
116
  frame.tap do
116
117
  register_callbacks frame, cbs
117
118
  trigger_before_transmitting frame
118
- connection.write_frame_nonblock frame
119
+ connection && connection.write_frame_nonblock(frame)
119
120
  end
120
121
  end
121
122
 
@@ -8,6 +8,5 @@ require 'onstomp/components/uri'
8
8
  require 'onstomp/components/frame_headers'
9
9
  require 'onstomp/components/frame'
10
10
  require 'onstomp/components/subscription'
11
- require 'onstomp/components/nil_processor'
12
11
  require 'onstomp/components/threaded_processor'
13
12
  require 'onstomp/components/scopes'
@@ -14,7 +14,7 @@ class OnStomp::Components::FrameHeaders
14
14
  # @see #merge!
15
15
  def initialize(headers={})
16
16
  @values = {}
17
- __initialize_names__
17
+ initialize_names
18
18
  merge! headers
19
19
  end
20
20
 
@@ -115,7 +115,7 @@ class OnStomp::Components::FrameHeaders
115
115
  def delete(name)
116
116
  name = name.to_sym
117
117
  if @values.key? name
118
- __delete_name__ name
118
+ delete_name name
119
119
  @values.delete name
120
120
  end
121
121
  end
@@ -150,7 +150,7 @@ class OnStomp::Components::FrameHeaders
150
150
  def []=(name, val)
151
151
  name = name.to_sym
152
152
  val = val.to_s
153
- __add_name__ name
153
+ add_name name
154
154
  @values[name] = [val]
155
155
  val
156
156
  end
@@ -162,51 +162,48 @@ class OnStomp::Components::FrameHeaders
162
162
  @values.inject({}) { |h, (k,v)| h[k] = v.first; h }
163
163
  end
164
164
 
165
+ # Iterates over header name / value pairs, yielding them as a pair
166
+ # of strings to the supplied block.
167
+ # @yield [header_name, header_value]
168
+ # @yieldparam [String] header_name
169
+ # @yieldparam [String] header_value
170
+ def each &block
171
+ if block_given?
172
+ iterate_each &block
173
+ self
174
+ else
175
+ OnStomp::ENUMERATOR_KLASS.new(self)
176
+ end
177
+ end
178
+
165
179
  if RUBY_VERSION >= "1.9"
166
180
  def names; @values.keys; end
167
181
 
168
- def each(&block)
169
- if block_given?
170
- @values.each do |name, vals|
171
- name_str = name.to_s
172
- vals.each do |val|
173
- yield [name_str, val]
174
- end
182
+ private
183
+ def iterate_each
184
+ @values.each do |name, vals|
185
+ name_str = name.to_s
186
+ vals.each do |val|
187
+ yield [name_str, val]
175
188
  end
176
- self
177
- else
178
- Enumerator.new(self)
179
189
  end
180
190
  end
181
-
182
- private
183
- def __initialize_names__; end
184
- def __delete_name__(name); end
185
- def __add_name__(name); end
191
+ def initialize_names; end
192
+ def delete_name(name); end
193
+ def add_name(name); end
186
194
  else
187
195
  attr_reader :names
188
-
189
- # Iterates over header name / value pairs, yielding them as a pair
190
- # of strings to the supplied block.
191
- # @yield [header_name, header_value]
192
- # @yieldparam [String] header_name
193
- # @yieldparam [String] header_value
194
- def each(&block)
195
- if block_given?
196
- @names.each do |name|
197
- @values[name].each do |val|
198
- yield [name.to_s, val]
199
- end
196
+
197
+ private
198
+ def iterate_each
199
+ @names.each do |name|
200
+ @values[name].each do |val|
201
+ yield [name.to_s, val]
200
202
  end
201
- self
202
- else
203
- Enumerable::Enumerator.new(self)
204
203
  end
205
204
  end
206
-
207
- private
208
- def __initialize_names__; @names = []; end
209
- def __delete_name__(name); @names.delete name; end
210
- def __add_name__(name); @names << name unless @values.key?(name); end
205
+ def initialize_names; @names = []; end
206
+ def delete_name(name); @names.delete name; end
207
+ def add_name(name); @names << name unless @values.key?(name); end
211
208
  end
212
209
  end
@@ -7,6 +7,7 @@ class OnStomp::Components::ThreadedProcessor
7
7
  def initialize client
8
8
  @client = client
9
9
  @run_thread = nil
10
+ @closing = false
10
11
  end
11
12
 
12
13
  # Returns true if its IO thread has been created and is alive, otherwise
@@ -25,6 +26,7 @@ class OnStomp::Components::ThreadedProcessor
25
26
  begin
26
27
  while @client.connected?
27
28
  @client.connection.io_process
29
+ Thread.stop if @closing
28
30
  end
29
31
  rescue OnStomp::StopReceiver
30
32
  rescue Exception
@@ -34,6 +36,17 @@ class OnStomp::Components::ThreadedProcessor
34
36
  self
35
37
  end
36
38
 
39
+ # Prepares the conneciton for closing by flushing its write buffer.
40
+ def prepare_to_close
41
+ if running?
42
+ @closing = true
43
+ Thread.pass until @run_thread.stop?
44
+ @client.connection.flush_write_buffer
45
+ @closing = false
46
+ @run_thread.wakeup
47
+ end
48
+ end
49
+
37
50
  # Causes the thread this method was invoked in to +pass+ until the
38
51
  # processor is no longer {#running? running}.
39
52
  # @return [self]
@@ -93,13 +93,19 @@ class OnStomp::Connections::Base
93
93
  end
94
94
  end
95
95
 
96
+ # Flushes the write buffer by invoking {#io_process_write} until the
97
+ # buffer is empty.
98
+ def flush_write_buffer
99
+ io_process_write until @write_buffer.empty?
100
+ end
101
+
96
102
  # Makes a single call to {#io_process_write} and a single call to
97
103
  # {#io_process_read}
98
104
  def io_process &cb
99
105
  io_process_write &cb
100
106
  io_process_read &cb
101
107
  if @connection_up && !connected?
102
- triggered_close :died
108
+ triggered_close 'connection timed out', :died
103
109
  end
104
110
  end
105
111
 
@@ -162,13 +168,13 @@ class OnStomp::Connections::Base
162
168
  unshift_write_buffer data, frame
163
169
  break
164
170
  rescue Exception
165
- triggered_close :terminated
171
+ triggered_close $!.message, :terminated
166
172
  raise
167
173
  end
168
174
  end
169
175
  end
170
176
  if @write_buffer.empty? && @closing
171
- triggered_close
177
+ triggered_close 'client disconnected'
172
178
  end
173
179
  end
174
180
 
@@ -189,20 +195,21 @@ class OnStomp::Connections::Base
189
195
  rescue Errno::EINTR, Errno::EAGAIN, Errno::EWOULDBLOCK
190
196
  # do not
191
197
  rescue EOFError
192
- triggered_close
198
+ triggered_close $!.message
193
199
  rescue Exception
194
- triggered_close :terminated
200
+ triggered_close $!.message, :terminated
195
201
  raise
196
202
  end
197
203
  end
198
204
  end
199
205
 
200
206
  private
201
- def triggered_close *evs
207
+ def triggered_close msg, *evs
202
208
  @connection_up = false
203
209
  @closing = false
204
210
  socket.close
205
- evs.each { |ev| trigger_connection_event ev }
206
- trigger_connection_event :closed
211
+ evs.each { |ev| trigger_connection_event ev, msg }
212
+ trigger_connection_event :closed, msg
213
+ @write_buffer.clear
207
214
  end
208
215
  end
@@ -38,12 +38,6 @@ module OnStomp::Connections::Stomp_1
38
38
  create_frame 'DISCONNECT', [h]
39
39
  end
40
40
 
41
- # Creates a SUBSCRIBE frame
42
- # @return [OnStomp::Components::Frame] SUBSCRIBE frame
43
- def subscribe_frame d, h
44
- create_frame 'SUBSCRIBE', [{:id => OnStomp.next_serial}, h, {:destination => d}]
45
- end
46
-
47
41
  # Creates an UNSUBSCRIBE frame
48
42
  # @return [OnStomp::Components::Frame] UNSUBSCRIBE frame
49
43
  def unsubscribe_frame f, h
@@ -14,6 +14,14 @@ class OnStomp::Connections::Stomp_1_0 < OnStomp::Connections::Base
14
14
  super
15
15
  @serializer = OnStomp::Connections::Serializers::Stomp_1_0.new
16
16
  end
17
+
18
+ # Creates a SUBSCRIBE frame. Sets +ack+ header to 'auto' unless it is
19
+ # already set to 'client'.
20
+ # @return [OnStomp::Components::Frame] SUBSCRIBE frame
21
+ def subscribe_frame d, h
22
+ h[:ack] = 'auto' unless h[:ack] == 'client'
23
+ create_frame 'SUBSCRIBE', [{:id => OnStomp.next_serial}, h, {:destination => d}]
24
+ end
17
25
 
18
26
  # Creates an ACK frame
19
27
  # @return [OnStomp::Components::Frame] ACK frame
@@ -31,6 +31,14 @@ class OnStomp::Connections::Stomp_1_1 < OnStomp::Connections::Base
31
31
  super && pulse?
32
32
  end
33
33
 
34
+ # Creates a SUBSCRIBE frame. Sets +ack+ header to 'auto' unless it is
35
+ # already set to 'client' or 'client-individual'.
36
+ # @return [OnStomp::Components::Frame] SUBSCRIBE frame
37
+ def subscribe_frame d, h
38
+ h[:ack] = 'auto' unless ['client', 'client-individual'].include?(h[:ack])
39
+ create_frame 'SUBSCRIBE', [{:id => OnStomp.next_serial}, h, {:destination => d}]
40
+ end
41
+
34
42
  # Creates an ACK frame
35
43
  # @return [OnStomp::Components::Frame] ACK frame
36
44
  def ack_frame *args
@@ -0,0 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # Namespace for failover extensions.
4
+ module OnStomp::Failover
5
+ # Raised if the supplied failover: URI is not properly formatted as
6
+ # +failover:(uri,uri,...)?optionalParams=values+
7
+ class InvalidFailoverURIError < OnStomp::OnStompError; end
8
+ end
9
+
10
+ require 'onstomp/failover/uri'
11
+ require 'onstomp/failover/failover_configurable'
12
+ require 'onstomp/failover/failover_events'
13
+ require 'onstomp/failover/buffers'
14
+ require 'onstomp/failover/pools'
15
+ require 'onstomp/failover/client'
16
+ require 'onstomp/failover/new_with_failover'
@@ -0,0 +1,8 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # Namespace for various frame buffering strategies to keep failover working
4
+ # like it should.
5
+ module OnStomp::Failover::Buffers
6
+ end
7
+
8
+ require 'onstomp/failover/buffers/written'
@@ -0,0 +1,91 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # A buffer that ensures frames are at least written to a
4
+ # {OnStomp::Client client}'s {OnStomp::Connections::Base connection} and
5
+ # replays the ones that were not when the
6
+ # {OnStomp::Failover::Client failover} client reconnects.
7
+ class OnStomp::Failover::Buffers::Written
8
+ def initialize failover
9
+ @failover = failover
10
+ @buffer_mutex = Mutex.new
11
+ @buffer = []
12
+ @txs = {}
13
+
14
+ failover.before_send &method(:buffer_frame)
15
+ failover.before_commit &method(:buffer_frame)
16
+ failover.before_abort &method(:buffer_frame)
17
+ failover.before_subscribe &method(:buffer_frame)
18
+ failover.before_begin &method(:buffer_transaction)
19
+ # We can scrub the subscription before UNSUBSCRIBE is fully written
20
+ # because if we replay before UNSUBSCRIBE was sent, we still don't
21
+ # want to be subscribed when we reconnect.
22
+ failover.before_unsubscribe &method(:debuffer_subscription)
23
+ # We only want to scrub the transactions if ABORT or COMMIT was
24
+ # at least written fully to the socket.
25
+ failover.on_commit &method(:debuffer_transaction)
26
+ failover.on_abort &method(:debuffer_transaction)
27
+ failover.on_send &method(:debuffer_non_transactional_frame)
28
+
29
+ failover.on_failover_connected &method(:replay)
30
+ end
31
+
32
+ # Adds a frame to a buffer so that it may be replayed if the
33
+ # {OnStomp::Failover::Client failover} client re-connects
34
+ def buffer_frame f, *_
35
+ @buffer_mutex.synchronize do
36
+ unless f.header? :'x-onstomp-failover-replay'
37
+ @buffer << f
38
+ end
39
+ end
40
+ end
41
+
42
+ # Records the start of a transaction so that it may be replayed if the
43
+ # {OnStomp::Failover::Client failover} client re-connects
44
+ def buffer_transaction f, *_
45
+ @txs[f[:transaction]] = true
46
+ buffer_frame f
47
+ end
48
+
49
+ # Removes the recorded transaction from the buffer after it has been
50
+ # written the broker socket so that it will not be replayed when the
51
+ # {OnStomp::Failover::Client failover} client re-connects
52
+ def debuffer_transaction f, *_
53
+ tx = f[:transaction]
54
+ if @txs.delete tx
55
+ @buffer_mutex.synchronize do
56
+ @buffer.reject! { |bf| bf[:transaction] == tx }
57
+ end
58
+ end
59
+ end
60
+
61
+ # Removes the matching SUBSCRIBE frame from the buffer after the
62
+ # UNSUBSCRIBE has been added to the connection's write buffer
63
+ # so that it will not be replayed when the
64
+ # {OnStomp::Failover::Client failover} client re-connects
65
+ def debuffer_subscription f, *_
66
+ @buffer_mutex.synchronize do
67
+ @buffer.reject! { |bf| bf.command == 'SUBSCRIBE' && bf[:id] == f[:id] }
68
+ end
69
+ end
70
+
71
+ # Removes a frame that is not part of a transaction from the buffer
72
+ # after it has been written the broker socket so that it will not be
73
+ # replayed when the {OnStomp::Failover::Client failover} client re-connects
74
+ def debuffer_non_transactional_frame f, *_
75
+ unless @txs.key?(f[:transaction])
76
+ @buffer_mutex.synchronize { @buffer.delete f }
77
+ end
78
+ end
79
+
80
+ # Called when the {OnStomp::Failover::Client failover} client triggers
81
+ # +on_failover_connected+ to start replaying any frames in the buffer.
82
+ def replay fail, client, *_
83
+ replay_frames = @buffer_mutex.synchronize do
84
+ @buffer.select { |f| f[:'x-onstomp-failover-replay'] = '1'; true }
85
+ end
86
+
87
+ replay_frames.each do |f|
88
+ client.transmit f
89
+ end
90
+ end
91
+ end