promiscuous 0.90.0 → 0.91.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 (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