gorgon 0.5.0.rc1 → 0.6.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/Gemfile.lock +2 -4
  2. data/gorgon.gemspec +0 -1
  3. data/lib/gorgon/amqp_service.rb +5 -5
  4. data/lib/gorgon/gem_command_handler.rb +2 -1
  5. data/lib/gorgon/listener.rb +7 -5
  6. data/lib/gorgon/originator_protocol.rb +1 -0
  7. data/lib/gorgon/version.rb +1 -1
  8. data/lib/gorgon/worker_manager.rb +5 -2
  9. data/lib/gorgon_amq-protocol/.gitignore +15 -0
  10. data/lib/gorgon_amq-protocol/.gitmodules +3 -0
  11. data/lib/gorgon_amq-protocol/.rspec +3 -0
  12. data/lib/gorgon_amq-protocol/.travis.yml +19 -0
  13. data/lib/gorgon_amq-protocol/lib/gorgon_amq/bit_set.rb +82 -0
  14. data/lib/gorgon_amq-protocol/lib/gorgon_amq/endianness.rb +15 -0
  15. data/lib/gorgon_amq-protocol/lib/gorgon_amq/int_allocator.rb +96 -0
  16. data/lib/gorgon_amq-protocol/lib/gorgon_amq/pack.rb +53 -0
  17. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol.rb +4 -0
  18. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/client.rb +2322 -0
  19. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/constants.rb +22 -0
  20. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/exceptions.rb +60 -0
  21. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/float_32bit.rb +14 -0
  22. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/frame.rb +210 -0
  23. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/table.rb +142 -0
  24. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/table_value_decoder.rb +190 -0
  25. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/table_value_encoder.rb +123 -0
  26. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/type_constants.rb +26 -0
  27. data/lib/gorgon_amq-protocol/lib/gorgon_amq/protocol/version.rb +5 -0
  28. data/lib/gorgon_amq-protocol/lib/gorgon_amq/settings.rb +114 -0
  29. data/lib/gorgon_amq-protocol/lib/gorgon_amq/uri.rb +37 -0
  30. data/lib/gorgon_bunny/lib/gorgon_amq/protocol/extensions.rb +16 -0
  31. data/lib/gorgon_bunny/lib/gorgon_bunny.rb +89 -0
  32. data/lib/gorgon_bunny/lib/gorgon_bunny/authentication/credentials_encoder.rb +55 -0
  33. data/lib/gorgon_bunny/lib/gorgon_bunny/authentication/external_mechanism_encoder.rb +27 -0
  34. data/lib/gorgon_bunny/lib/gorgon_bunny/authentication/plain_mechanism_encoder.rb +19 -0
  35. data/lib/gorgon_bunny/lib/gorgon_bunny/channel.rb +1875 -0
  36. data/lib/gorgon_bunny/lib/gorgon_bunny/channel_id_allocator.rb +80 -0
  37. data/lib/gorgon_bunny/lib/gorgon_bunny/compatibility.rb +24 -0
  38. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/atomic_fixnum.rb +74 -0
  39. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/condition.rb +66 -0
  40. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/continuation_queue.rb +41 -0
  41. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/linked_continuation_queue.rb +61 -0
  42. data/lib/gorgon_bunny/lib/gorgon_bunny/concurrent/synchronized_sorted_set.rb +56 -0
  43. data/lib/gorgon_bunny/lib/gorgon_bunny/consumer.rb +123 -0
  44. data/lib/gorgon_bunny/lib/gorgon_bunny/consumer_tag_generator.rb +23 -0
  45. data/lib/gorgon_bunny/lib/gorgon_bunny/consumer_work_pool.rb +94 -0
  46. data/lib/gorgon_bunny/lib/gorgon_bunny/delivery_info.rb +93 -0
  47. data/lib/gorgon_bunny/lib/gorgon_bunny/exceptions.rb +236 -0
  48. data/lib/gorgon_bunny/lib/gorgon_bunny/exchange.rb +271 -0
  49. data/lib/gorgon_bunny/lib/gorgon_bunny/framing.rb +56 -0
  50. data/lib/gorgon_bunny/lib/gorgon_bunny/heartbeat_sender.rb +70 -0
  51. data/lib/gorgon_bunny/lib/gorgon_bunny/message_properties.rb +119 -0
  52. data/lib/gorgon_bunny/lib/gorgon_bunny/queue.rb +387 -0
  53. data/lib/gorgon_bunny/lib/gorgon_bunny/reader_loop.rb +116 -0
  54. data/lib/gorgon_bunny/lib/gorgon_bunny/return_info.rb +74 -0
  55. data/lib/gorgon_bunny/lib/gorgon_bunny/session.rb +1044 -0
  56. data/lib/gorgon_bunny/lib/gorgon_bunny/socket.rb +83 -0
  57. data/lib/gorgon_bunny/lib/gorgon_bunny/ssl_socket.rb +57 -0
  58. data/lib/gorgon_bunny/lib/gorgon_bunny/system_timer.rb +20 -0
  59. data/lib/gorgon_bunny/lib/gorgon_bunny/test_kit.rb +27 -0
  60. data/lib/gorgon_bunny/lib/gorgon_bunny/timeout.rb +18 -0
  61. data/lib/gorgon_bunny/lib/gorgon_bunny/transport.rb +398 -0
  62. data/lib/gorgon_bunny/lib/gorgon_bunny/version.rb +6 -0
  63. data/lib/gorgon_bunny/lib/gorgon_bunny/versioned_delivery_tag.rb +28 -0
  64. data/spec/crash_reporter_spec.rb +1 -1
  65. data/spec/gem_command_handler_spec.rb +2 -2
  66. data/spec/listener_spec.rb +5 -5
  67. data/spec/worker_manager_spec.rb +3 -3
  68. metadata +56 -17
@@ -0,0 +1,80 @@
1
+ require "thread"
2
+ require "monitor"
3
+ require "gorgon_amq/int_allocator"
4
+
5
+ module GorgonBunny
6
+ # Bitset-based channel id allocator. When channels are closed,
7
+ # ids are released back to the pool.
8
+ #
9
+ # Every connection has its own allocator.
10
+ #
11
+ # Allocating and releasing ids is synchronized and can be performed
12
+ # from multiple threads.
13
+ class ChannelIdAllocator
14
+
15
+ #
16
+ # API
17
+ #
18
+
19
+ # @param [Integer] max_channel Max allowed channel id
20
+ def initialize(max_channel = ((1 << 16) - 1))
21
+ @allocator = GorgonAMQ::IntAllocator.new(1, max_channel)
22
+ @mutex = Monitor.new
23
+ end
24
+
25
+
26
+ # Returns next available channel id. This method is thread safe.
27
+ #
28
+ # @return [Fixnum]
29
+ # @api public
30
+ # @see ChannelManager#release_channel_id
31
+ # @see ChannelManager#reset_channel_id_allocator
32
+ def next_channel_id
33
+ @mutex.synchronize do
34
+ @allocator.allocate
35
+ end
36
+ end
37
+
38
+ # Releases previously allocated channel id. This method is thread safe.
39
+ #
40
+ # @param [Fixnum] i Channel id to release
41
+ # @api public
42
+ # @see ChannelManager#next_channel_id
43
+ # @see ChannelManager#reset_channel_id_allocator
44
+ def release_channel_id(i)
45
+ @mutex.synchronize do
46
+ @allocator.release(i)
47
+ end
48
+ end
49
+
50
+
51
+ # Returns true if given channel id has been previously allocated and not yet released.
52
+ # This method is thread safe.
53
+ #
54
+ # @param [Fixnum] i Channel id to check
55
+ # @return [Boolean] true if given channel id has been previously allocated and not yet released
56
+ # @api public
57
+ # @see ChannelManager#next_channel_id
58
+ # @see ChannelManager#release_channel_id
59
+ def allocated_channel_id?(i)
60
+ @mutex.synchronize do
61
+ @allocator.allocated?(i)
62
+ end
63
+ end
64
+
65
+ # Resets channel allocator. This method is thread safe.
66
+ # @api public
67
+ # @see Channel.next_channel_id
68
+ # @see Channel.release_channel_id
69
+ def reset_channel_id_allocator
70
+ @mutex.synchronize do
71
+ @allocator.reset
72
+ end
73
+ end
74
+
75
+ # @private
76
+ def synchronize(&block)
77
+ @mutex.synchronize(&block)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,24 @@
1
+ module GorgonBunny
2
+ # Helper methods necessary to stay mostly backwards-compatible with legacy (0.7.x, 0.8.x) GorgonBunny
3
+ # releases that hide channels completely from the API.
4
+ #
5
+ # @private
6
+ module Compatibility
7
+
8
+ #
9
+ # API
10
+ #
11
+
12
+ # @private
13
+ def channel_from(channel_or_connection)
14
+ # GorgonBunny 0.8.x and earlier completely hide channels from the API. So, queues and exchanges are
15
+ # instantiated with a "GorgonBunny object", which is a session. This function coerces two types of input to a
16
+ # channel.
17
+ if channel_or_connection.is_a?(GorgonBunny::Session)
18
+ channel_or_connection.default_channel
19
+ else
20
+ channel_or_connection
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,74 @@
1
+ require "set"
2
+ require "thread"
3
+ require "monitor"
4
+
5
+ module GorgonBunny
6
+ module Concurrent
7
+ # Minimalistic implementation of a synchronized fixnum value,
8
+ # designed after (but not implementing the entire API of!)
9
+ #
10
+ # @note Designed to be intentionally minimalistic and only cover GorgonBunny's needs.
11
+ #
12
+ # @api public
13
+ class AtomicFixnum
14
+ def initialize(n = 0)
15
+ @n = n
16
+ @mutex = Monitor.new
17
+ end
18
+
19
+ def get
20
+ @mutex.synchronize do
21
+ @n
22
+ end
23
+ end
24
+
25
+ def set(n)
26
+ @mutex.synchronize do
27
+ @n = n
28
+ end
29
+ end
30
+
31
+ def increment
32
+ @mutex.synchronize do
33
+ @n = @n + 1
34
+ end
35
+ end
36
+ alias inc increment
37
+ alias increment_and_get increment
38
+
39
+ def get_and_add(i)
40
+ @mutex.synchronize do
41
+ v = @n
42
+ @n = @n + i
43
+
44
+ v
45
+ end
46
+ end
47
+
48
+ def get_and_increment
49
+ @mutex.synchronize do
50
+ v = @n
51
+ @n = @n + 1
52
+
53
+ v
54
+ end
55
+ end
56
+
57
+ def decrement
58
+ @mutex.synchronize do
59
+ @n = @n - 1
60
+ end
61
+ end
62
+ alias dec decrement
63
+ alias decrement_and_get decrement
64
+
65
+ def ==(m)
66
+ @mutex.synchronize { @n == m }
67
+ end
68
+
69
+ def ===(v)
70
+ @mutex.synchronize { @n === v }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,66 @@
1
+ require "thread"
2
+ require "monitor"
3
+
4
+ module GorgonBunny
5
+ # @private
6
+ module Concurrent
7
+ # Akin to java.util.concurrent.Condition and intrinsic object monitors (Object#wait, Object#notify, Object#notifyAll) in Java:
8
+ # threads can wait (block until notified) on a condition other threads notify them about.
9
+ # Unlike the j.u.c. version, this one has a single waiting set.
10
+ #
11
+ # Conditions can optionally be annotated with a description string for ease of debugging.
12
+ # @private
13
+ class Condition
14
+ attr_reader :waiting_threads, :description
15
+
16
+
17
+ def initialize(description = nil)
18
+ @mutex = Monitor.new
19
+ @waiting_threads = []
20
+ @description = description
21
+ end
22
+
23
+ def wait
24
+ @mutex.synchronize do
25
+ t = Thread.current
26
+ @waiting_threads.push(t)
27
+ end
28
+
29
+ Thread.stop
30
+ end
31
+
32
+ def notify
33
+ @mutex.synchronize do
34
+ t = @waiting_threads.shift
35
+ begin
36
+ t.run if t
37
+ rescue ThreadError
38
+ retry
39
+ end
40
+ end
41
+ end
42
+
43
+ def notify_all
44
+ @mutex.synchronize do
45
+ @waiting_threads.each do |t|
46
+ t.run
47
+ end
48
+
49
+ @waiting_threads.clear
50
+ end
51
+ end
52
+
53
+ def waiting_set_size
54
+ @mutex.synchronize { @waiting_threads.size }
55
+ end
56
+
57
+ def any_threads_waiting?
58
+ @mutex.synchronize { !@waiting_threads.empty? }
59
+ end
60
+
61
+ def none_threads_waiting?
62
+ @mutex.synchronize { @waiting_threads.empty? }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,41 @@
1
+ require "thread"
2
+
3
+ module GorgonBunny
4
+ module Concurrent
5
+ # Continuation queue implementation for MRI and Rubinius
6
+ #
7
+ # @private
8
+ class ContinuationQueue
9
+ def initialize(*args, &block)
10
+ @q = ::Queue.new(*args)
11
+ end
12
+
13
+ def push(*args)
14
+ @q.push(*args)
15
+ end
16
+ alias << push
17
+
18
+ def pop
19
+ @q.pop
20
+ end
21
+
22
+ def poll(timeout_in_ms = nil)
23
+ if timeout_in_ms
24
+ GorgonBunny::Timeout.timeout(timeout_in_ms / 1000, ::Timeout::Error) do
25
+ @q.pop
26
+ end
27
+ else
28
+ @q.pop
29
+ end
30
+ end
31
+
32
+ def clear
33
+ @q.clear
34
+ end
35
+
36
+ def method_missing(selector, *args, &block)
37
+ @q.__send__(selector, *args, &block)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ if !defined?(JRUBY_VERSION)
2
+ raise "GorgonBunny::Concurrent::LinkedContinuationQueue can only be used on JRuby!"
3
+ end
4
+
5
+ require "java"
6
+
7
+ java_import java.util.concurrent.LinkedBlockingQueue
8
+ java_import java.util.concurrent.TimeUnit
9
+
10
+ module GorgonBunny
11
+ module Concurrent
12
+ # Continuation queue implementation for JRuby.
13
+ #
14
+ # On JRuby, we'd rather use reliable and heavily battle tested j.u.c.
15
+ # primitives with well described semantics than informally specified, clumsy
16
+ # and limited Ruby standard library parts.
17
+ #
18
+ # This is an implementation of the continuation queue on top of the linked blocking
19
+ # queue in j.u.c.
20
+ #
21
+ # Compared to the Ruby standard library Queue, there is one limitation: you cannot
22
+ # push a nil on the queue, it will fail with a null pointer exception.
23
+ # @private
24
+ class LinkedContinuationQueue
25
+ def initialize(*args, &block)
26
+ @q = LinkedBlockingQueue.new
27
+ end
28
+
29
+ def push(el, timeout_in_ms = nil)
30
+ if timeout_in_ms
31
+ @q.offer(el, timeout_in_ms, TimeUnit::MILLISECONDS)
32
+ else
33
+ @q.offer(el)
34
+ end
35
+ end
36
+ alias << push
37
+
38
+ def pop
39
+ @q.take
40
+ end
41
+
42
+ def poll(timeout_in_ms = nil)
43
+ if timeout_in_ms
44
+ v = @q.poll(timeout_in_ms, TimeUnit::MILLISECONDS)
45
+ raise ::Timeout::Error.new("operation did not finish in #{timeout_in_ms} ms") if v.nil?
46
+ v
47
+ else
48
+ @q.poll
49
+ end
50
+ end
51
+
52
+ def clear
53
+ @q.clear
54
+ end
55
+
56
+ def method_missing(selector, *args, &block)
57
+ @q.__send__(selector, *args, &block)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,56 @@
1
+ require "set"
2
+ require "thread"
3
+
4
+ module GorgonBunny
5
+ module Concurrent
6
+ # A SortedSet variation that synchronizes key mutation operations.
7
+ #
8
+ # @note This is NOT a complete SortedSet replacement. It only synchronizes operations needed by GorgonBunny.
9
+ # @api public
10
+ class SynchronizedSortedSet < SortedSet
11
+ def initialize(enum = nil)
12
+ @mutex = Mutex.new
13
+
14
+ super
15
+ end
16
+
17
+ def add(o)
18
+ # avoid using Mutex#synchronize because of a Ruby 1.8.7-specific
19
+ # bug that prevents super from being called from within a block. MK.
20
+ @mutex.lock
21
+ begin
22
+ super
23
+ ensure
24
+ @mutex.unlock
25
+ end
26
+ end
27
+
28
+ def delete(o)
29
+ @mutex.lock
30
+ begin
31
+ super
32
+ ensure
33
+ @mutex.unlock
34
+ end
35
+ end
36
+
37
+ def delete_if(&block)
38
+ @mutex.lock
39
+ begin
40
+ super
41
+ ensure
42
+ @mutex.unlock
43
+ end
44
+ end
45
+
46
+ def include?(o)
47
+ @mutex.lock
48
+ begin
49
+ super
50
+ ensure
51
+ @mutex.unlock
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,123 @@
1
+ module GorgonBunny
2
+ # Base class that represents consumer interface. Subclasses of this class implement
3
+ # specific logic of handling consumer life cycle events. Note that when the only event
4
+ # you are interested in is message deliveries, it is recommended to just use
5
+ # {GorgonBunny::Queue#subscribe} instead of subclassing this class.
6
+ #
7
+ # @see GorgonBunny::Queue#subscribe
8
+ # @see GorgonBunny::Queue#subscribe_with
9
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
10
+ # @api public
11
+ class Consumer
12
+
13
+ #
14
+ # API
15
+ #
16
+
17
+ attr_reader :channel
18
+ attr_reader :queue
19
+ attr_accessor :consumer_tag
20
+ attr_reader :arguments
21
+ attr_reader :no_ack
22
+ attr_reader :exclusive
23
+
24
+
25
+ # @param [GorgonBunny::Channel] channel Channel this consumer will use
26
+ # @param [GorgonBunny::Queue,String] queue Queue messages will be consumed from
27
+ # @param [String] consumer_tag Consumer tag (unique identifier). Generally it is better to let GorgonBunny generate one.
28
+ # Empty string means RabbitMQ will generate consumer tag.
29
+ # @param [Boolean] no_ack (true) If true, delivered messages will be automatically acknowledged.
30
+ # If false, manual acknowledgements will be necessary.
31
+ # @param [Boolean] exclusive (false) Should this consumer be exclusive?
32
+ # @param [Hash] arguments (nil) Optional arguments that may be used by RabbitMQ extensions, etc
33
+ # @api public
34
+ def initialize(channel, queue, consumer_tag = channel.generate_consumer_tag, no_ack = true, exclusive = false, arguments = {})
35
+ @channel = channel || raise(ArgumentError, "channel is nil")
36
+ @queue = queue || raise(ArgumentError, "queue is nil")
37
+ @consumer_tag = consumer_tag
38
+ @exclusive = exclusive
39
+ @arguments = arguments
40
+ # no_ack set to true = no manual ack = automatic ack. MK.
41
+ @no_ack = no_ack
42
+ end
43
+
44
+ # Defines message delivery handler
45
+ # @api public
46
+ def on_delivery(&block)
47
+ @on_delivery = block
48
+ self
49
+ end
50
+
51
+ # Invokes message delivery handler
52
+ # @private
53
+ def call(*args)
54
+ @on_delivery.call(*args) if @on_delivery
55
+ end
56
+ alias handle_delivery call
57
+
58
+ # Defines consumer cancellation notification handler
59
+ #
60
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
61
+ # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
62
+ # @api public
63
+ def on_cancellation(&block)
64
+ @on_cancellation = block
65
+ self
66
+ end
67
+
68
+ # Invokes consumer cancellation notification handler
69
+ # @private
70
+ def handle_cancellation(basic_cancel)
71
+ @on_cancellation.call(basic_cancel) if @on_cancellation
72
+ end
73
+
74
+ # Cancels this consumer. Messages for this consumer will no longer be delivered. If the queue
75
+ # it was on is auto-deleted and this consumer was the last one, the queue will be deleted.
76
+ #
77
+ # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
78
+ # @api public
79
+ def cancel
80
+ @channel.basic_cancel(@consumer_tag)
81
+ end
82
+
83
+ # @return [String] More detailed human-readable string representation of this consumer
84
+ def inspect
85
+ "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag} @exclusive=#{@exclusive} @no_ack=#{@no_ack}>"
86
+ end
87
+
88
+ # @return [String] Brief human-readable string representation of this consumer
89
+ def to_s
90
+ "#<#{self.class.name}:#{object_id} @channel_id=#{@channel.number} @queue=#{self.queue_name}> @consumer_tag=#{@consumer_tag}>"
91
+ end
92
+
93
+ # @return [Boolean] true if this consumer uses automatic acknowledgement mode
94
+ # @api public
95
+ def automatic_acknowledgement?
96
+ @no_ack == true
97
+ end
98
+
99
+ # @return [Boolean] true if this consumer uses manual (explicit) acknowledgement mode
100
+ # @api public
101
+ def manual_acknowledgement?
102
+ @no_ack == false
103
+ end
104
+
105
+ #
106
+ # Recovery
107
+ #
108
+
109
+ # @private
110
+ def recover_from_network_failure
111
+ @channel.basic_consume_with(self)
112
+ end
113
+
114
+ # @private
115
+ def queue_name
116
+ if @queue.respond_to?(:name)
117
+ @queue.name
118
+ else
119
+ @queue
120
+ end
121
+ end
122
+ end
123
+ end