promiscuous 0.90.0 → 0.91.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/lib/promiscuous/amqp/bunny.rb +63 -36
  3. data/lib/promiscuous/amqp/fake.rb +3 -1
  4. data/lib/promiscuous/amqp/hot_bunnies.rb +26 -16
  5. data/lib/promiscuous/amqp/null.rb +1 -0
  6. data/lib/promiscuous/amqp.rb +12 -12
  7. data/lib/promiscuous/cli.rb +70 -29
  8. data/lib/promiscuous/config.rb +54 -29
  9. data/lib/promiscuous/convenience.rb +1 -1
  10. data/lib/promiscuous/dependency.rb +25 -6
  11. data/lib/promiscuous/error/connection.rb +11 -9
  12. data/lib/promiscuous/error/dependency.rb +8 -1
  13. data/lib/promiscuous/loader.rb +4 -2
  14. data/lib/promiscuous/publisher/bootstrap/connection.rb +25 -0
  15. data/lib/promiscuous/publisher/bootstrap/data.rb +127 -0
  16. data/lib/promiscuous/publisher/bootstrap/mode.rb +19 -0
  17. data/lib/promiscuous/publisher/bootstrap/status.rb +40 -0
  18. data/lib/promiscuous/publisher/bootstrap/version.rb +46 -0
  19. data/lib/promiscuous/publisher/bootstrap.rb +27 -0
  20. data/lib/promiscuous/publisher/context/base.rb +67 -0
  21. data/lib/promiscuous/{middleware.rb → publisher/context/middleware.rb} +16 -13
  22. data/lib/promiscuous/publisher/context/transaction.rb +36 -0
  23. data/lib/promiscuous/publisher/context.rb +4 -88
  24. data/lib/promiscuous/publisher/mock_generator.rb +9 -9
  25. data/lib/promiscuous/publisher/model/active_record.rb +7 -7
  26. data/lib/promiscuous/publisher/model/base.rb +29 -29
  27. data/lib/promiscuous/publisher/model/ephemeral.rb +5 -3
  28. data/lib/promiscuous/publisher/model/mock.rb +9 -5
  29. data/lib/promiscuous/publisher/model/mongoid.rb +5 -22
  30. data/lib/promiscuous/publisher/operation/active_record.rb +360 -0
  31. data/lib/promiscuous/publisher/operation/atomic.rb +167 -0
  32. data/lib/promiscuous/publisher/operation/base.rb +279 -474
  33. data/lib/promiscuous/publisher/operation/mongoid.rb +153 -145
  34. data/lib/promiscuous/publisher/operation/non_persistent.rb +28 -0
  35. data/lib/promiscuous/publisher/operation/proxy_for_query.rb +42 -0
  36. data/lib/promiscuous/publisher/operation/transaction.rb +85 -0
  37. data/lib/promiscuous/publisher/operation.rb +1 -1
  38. data/lib/promiscuous/publisher/worker.rb +7 -7
  39. data/lib/promiscuous/publisher.rb +1 -1
  40. data/lib/promiscuous/railtie.rb +20 -5
  41. data/lib/promiscuous/redis.rb +104 -56
  42. data/lib/promiscuous/subscriber/message_processor/base.rb +38 -0
  43. data/lib/promiscuous/subscriber/message_processor/bootstrap.rb +17 -0
  44. data/lib/promiscuous/subscriber/message_processor/regular.rb +192 -0
  45. data/lib/promiscuous/subscriber/message_processor.rb +4 -0
  46. data/lib/promiscuous/subscriber/model/base.rb +20 -15
  47. data/lib/promiscuous/subscriber/model/mongoid.rb +4 -4
  48. data/lib/promiscuous/subscriber/model/observer.rb +16 -2
  49. data/lib/promiscuous/subscriber/operation/base.rb +68 -0
  50. data/lib/promiscuous/subscriber/operation/bootstrap.rb +54 -0
  51. data/lib/promiscuous/subscriber/operation/regular.rb +13 -0
  52. data/lib/promiscuous/subscriber/operation.rb +3 -166
  53. data/lib/promiscuous/subscriber/worker/message.rb +61 -35
  54. data/lib/promiscuous/subscriber/worker/message_synchronizer.rb +90 -59
  55. data/lib/promiscuous/subscriber/worker/pump.rb +17 -5
  56. data/lib/promiscuous/subscriber/worker/recorder.rb +4 -1
  57. data/lib/promiscuous/subscriber/worker/runner.rb +49 -9
  58. data/lib/promiscuous/subscriber/worker/stats.rb +2 -2
  59. data/lib/promiscuous/subscriber/worker.rb +6 -0
  60. data/lib/promiscuous/subscriber.rb +1 -1
  61. data/lib/promiscuous/timer.rb +31 -18
  62. data/lib/promiscuous/version.rb +1 -1
  63. data/lib/promiscuous.rb +23 -3
  64. metadata +104 -89
  65. data/lib/promiscuous/subscriber/payload.rb +0 -34
@@ -12,7 +12,7 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
12
12
  @root = root
13
13
  @node_synchronizers = {}
14
14
  @lock = Mutex.new
15
- @reconnect_timer = Promiscuous::Timer.new
15
+ @reconnect_timer = Promiscuous::Timer.new("redis", RECONNECT_INTERVAL) { reconnect }
16
16
  end
17
17
 
18
18
  def connected?
@@ -29,7 +29,11 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
29
29
  redis.nodes.each { |node| @node_synchronizers[node] = NodeSynchronizer.new(self, node) }
30
30
  @redis = redis
31
31
  end
32
- @root.pump.recover
32
+ # Do not recover messages while bootstrapping as there are a very large
33
+ # number of messages that remain un-acked. If bootstrap messages are missed
34
+ # these will be caught in the final phase of bootstrapping (see
35
+ # Promiscuous::Subscriber::Operation).
36
+ @root.pump.recover unless Promiscuous::Config.bootstrap
33
37
  end
34
38
 
35
39
  def disconnect
@@ -51,14 +55,13 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
51
55
  Promiscuous.warn "[redis] Reconnected"
52
56
  end
53
57
 
54
- def rescue_connection
55
- e = Promiscuous::Redis.lost_connection_exception
58
+ def rescue_connection(node, exception)
59
+ # TODO stop the pump to unack all messages
60
+ @reconnect_timer.start
56
61
 
62
+ e = Promiscuous::Redis.lost_connection_exception(node, :inner => exception)
57
63
  Promiscuous.warn "[redis] #{e}. Reconnecting..."
58
- Promiscuous::Config.error_notifier.try(:call, e)
59
-
60
- # TODO stop the pump to unack all messages
61
- @reconnect_timer.run_every(RECONNECT_INTERVAL) { reconnect }
64
+ Promiscuous::Config.error_notifier.call(e)
62
65
  end
63
66
 
64
67
  # process_when_ready() is called by the AMQP pump. This is what happens:
@@ -67,32 +70,33 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
67
70
  # If not we bail out and rely on the subscription to kick the processing.
68
71
  # Because we subscribed in advanced, we will not miss the notification.
69
72
  def process_when_ready(msg)
73
+ unless msg.has_dependencies?
74
+ process_message!(msg)
75
+ return
76
+ end
77
+
70
78
  # Dropped messages will be redelivered as we (re)connect
71
79
  return unless self.redis
72
80
 
73
- @lock.synchronize do
74
- @num_queued_messages += 1
75
- end
81
+ @lock.synchronize { @num_queued_messages += 1 }
76
82
 
77
- if msg.has_dependencies?
78
- process_message_proc = proc { process_message!(msg) }
79
- msg.happens_before_dependencies.reduce(process_message_proc) do |chain, dep|
80
- get_redis = dep.redis_node
81
- subscriber_redis = dep.redis_node(@redis)
82
-
83
- key = dep.key(:sub).join('rw').to_s
84
- version = dep.version
85
- node_synchronizer = @node_synchronizers[subscriber_redis]
86
- proc { node_synchronizer.on_version(subscriber_redis, get_redis, key, version, msg) { chain.call } }
87
- end.call
88
- else
89
- process_message!(msg)
90
- end
83
+ process_message_proc = proc { process_message!(msg) }
84
+ msg.happens_before_dependencies.reduce(process_message_proc) do |chain, dep|
85
+ get_redis = dep.redis_node
86
+ subscriber_redis = dep.redis_node(@redis)
87
+
88
+ key = dep.key(:sub).join('rw').to_s
89
+ version = dep.version
90
+ node_synchronizer = @node_synchronizers[subscriber_redis]
91
+ proc { node_synchronizer.on_version(subscriber_redis, get_redis, key, version, msg) { chain.call } }
92
+ end.call
91
93
  end
92
94
 
93
95
  def process_message!(msg)
94
96
  @root.runner.messages_to_process << msg
95
97
 
98
+ return unless msg.has_dependencies?
99
+
96
100
  cleanup = false
97
101
  @lock.synchronize do
98
102
  @num_queued_messages -= 1
@@ -108,38 +112,57 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
108
112
  # We also know that we are not processing messages (@num_queued_messages is
109
113
  # decremented before we send the message to the runners), and we are called
110
114
  # after adding a pending callback.
111
- recover
115
+ recover_dependencies_for(blocked_messages.first)
112
116
  end
113
117
  end
114
118
 
115
- def recover
119
+ def recover_dependencies_for(msg)
116
120
  # XXX This recovery mechanism only works with one worker.
117
121
  # We are taking the earliest message to unblock, but in reality we should
118
122
  # do the DAG of the happens before dependencies, take root nodes
119
123
  # of the disconnected graphs, and sort by timestamps if needed.
120
- msg = blocked_messages.first
121
124
 
122
- versions_to_skip = msg.happens_before_dependencies.map do |dep|
123
- key = dep.key(:sub).join('rw').to_s
124
- to_skip = dep.version - dep.redis_node.get(key).to_i
125
- [dep, key, to_skip] if to_skip > 0
126
- end.compact
125
+ incremented_deps = {}
126
+
127
+ msg.happens_before_dependencies.each do |dep|
128
+ key = dep.key(:sub).join('rw')
129
+ guard_key = key.join('guard') if dep.write?
130
+ version = dep.version
127
131
 
128
- return not_recovering if versions_to_skip.blank?
132
+ @@version_recovery_script ||= Promiscuous::Redis::Script.new <<-SCRIPT
133
+ local key = ARGV[1]
134
+ local wanted_version = tonumber(ARGV[2])
135
+ local guard_key = ARGV[3]
129
136
 
130
- recovery_msg = "Skipping "
131
- recovery_msg += versions_to_skip.map do |dep, key, to_skip|
132
- dep.redis_node.set(key, dep.version)
133
- dep.redis_node.publish(key, dep.version)
137
+ if redis.call('exists', guard_key) == 1 then
138
+ return
139
+ end
140
+
141
+ local current_version = tonumber(redis.call('get', key)) or 0
142
+
143
+ if wanted_version > current_version then
144
+ redis.call('set', guard_key, 1)
145
+ redis.call('expire', guard_key, 10)
146
+
147
+ redis.call('set', key, wanted_version)
148
+ redis.call('publish', key, wanted_version)
149
+ return wanted_version - current_version
150
+ end
151
+ SCRIPT
152
+ increment = @@version_recovery_script.eval(dep.redis_node, :argv => [key, version, guard_key].compact)
153
+ incremented_deps[dep] = increment if increment
154
+ end
134
155
 
135
- # Note: the skipped message would have a write dependency with dep.to_s
136
- "#{to_skip} message(s) on #{dep}"
137
- end.join(", ")
156
+ if incremented_deps.present?
157
+ recovery_msg = "Incrementing "
158
+ recovery_msg += incremented_deps.map { |dep, increment| "#{dep} by #{increment}" }.join(", ")
138
159
 
139
- e = Promiscuous::Error::Recovery.new(recovery_msg)
140
- Promiscuous.error "[synchronization recovery] #{e}"
141
- # TODO Don't report when doing the initial sync
142
- Promiscuous::Config.error_notifier.try(:call, e)
160
+ e = Promiscuous::Error::Recovery.new(recovery_msg)
161
+ Promiscuous.error "[synchronization recovery] #{e}"
162
+
163
+ # TODO Should we report the error to the notifier, or the log file is enough?
164
+ # Promiscuous::Config.error_notifier.call(e)
165
+ end
143
166
  end
144
167
 
145
168
  def blocked_messages
@@ -150,10 +173,6 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
150
173
  .sort_by { |msg| msg.timestamp }
151
174
  end
152
175
 
153
- def not_recovering
154
- Promiscuous.warn "[synchronization recovery] Nothing to recover from"
155
- end
156
-
157
176
  class NodeSynchronizer
158
177
  attr_accessor :node, :subscriptions, :root_synchronizer
159
178
 
@@ -181,16 +200,16 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
181
200
  notify_key_change(subscription, arg)
182
201
  end
183
202
  end
184
- rescue EOFError, Errno::ECONNRESET
203
+ rescue EOFError, Errno::ECONNRESET => e
185
204
  # Unwanted disconnection
186
- @root_synchronizer.rescue_connection unless @stop
187
- rescue IOError => e
188
- raise e unless @stop
205
+ @root_synchronizer.rescue_connection(redis_client, e) unless @stop
189
206
  rescue Exception => e
190
- Promiscuous.warn "[redis] #{e.class} #{e.message}"
191
- Promiscuous.warn "[redis] #{e} #{e.backtrace.join("\n")}"
207
+ unless @stop
208
+ Promiscuous.warn "[redis] #{e.class} #{e.message}"
209
+ Promiscuous.warn "[redis] #{e}\n#{e.backtrace.join("\n")}"
192
210
 
193
- Promiscuous::Config.error_notifier.try(:call, e)
211
+ Promiscuous::Config.error_notifier.call(e)
212
+ end
194
213
  end
195
214
 
196
215
  def stop_main_loop
@@ -270,6 +289,17 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
270
289
  refresh_activity
271
290
  end
272
291
 
292
+ def with_rescue_connection(node, &block)
293
+ block.call
294
+ rescue Exception => e
295
+ # TODO only catch exceptions related to network issues
296
+ node_synchronizer.root_synchronizer.rescue_connection(node, e)
297
+ end
298
+
299
+ def redis_exec_raw(node, *commands)
300
+ with_rescue_connection(node) { node.client.process([commands]) }
301
+ end
302
+
273
303
  def total_num_processed_messages
274
304
  node_synchronizer.root_synchronizer.num_processed_messages
275
305
  end
@@ -285,7 +315,7 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
285
315
 
286
316
  def cleanup_if_old
287
317
  if is_old?
288
- subscriber_redis.client.process([[:unsubscribe, key]])
318
+ redis_exec_raw(subscriber_redis, :unsubscribe, key)
289
319
  node_synchronizer.subscriptions.delete(key) # lock is already held
290
320
  end
291
321
  end
@@ -296,11 +326,12 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
296
326
  @subscription_requested = true
297
327
  end
298
328
 
299
- subscriber_redis.client.process([[:subscribe, key]])
329
+ redis_exec_raw(subscriber_redis, :subscribe, key)
300
330
  end
301
331
 
302
332
  def finalize_subscription
303
- signal_version(get_redis.get(key))
333
+ v = with_rescue_connection(get_redis) { get_redis.get(key) }
334
+ signal_version(v)
304
335
  end
305
336
 
306
337
  def signal_version(current_version)
@@ -8,17 +8,29 @@ class Promiscuous::Subscriber::Worker::Pump
8
8
 
9
9
  def connect
10
10
  options = {}
11
- options[:queue_name] = ENV['QUEUE_NAME'] || "#{Promiscuous::Config.app}.promiscuous"
11
+ options[:bindings] = {}
12
12
  # We need to subscribe to everything to keep up with the version tracking
13
- options[:bindings] = ['*']
13
+ Promiscuous::Config.subscriber_exchanges.each do |exchange|
14
+ options[:bindings][exchange] = ['*']
15
+ end
16
+
17
+ if Promiscuous::Config.bootstrap
18
+ options[:bindings][Promiscuous::AMQP::BOOTSTRAP_EXCHANGE] = ['*']
19
+ end
20
+
14
21
  subscribe(options, &method(:on_message))
15
22
  end
16
23
 
17
24
  def on_message(metadata, payload)
18
25
  msg = Promiscuous::Subscriber::Worker::Message.new(payload, :metadata => metadata, :root_worker => @root)
19
- @root.message_synchronizer.process_when_ready(msg)
26
+ if Promiscuous::Config.bootstrap
27
+ # Bootstrapping doesn't require synchronzation
28
+ @root.runner.messages_to_process << msg
29
+ else
30
+ @root.message_synchronizer.process_when_ready(msg)
31
+ end
20
32
  rescue Exception => e
21
- Promiscuous.warn "[receive] cannot process message: #{e} #{e.backtrace.join("\n")}"
22
- Promiscuous::Config.error_notifier.try(:call, e)
33
+ Promiscuous.warn "[receive] cannot process message: #{e}\n#{e.backtrace.join("\n")}"
34
+ Promiscuous::Config.error_notifier.call(e)
23
35
  end
24
36
  end
@@ -8,8 +8,11 @@ class Promiscuous::Subscriber::Worker::Recorder
8
8
  @file = File.open(@log_file, 'a')
9
9
  options = {}
10
10
  options[:queue_name] = "#{Promiscuous::Config.app}.promiscuous"
11
+ options[:bindings] = {}
11
12
  # We need to subscribe to everything to keep up with the version tracking
12
- options[:bindings] = ['*']
13
+ Promiscuous::Config.subscriber_exchanges.each do |exchange|
14
+ options[:bindings][exchange] = ['*']
15
+ end
13
16
 
14
17
  subscribe(options) do |metadata, payload|
15
18
  @file.puts payload
@@ -8,21 +8,61 @@ class Promiscuous::Subscriber::Worker::Runner
8
8
 
9
9
  def start
10
10
  num_threads = Promiscuous::Config.subscriber_threads
11
- @locks ||= num_threads.times.map { Mutex.new }
12
- @threads ||= num_threads.times.map { |i| Thread.new { main_loop(@locks[i]) } }
11
+ @runner_threads ||= num_threads.times.map { RunnerThread.new(@messages_to_process) }
13
12
  end
14
13
 
15
14
  def stop
16
- return unless @threads
17
- @threads.zip(@locks).each { |thread, lock| lock.synchronize { thread.kill } }
18
- @threads = @locks = nil
15
+ return unless @runner_threads
16
+
17
+ @runner_threads.each { |runner_thread| runner_thread.stop }
18
+ @runner_threads = nil
19
+
19
20
  @messages_to_process.clear
20
21
  end
21
22
 
22
- def main_loop(lock)
23
- loop do
24
- msg = @messages_to_process.pop
25
- lock.synchronize { msg.process }
23
+ def show_stop_status(num_requests)
24
+ @runner_threads.each { |runner_thread| runner_thread.show_stop_status(num_requests) }
25
+ end
26
+
27
+ class RunnerThread
28
+ def initialize(message_queue)
29
+ @message_queue = message_queue
30
+ @kill_lock = Mutex.new
31
+ @thread = Thread.new { main_loop }
32
+ end
33
+
34
+ def main_loop
35
+ loop do
36
+ msg = @message_queue.pop
37
+ @kill_lock.synchronize do
38
+ @current_message = msg
39
+ msg.process # msg.process does not throw
40
+ @current_message = nil
41
+ end
42
+ end
43
+ end
44
+
45
+ def stop
46
+ @kill_lock.synchronize { @thread.kill }
47
+ end
48
+
49
+ def show_stop_status(num_requests)
50
+ msg = @current_message
51
+ backtrace = @thread.backtrace
52
+
53
+ if msg
54
+ STDERR.puts "Still processing #{msg.payload}"
55
+
56
+ if num_requests > 1 && backtrace
57
+ STDERR.puts
58
+ STDERR.puts backtrace.map { |line| " \e[1;30m#{line}\e[0m\n" }
59
+ STDERR.puts
60
+ STDERR.puts "I'm a little busy, check out my stack trace."
61
+ STDERR.puts "Be patient (or kill me with -9, but that wouldn't be very nice of you)."
62
+ else
63
+ STDERR.puts "Just a second..."
64
+ end
65
+ end
26
66
  end
27
67
  end
28
68
  end
@@ -15,8 +15,8 @@ class Promiscuous::Subscriber::Worker::Stats
15
15
 
16
16
  STDERR.puts ""
17
17
 
18
- @timer ||= Promiscuous::Timer.new
19
- @timer.run_every(@interval) { aggregate_stats }
18
+ @timer ||= Promiscuous::Timer.new("stats", @interval) { aggregate_stats }
19
+ @timer.start
20
20
  end
21
21
 
22
22
  def disconnect
@@ -24,4 +24,10 @@ class Promiscuous::Subscriber::Worker
24
24
  @pump.disconnect
25
25
  @message_synchronizer.disconnect
26
26
  end
27
+
28
+ def show_stop_status
29
+ @num_show_stop_requests ||= 0
30
+ @num_show_stop_requests += 1
31
+ @runner.show_stop_status(@num_show_stop_requests)
32
+ end
27
33
  end
@@ -1,6 +1,6 @@
1
1
  module Promiscuous::Subscriber
2
2
  extend Promiscuous::Autoload
3
- autoload :Worker, :Payload, :Model, :Operation
3
+ autoload :Worker, :MessageProcessor, :Model, :Operation
4
4
 
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -1,32 +1,44 @@
1
1
  class Promiscuous::Timer
2
- def initialize
2
+ def initialize(name, duration, &block)
3
+ @name = name
4
+ @duration = duration.to_f
5
+ @block = block
3
6
  @lock = Mutex.new
4
7
  end
5
8
 
6
- def run_every(duration, options={}, &block)
9
+ def run_callback
10
+ @block.call
11
+ rescue Exception => e
12
+ # Report the exception only once
13
+ unless @last_exception == e.to_s
14
+ @last_exception = e.to_s
15
+ Promiscuous.warn "[#{@name}] #{e}"
16
+ Promiscuous::Config.error_notifier.call(e)
17
+ end
18
+ end
19
+
20
+ def main_loop(options={})
21
+ Thread.current[:promiscuous_timer_instance] = self
7
22
  options = options.dup
8
- duration = duration.to_f unless duration.is_a?(Integer)
9
- reset
10
23
 
11
- @lock.synchronize do
12
- @thread ||= Thread.new do
13
- loop do
14
- sleep duration unless options.delete(:run_immediately)
15
- @lock.synchronize do
16
- if @thread == Thread.current
17
- begin
18
- block.call
19
- rescue Exception
20
- end
21
- end
22
- end
23
- end
24
+ loop do
25
+ sleep @duration unless options.delete(:run_immediately)
26
+ @lock.synchronize do
27
+ return unless @thread
28
+ run_callback
24
29
  end
25
30
  end
26
31
  end
27
32
 
33
+ def start(options={})
34
+ @lock.synchronize do
35
+ @thread ||= Thread.new { main_loop(options) }
36
+ end
37
+ end
38
+
28
39
  def reset
29
- if @thread == Thread.current
40
+ if Thread.current[:promiscuous_timer_instance] == self
41
+ # We already hold the lock (the callback called reset)
30
42
  @thread = nil
31
43
  else
32
44
  @lock.synchronize do
@@ -34,5 +46,6 @@ class Promiscuous::Timer
34
46
  @thread = nil
35
47
  end
36
48
  end
49
+ @last_exception = nil
37
50
  end
38
51
  end
@@ -1,3 +1,3 @@
1
1
  module Promiscuous
2
- VERSION = '0.90.0'
2
+ VERSION = '0.91.0'
3
3
  end
data/lib/promiscuous.rb CHANGED
@@ -18,7 +18,10 @@ module Promiscuous
18
18
  extend Promiscuous::Autoload
19
19
  autoload :Common, :Publisher, :Subscriber, :Observer, :Worker, :Ephemeral,
20
20
  :CLI, :Error, :Loader, :AMQP, :Redis, :ZK, :Config, :DSL, :Key,
21
- :Convenience, :Dependency, :Middleware, :Timer
21
+ :Convenience, :Dependency, :Timer
22
+
23
+ # Shortcut for the middleware, TODO make load on demand
24
+ Middleware = Publisher::Context::Middleware
22
25
 
23
26
  extend Promiscuous::DSL
24
27
 
@@ -38,11 +41,17 @@ module Promiscuous
38
41
  def connect
39
42
  AMQP.connect
40
43
  Redis.connect
44
+ @should_be_connected = true
41
45
  end
42
46
 
43
47
  def disconnect
44
48
  AMQP.disconnect
45
49
  Redis.disconnect
50
+ @should_be_connected = false
51
+ end
52
+
53
+ def should_be_connected?
54
+ !!@should_be_connected
46
55
  end
47
56
 
48
57
  def healthy?
@@ -54,16 +63,27 @@ module Promiscuous
54
63
  true
55
64
  end
56
65
 
66
+ def ensure_connected
67
+ unless should_be_connected?
68
+ connect
69
+ end
70
+ end
71
+
57
72
  def disabled
58
- Thread.current[:promiscuous_disabled] || $promiscuous_disabled
73
+ return $promiscuous_disabled if Thread.current[:promiscuous_disabled].nil?
74
+ Thread.current[:promiscuous_disabled]
59
75
  end
60
76
 
61
77
  def disabled=(value)
62
78
  Thread.current[:promiscuous_disabled] = value
63
79
  end
64
80
 
81
+ def disabled?
82
+ !!Thread.current[:promiscuous_disabled]
83
+ end
84
+
65
85
  def context(*args, &block)
66
- Publisher::Context.open(*args, &block)
86
+ Publisher::Context::Base.with_context(*args, &block)
67
87
  end
68
88
  end
69
89