onstomp 1.0.0pre1 → 1.0.0

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.
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