promiscuous 0.53.1 → 0.90.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.
- data/lib/promiscuous.rb +25 -28
- data/lib/promiscuous/amqp.rb +27 -8
- data/lib/promiscuous/amqp/bunny.rb +131 -16
- data/lib/promiscuous/amqp/fake.rb +52 -0
- data/lib/promiscuous/amqp/hot_bunnies.rb +56 -0
- data/lib/promiscuous/amqp/null.rb +6 -6
- data/lib/promiscuous/cli.rb +108 -24
- data/lib/promiscuous/config.rb +73 -12
- data/lib/promiscuous/convenience.rb +18 -0
- data/lib/promiscuous/dependency.rb +59 -0
- data/lib/promiscuous/dsl.rb +36 -0
- data/lib/promiscuous/error.rb +3 -1
- data/lib/promiscuous/error/already_processed.rb +5 -0
- data/lib/promiscuous/error/base.rb +1 -0
- data/lib/promiscuous/error/connection.rb +7 -5
- data/lib/promiscuous/error/dependency.rb +111 -0
- data/lib/promiscuous/error/lock_unavailable.rb +12 -0
- data/lib/promiscuous/error/lost_lock.rb +12 -0
- data/lib/promiscuous/error/missing_context.rb +29 -0
- data/lib/promiscuous/error/publisher.rb +5 -15
- data/lib/promiscuous/error/recovery.rb +7 -0
- data/lib/promiscuous/error/subscriber.rb +2 -4
- data/lib/promiscuous/key.rb +36 -0
- data/lib/promiscuous/loader.rb +12 -16
- data/lib/promiscuous/middleware.rb +112 -0
- data/lib/promiscuous/publisher.rb +7 -4
- data/lib/promiscuous/publisher/context.rb +92 -0
- data/lib/promiscuous/publisher/mock_generator.rb +72 -0
- data/lib/promiscuous/publisher/model.rb +3 -86
- data/lib/promiscuous/publisher/model/active_record.rb +8 -15
- data/lib/promiscuous/publisher/model/base.rb +136 -0
- data/lib/promiscuous/publisher/model/ephemeral.rb +69 -0
- data/lib/promiscuous/publisher/model/mock.rb +61 -0
- data/lib/promiscuous/publisher/model/mongoid.rb +57 -100
- data/lib/promiscuous/{common/lint.rb → publisher/operation.rb} +1 -1
- data/lib/promiscuous/publisher/operation/base.rb +707 -0
- data/lib/promiscuous/publisher/operation/mongoid.rb +370 -0
- data/lib/promiscuous/publisher/worker.rb +22 -0
- data/lib/promiscuous/railtie.rb +21 -3
- data/lib/promiscuous/redis.rb +132 -40
- data/lib/promiscuous/resque.rb +12 -0
- data/lib/promiscuous/sidekiq.rb +15 -0
- data/lib/promiscuous/subscriber.rb +9 -20
- data/lib/promiscuous/subscriber/model.rb +4 -104
- data/lib/promiscuous/subscriber/model/active_record.rb +10 -0
- data/lib/promiscuous/subscriber/model/base.rb +96 -0
- data/lib/promiscuous/subscriber/model/mongoid.rb +86 -0
- data/lib/promiscuous/subscriber/model/observer.rb +37 -0
- data/lib/promiscuous/subscriber/operation.rb +167 -0
- data/lib/promiscuous/subscriber/payload.rb +34 -0
- data/lib/promiscuous/subscriber/worker.rb +22 -18
- data/lib/promiscuous/subscriber/worker/message.rb +48 -25
- data/lib/promiscuous/subscriber/worker/message_synchronizer.rb +273 -181
- data/lib/promiscuous/subscriber/worker/pump.rb +17 -43
- data/lib/promiscuous/subscriber/worker/recorder.rb +24 -0
- data/lib/promiscuous/subscriber/worker/runner.rb +24 -3
- data/lib/promiscuous/subscriber/worker/stats.rb +62 -0
- data/lib/promiscuous/timer.rb +38 -0
- data/lib/promiscuous/version.rb +1 -1
- metadata +98 -143
- data/README.md +0 -33
- data/lib/promiscuous/amqp/ruby_amqp.rb +0 -140
- data/lib/promiscuous/common.rb +0 -4
- data/lib/promiscuous/common/class_helpers.rb +0 -12
- data/lib/promiscuous/common/lint/base.rb +0 -24
- data/lib/promiscuous/common/options.rb +0 -51
- data/lib/promiscuous/ephemeral.rb +0 -14
- data/lib/promiscuous/error/recover.rb +0 -1
- data/lib/promiscuous/observer.rb +0 -5
- data/lib/promiscuous/publisher/active_record.rb +0 -7
- data/lib/promiscuous/publisher/amqp.rb +0 -18
- data/lib/promiscuous/publisher/attributes.rb +0 -32
- data/lib/promiscuous/publisher/base.rb +0 -23
- data/lib/promiscuous/publisher/class.rb +0 -36
- data/lib/promiscuous/publisher/envelope.rb +0 -7
- data/lib/promiscuous/publisher/ephemeral.rb +0 -9
- data/lib/promiscuous/publisher/lint.rb +0 -35
- data/lib/promiscuous/publisher/lint/amqp.rb +0 -14
- data/lib/promiscuous/publisher/lint/attributes.rb +0 -12
- data/lib/promiscuous/publisher/lint/base.rb +0 -5
- data/lib/promiscuous/publisher/lint/class.rb +0 -15
- data/lib/promiscuous/publisher/lint/polymorphic.rb +0 -22
- data/lib/promiscuous/publisher/mock.rb +0 -79
- data/lib/promiscuous/publisher/mongoid.rb +0 -33
- data/lib/promiscuous/publisher/mongoid/embedded.rb +0 -27
- data/lib/promiscuous/publisher/mongoid/embedded_many.rb +0 -12
- data/lib/promiscuous/publisher/polymorphic.rb +0 -8
- data/lib/promiscuous/subscriber/active_record.rb +0 -11
- data/lib/promiscuous/subscriber/amqp.rb +0 -25
- data/lib/promiscuous/subscriber/attributes.rb +0 -35
- data/lib/promiscuous/subscriber/base.rb +0 -29
- data/lib/promiscuous/subscriber/class.rb +0 -29
- data/lib/promiscuous/subscriber/dummy.rb +0 -19
- data/lib/promiscuous/subscriber/envelope.rb +0 -18
- data/lib/promiscuous/subscriber/lint.rb +0 -30
- data/lib/promiscuous/subscriber/lint/amqp.rb +0 -21
- data/lib/promiscuous/subscriber/lint/attributes.rb +0 -21
- data/lib/promiscuous/subscriber/lint/base.rb +0 -14
- data/lib/promiscuous/subscriber/lint/class.rb +0 -13
- data/lib/promiscuous/subscriber/lint/polymorphic.rb +0 -39
- data/lib/promiscuous/subscriber/mongoid.rb +0 -27
- data/lib/promiscuous/subscriber/mongoid/embedded.rb +0 -17
- data/lib/promiscuous/subscriber/mongoid/embedded_many.rb +0 -44
- data/lib/promiscuous/subscriber/observer.rb +0 -26
- data/lib/promiscuous/subscriber/polymorphic.rb +0 -36
- data/lib/promiscuous/subscriber/upsert.rb +0 -12
data/lib/promiscuous.rb
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
require 'active_support/core_ext'
|
|
2
|
+
require 'active_model/callbacks'
|
|
3
|
+
require 'multi_json'
|
|
2
4
|
|
|
3
5
|
module Promiscuous
|
|
6
|
+
def self.require_for(gem, file)
|
|
7
|
+
require gem
|
|
8
|
+
require file
|
|
9
|
+
rescue LoadError
|
|
10
|
+
end
|
|
11
|
+
|
|
4
12
|
require 'promiscuous/autoload'
|
|
5
|
-
|
|
13
|
+
require_for 'rails', 'promiscuous/railtie'
|
|
14
|
+
require_for 'resque', 'promiscuous/resque'
|
|
15
|
+
require_for 'sidekiq', 'promiscuous/sidekiq'
|
|
16
|
+
|
|
6
17
|
|
|
7
18
|
extend Promiscuous::Autoload
|
|
8
19
|
autoload :Common, :Publisher, :Subscriber, :Observer, :Worker, :Ephemeral,
|
|
9
|
-
:CLI, :Error, :Loader, :AMQP, :Redis, :Config
|
|
20
|
+
:CLI, :Error, :Loader, :AMQP, :Redis, :ZK, :Config, :DSL, :Key,
|
|
21
|
+
:Convenience, :Dependency, :Middleware, :Timer
|
|
22
|
+
|
|
23
|
+
extend Promiscuous::DSL
|
|
24
|
+
|
|
25
|
+
Object.__send__(:include, Promiscuous::Convenience)
|
|
10
26
|
|
|
11
27
|
class << self
|
|
12
28
|
def configure(&block)
|
|
@@ -19,13 +35,6 @@ module Promiscuous
|
|
|
19
35
|
end
|
|
20
36
|
end
|
|
21
37
|
|
|
22
|
-
def reload
|
|
23
|
-
desc = Promiscuous::Publisher::Base.descendants
|
|
24
|
-
desc += Promiscuous::Subscriber::Base.descendants
|
|
25
|
-
desc.reject! { |klass| klass.name =~ /^Promiscuous::/ }
|
|
26
|
-
desc.each { |klass| klass.setup_class_binding }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
38
|
def connect
|
|
30
39
|
AMQP.connect
|
|
31
40
|
Redis.connect
|
|
@@ -39,36 +48,24 @@ module Promiscuous
|
|
|
39
48
|
def healthy?
|
|
40
49
|
AMQP.ensure_connected
|
|
41
50
|
Redis.ensure_connected
|
|
42
|
-
rescue
|
|
51
|
+
rescue Exception
|
|
43
52
|
false
|
|
44
53
|
else
|
|
45
54
|
true
|
|
46
55
|
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
module ConsoleHelpers
|
|
50
|
-
# These are just for the subscriber, some helpers to debug in production
|
|
51
|
-
def global_key
|
|
52
|
-
Promiscuous::Redis.sub_key('global')
|
|
53
|
-
end
|
|
54
56
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
+
def disabled
|
|
58
|
+
Thread.current[:promiscuous_disabled] || $promiscuous_disabled
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
Promiscuous::Redis.publish(global_key, value)
|
|
62
|
-
value
|
|
61
|
+
def disabled=(value)
|
|
62
|
+
Thread.current[:promiscuous_disabled] = value
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
Promiscuous::Redis.publish(global_key, version)
|
|
68
|
-
version
|
|
65
|
+
def context(*args, &block)
|
|
66
|
+
Publisher::Context.open(*args, &block)
|
|
69
67
|
end
|
|
70
68
|
end
|
|
71
|
-
extend ConsoleHelpers
|
|
72
69
|
|
|
73
70
|
at_exit { self.disconnect rescue nil }
|
|
74
71
|
end
|
data/lib/promiscuous/amqp.rb
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
module Promiscuous::AMQP
|
|
2
2
|
extend Promiscuous::Autoload
|
|
3
|
-
autoload :Bunny, :
|
|
3
|
+
autoload :HotBunnies, :Bunny, :Null, :Fake
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
PUB_EXCHANGE = ENV['PUB_EXCHANGE'] || 'promiscuous'
|
|
6
|
+
SUB_EXCHANGE = ENV['SUB_EXCHANGE'] || 'promiscuous'
|
|
6
7
|
|
|
7
8
|
class << self
|
|
8
9
|
attr_accessor :backend
|
|
10
|
+
attr_accessor :backend_class
|
|
9
11
|
|
|
10
12
|
def backend=(value)
|
|
11
|
-
disconnect
|
|
12
|
-
@
|
|
13
|
-
connect if @
|
|
13
|
+
disconnect
|
|
14
|
+
@backend_class = value.nil? ? nil : "Promiscuous::AMQP::#{value.to_s.camelize.gsub(/amqp/, 'AMQP')}".constantize
|
|
15
|
+
connect if @backend_class
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def lost_connection_exception
|
|
@@ -23,10 +25,27 @@ module Promiscuous::AMQP
|
|
|
23
25
|
|
|
24
26
|
def publish(options={})
|
|
25
27
|
ensure_connected
|
|
26
|
-
Promiscuous.debug "[publish]
|
|
27
|
-
backend.publish(options)
|
|
28
|
+
Promiscuous.debug "[publish] #{options[:key]} -> #{options[:payload]}"
|
|
29
|
+
backend.respond_to?(:async) ? backend.async.publish(options) : backend.publish(options)
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
def connect
|
|
33
|
+
return if @backend
|
|
34
|
+
@backend = backend_class.new
|
|
35
|
+
@backend.connect
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def disconnect
|
|
39
|
+
return unless @backend
|
|
40
|
+
@backend.disconnect
|
|
41
|
+
@backend.terminate if @backend.respond_to?(:terminate)
|
|
42
|
+
@backend = nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
delegate :connected?, :to => :backend
|
|
46
|
+
|
|
47
|
+
def const_missing(sym)
|
|
48
|
+
backend_class.const_get(sym)
|
|
49
|
+
end
|
|
31
50
|
end
|
|
32
51
|
end
|
|
@@ -1,30 +1,145 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
class Promiscuous::AMQP::Bunny
|
|
2
|
+
def self.hijack_bunny
|
|
3
|
+
return if @bunny_hijacked
|
|
4
|
+
::Bunny::Session.class_eval do
|
|
5
|
+
alias_method :handle_network_failure_without_promiscuous, :handle_network_failure
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
def handle_network_failure(e)
|
|
8
|
+
Promiscuous.warn "[amqp] #{e}. Reconnecting..."
|
|
9
|
+
Promiscuous::Config.error_notifier.try(:call, e)
|
|
10
|
+
handle_network_failure_without_promiscuous(e)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
@bunny_hijacked = true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_accessor :connection, :connection_lock, :callback_mapping
|
|
17
|
+
|
|
18
|
+
def initialize_driver
|
|
5
19
|
require 'bunny'
|
|
6
|
-
self.
|
|
7
|
-
|
|
20
|
+
self.class.hijack_bunny
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize
|
|
24
|
+
initialize_driver
|
|
25
|
+
# The bunny socket doesn't like when multiple threads access to it apparently
|
|
26
|
+
@connection_lock = Mutex.new
|
|
27
|
+
@callback_mapping = {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def connect
|
|
31
|
+
@connection, @channel = new_connection
|
|
32
|
+
@exchange = exchange(@channel, :pub)
|
|
33
|
+
confirm_select(@channel, &method(:on_confirm))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def new_connection
|
|
37
|
+
connection = ::Bunny.new(Promiscuous::Config.amqp_url,
|
|
38
|
+
:heartbeat_interval => Promiscuous::Config.heartbeat,
|
|
39
|
+
:socket_timeout => Promiscuous::Config.socket_timeout,
|
|
40
|
+
:connect_timeout => Promiscuous::Config.socket_timeout)
|
|
41
|
+
connection.start
|
|
42
|
+
|
|
43
|
+
channel = connection.create_channel
|
|
44
|
+
[connection, channel]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def disconnect
|
|
48
|
+
@connection_lock.synchronize do
|
|
49
|
+
return unless connected?
|
|
50
|
+
@connection.stop
|
|
51
|
+
@connection = @channel = nil
|
|
52
|
+
end
|
|
8
53
|
end
|
|
9
54
|
|
|
10
|
-
def
|
|
11
|
-
|
|
55
|
+
def connected?
|
|
56
|
+
!!@connection.try(:connected?)
|
|
12
57
|
end
|
|
13
58
|
|
|
14
|
-
def
|
|
15
|
-
|
|
59
|
+
def raw_publish(options)
|
|
60
|
+
@exchange.publish(options[:payload], :key => options[:key], :persistent => true)
|
|
16
61
|
end
|
|
17
62
|
|
|
18
|
-
def
|
|
19
|
-
|
|
20
|
-
|
|
63
|
+
def exchange(channel, which)
|
|
64
|
+
exchange_name = which == :pub ? Promiscuous::AMQP::PUB_EXCHANGE :
|
|
65
|
+
Promiscuous::AMQP::SUB_EXCHANGE
|
|
66
|
+
channel.exchange(exchange_name, :type => :topic, :durable => true)
|
|
21
67
|
end
|
|
22
68
|
|
|
23
|
-
def
|
|
24
|
-
|
|
69
|
+
def publish(options={})
|
|
70
|
+
@connection_lock.synchronize do
|
|
71
|
+
tag = @channel.next_publish_seq_no if options[:on_confirm]
|
|
72
|
+
raw_publish(options)
|
|
73
|
+
@callback_mapping[tag] = options[:on_confirm] if options[:on_confirm]
|
|
74
|
+
end
|
|
75
|
+
rescue Exception => e
|
|
76
|
+
e = Promiscuous::Error::Publisher.new(e, :payload => options[:payload])
|
|
77
|
+
Promiscuous.warn "[publish] #{e} #{e.backtrace.join("\n")}"
|
|
78
|
+
Promiscuous::Config.error_notifier.try(:call, e)
|
|
25
79
|
end
|
|
26
80
|
|
|
27
|
-
def
|
|
28
|
-
|
|
81
|
+
def confirm_select(channel, &callback)
|
|
82
|
+
channel.confirm_select(callback)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def on_confirm(tag, multiple, nack=false)
|
|
86
|
+
if multiple
|
|
87
|
+
cbs = @callback_mapping.keys
|
|
88
|
+
.select { |k| k <= tag }
|
|
89
|
+
.map { |k| @callback_mapping.delete(k) }
|
|
90
|
+
cbs.each(&:call) unless nack
|
|
91
|
+
else
|
|
92
|
+
cb = @callback_mapping.delete(tag)
|
|
93
|
+
cb.try(:call) unless nack
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
module Subscriber
|
|
98
|
+
def subscribe(options={}, &block)
|
|
99
|
+
queue_name = options[:queue_name]
|
|
100
|
+
bindings = options[:bindings]
|
|
101
|
+
Promiscuous::AMQP.ensure_connected
|
|
102
|
+
|
|
103
|
+
@lock = Mutex.new
|
|
104
|
+
@connection, @channel = Promiscuous::AMQP.backend.new_connection
|
|
105
|
+
@channel.basic_qos(Promiscuous::Config.prefetch)
|
|
106
|
+
exchange = Promiscuous::AMQP.backend.exchange(@channel, :sub)
|
|
107
|
+
@queue = @channel.queue(queue_name, Promiscuous::Config.queue_options)
|
|
108
|
+
bindings.each do |binding|
|
|
109
|
+
@queue.bind(exchange, :routing_key => binding)
|
|
110
|
+
Promiscuous.debug "[bind] #{queue_name} -> #{binding}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
@subscription = subscribe_queue(@queue, &block)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def subscribe_queue(queue, &block)
|
|
117
|
+
queue.subscribe(:ack => true) do |delivery_info, metadata, payload|
|
|
118
|
+
block.call(MetaData.new(self, delivery_info), payload)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class MetaData
|
|
123
|
+
def initialize(subscriber, delivery_info)
|
|
124
|
+
@subscriber = subscriber
|
|
125
|
+
@delivery_info = delivery_info
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def ack
|
|
129
|
+
@subscriber.ack_message(@delivery_info.delivery_tag)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def ack_message(tag)
|
|
134
|
+
@lock.synchronize { @channel.ack(tag) } if @channel
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def recover
|
|
138
|
+
@lock.synchronize { @channel.basic_recover(true) } if @channel
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def disconnect
|
|
142
|
+
@lock.synchronize { @connection.stop; @channel = nil }
|
|
143
|
+
end
|
|
29
144
|
end
|
|
30
145
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class Promiscuous::AMQP::Fake
|
|
2
|
+
attr_accessor :messages
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
def backend
|
|
6
|
+
Promiscuous::AMQP.backend
|
|
7
|
+
end
|
|
8
|
+
delegate :num_messages, :get_next_message, :get_next_payload, :to => :backend
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def connect
|
|
12
|
+
@messages = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def disconnect
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def connected?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def publish(options={})
|
|
23
|
+
@messages << options
|
|
24
|
+
options[:on_confirm].try(:call)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def num_messages
|
|
28
|
+
@messages.count
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def get_next_message
|
|
32
|
+
@messages.shift
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def get_next_payload
|
|
36
|
+
JSON.parse(get_next_message[:payload])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def open_queue(options={}, &block)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module Subscriber
|
|
43
|
+
def subscribe(options={}, &block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def recover
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def disconnect
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
class Promiscuous::AMQP::HotBunnies < Promiscuous::AMQP::Bunny
|
|
2
|
+
attr_accessor :connection
|
|
3
|
+
|
|
4
|
+
def initialize_driver
|
|
5
|
+
require 'hot_bunnies'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# TODO auto reconnect
|
|
9
|
+
|
|
10
|
+
def new_connection
|
|
11
|
+
connection = ::HotBunnies.connect(:uri => Promiscuous::Config.amqp_url,
|
|
12
|
+
:heartbeat_interval => Promiscuous::Config.heartbeat,
|
|
13
|
+
:connection_timeout => Promiscuous::Config.socket_timeout)
|
|
14
|
+
|
|
15
|
+
channel = connection.create_channel
|
|
16
|
+
[connection, channel]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def disconnect
|
|
20
|
+
@connection_lock.synchronize do
|
|
21
|
+
return unless connected?
|
|
22
|
+
@channel.close rescue nil
|
|
23
|
+
@connection.close rescue nil
|
|
24
|
+
@connection = @channel = nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def connected?
|
|
29
|
+
!!@connection.try(:is_open)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def raw_publish(options={})
|
|
33
|
+
@exchange.publish(options[:payload], :routing_key => options[:key], :persistent => true)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def confirm_select(channel, &callback)
|
|
37
|
+
channel.add_confirm_listener(&callback)
|
|
38
|
+
channel.confirm_select
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module Subscriber
|
|
42
|
+
include Promiscuous::AMQP::Bunny::Subscriber
|
|
43
|
+
|
|
44
|
+
def subscribe_queue(queue, &block)
|
|
45
|
+
queue.subscribe(:ack => true, :blocking => false, &block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def disconnect
|
|
49
|
+
@lock.synchronize do
|
|
50
|
+
@channel = nil
|
|
51
|
+
@subscription.shutdown! rescue nil
|
|
52
|
+
@connection.close rescue nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
def
|
|
1
|
+
class Promiscuous::AMQP::Null
|
|
2
|
+
def connect
|
|
3
3
|
end
|
|
4
4
|
|
|
5
|
-
def
|
|
5
|
+
def disconnect
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def connected?
|
|
9
9
|
true
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def
|
|
12
|
+
def publish(options={})
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def
|
|
15
|
+
def open_queue(options={}, &block)
|
|
16
16
|
end
|
|
17
17
|
end
|
data/lib/promiscuous/cli.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
class Promiscuous::CLI
|
|
2
2
|
attr_accessor :options
|
|
3
3
|
|
|
4
|
-
def
|
|
4
|
+
def trap_debug_signals
|
|
5
5
|
Signal.trap 'SIGUSR2' do
|
|
6
6
|
Thread.list.each do |thread|
|
|
7
|
-
print_status '-' *
|
|
7
|
+
print_status '----[ Threads ]----' + '-' * (100-19)
|
|
8
8
|
if thread.backtrace
|
|
9
9
|
print_status "Thread #{thread} #{thread['label']}"
|
|
10
10
|
print_status thread.backtrace.join("\n")
|
|
@@ -12,37 +12,98 @@ class Promiscuous::CLI
|
|
|
12
12
|
print_status "Thread #{thread} #{thread['label']} -- no backtrace"
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
if @worker && @worker.respond_to?(:message_synchronizer)
|
|
17
|
+
if blocked_messages = @worker.message_synchronizer.try(:blocked_messages)
|
|
18
|
+
print_status '----[ Pending Dependencies ]----' + '-' * (100-32)
|
|
19
|
+
blocked_messages.reverse_each { |msg| print_status msg }
|
|
20
|
+
end
|
|
21
|
+
print_status '-' * 80
|
|
22
|
+
end
|
|
15
23
|
end
|
|
24
|
+
end
|
|
16
25
|
|
|
26
|
+
def trap_exit_signals
|
|
17
27
|
%w(SIGTERM SIGINT).each do |signal|
|
|
18
28
|
Signal.trap(signal) do
|
|
29
|
+
exit 1 if @stop
|
|
19
30
|
print_status "Exiting..."
|
|
20
|
-
@worker.
|
|
31
|
+
@worker.try(:stop)
|
|
32
|
+
@worker = nil
|
|
21
33
|
@stop = true
|
|
22
34
|
end
|
|
23
35
|
end
|
|
24
36
|
end
|
|
25
37
|
|
|
38
|
+
def trap_signals
|
|
39
|
+
trap_debug_signals
|
|
40
|
+
trap_exit_signals
|
|
41
|
+
end
|
|
42
|
+
|
|
26
43
|
def publish
|
|
27
44
|
options[:criterias].map { |criteria| eval(criteria) }.each do |criteria|
|
|
28
45
|
break if @stop
|
|
29
46
|
title = criteria.name
|
|
30
47
|
title = "#{title}#{' ' * [0, 20 - title.size].max}"
|
|
31
48
|
bar = ProgressBar.create(:format => '%t |%b>%i| %c/%C %e', :title => title, :total => criteria.count)
|
|
32
|
-
criteria.each do |doc|
|
|
49
|
+
criteria.unscoped.each do |doc|
|
|
33
50
|
break if @stop
|
|
34
|
-
doc.
|
|
51
|
+
Promiscuous.context("cli/sync") { doc.promiscuous.sync }
|
|
35
52
|
bar.increment
|
|
36
53
|
end
|
|
37
54
|
end
|
|
38
55
|
end
|
|
39
56
|
|
|
57
|
+
def record
|
|
58
|
+
@worker = Promiscuous::Subscriber::Worker::Recorder.new(options[:log_file])
|
|
59
|
+
@worker.start
|
|
60
|
+
print_status "Recording..."
|
|
61
|
+
sleep 0.2 until !@worker
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def replay_payload(payload)
|
|
65
|
+
endpoint = MultiJson.load(payload)['__amqp__']
|
|
66
|
+
if endpoint
|
|
67
|
+
# TODO confirm
|
|
68
|
+
Promiscuous::AMQP.publish(:key => endpoint, :payload => payload)
|
|
69
|
+
@num_msg += 1
|
|
70
|
+
else
|
|
71
|
+
puts "[warn] missing destination in #{payload}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def replay
|
|
76
|
+
require 'json'
|
|
77
|
+
@num_msg = 0
|
|
78
|
+
File.open(options[:log_file], 'r').each do |line|
|
|
79
|
+
break if @stop
|
|
80
|
+
case line
|
|
81
|
+
when /^\[promiscuous\] \[receive\] ({.*})$/ then replay_payload($1)
|
|
82
|
+
when /^\[promiscuous\] \[publish\] .* -> ({.*})$/ then replay_payload($1)
|
|
83
|
+
when /^({.*})$/ then replay_payload($1)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
puts "Replayed #{@num_msg} messages"
|
|
88
|
+
end
|
|
89
|
+
|
|
40
90
|
def subscribe
|
|
41
|
-
Promiscuous::
|
|
42
|
-
@worker
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
91
|
+
@worker = Promiscuous::Subscriber::Worker.new
|
|
92
|
+
@worker.start
|
|
93
|
+
print_status "Replicating..."
|
|
94
|
+
sleep 0.2 until !@worker
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def publisher_recovery
|
|
98
|
+
@worker = Promiscuous::Publisher::Worker.new
|
|
99
|
+
@worker.start
|
|
100
|
+
print_status "Waiting for messages to recover..."
|
|
101
|
+
sleep 0.2 until !@worker
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def generate_mocks
|
|
105
|
+
f = options[:output] ? File.open(options[:output], 'w') : STDOUT
|
|
106
|
+
f.write Promiscuous::Publisher::MockGenerator.generate
|
|
46
107
|
end
|
|
47
108
|
|
|
48
109
|
def parse_args(args)
|
|
@@ -54,8 +115,12 @@ class Promiscuous::CLI
|
|
|
54
115
|
|
|
55
116
|
opts.separator ""
|
|
56
117
|
opts.separator "Actions:"
|
|
57
|
-
opts.separator " promiscuous publish \"
|
|
118
|
+
opts.separator " promiscuous publish \"Model1.where(:updated_at.gt => 1.day.ago)\" Model2 Model3..."
|
|
58
119
|
opts.separator " promiscuous subscribe"
|
|
120
|
+
opts.separator " promiscuous mocks"
|
|
121
|
+
opts.separator " promiscuous record logfile"
|
|
122
|
+
opts.separator " promiscuous replay logfile"
|
|
123
|
+
opts.separator " promiscuous publisher_recovery"
|
|
59
124
|
opts.separator ""
|
|
60
125
|
opts.separator "Options:"
|
|
61
126
|
|
|
@@ -76,6 +141,14 @@ class Promiscuous::CLI
|
|
|
76
141
|
Promiscuous::Config.prefetch = prefetch.to_i
|
|
77
142
|
end
|
|
78
143
|
|
|
144
|
+
opts.on "-o", "--output FILE", "Output file for mocks. Defaults to stdout" do |file|
|
|
145
|
+
options[:output] = file
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
opts.on "-s", "--stat-interval [DURATION]", "Stats refresh rate (0 to disable)" do |duration|
|
|
149
|
+
Promiscuous::Config.stats_interval = duration.to_f
|
|
150
|
+
end
|
|
151
|
+
|
|
79
152
|
opts.on("-h", "--help", "Show this message") do
|
|
80
153
|
puts opts
|
|
81
154
|
exit
|
|
@@ -93,19 +166,21 @@ class Promiscuous::CLI
|
|
|
93
166
|
|
|
94
167
|
options[:action] = args.shift.try(:to_sym)
|
|
95
168
|
options[:criterias] = args
|
|
169
|
+
options[:log_file] = args.first
|
|
96
170
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
else
|
|
105
|
-
raise "Why are you specifying a criteria?" if options[:criterias].present?
|
|
171
|
+
case options[:action]
|
|
172
|
+
when :publish then raise "Please specify one or more criterias" unless options[:criterias].present?
|
|
173
|
+
when :subscribe then raise "Why are you specifying a criteria?" if options[:criterias].present?
|
|
174
|
+
when :record then raise "Please specify a log file to record" unless options[:log_file].present?
|
|
175
|
+
when :replay then raise "Please specify a log file to replay" unless options[:log_file].present?
|
|
176
|
+
when :publisher_recovery
|
|
177
|
+
when :mocks
|
|
178
|
+
else puts parser; exit 1
|
|
106
179
|
end
|
|
107
180
|
|
|
108
181
|
options
|
|
182
|
+
rescue SystemExit
|
|
183
|
+
exit
|
|
109
184
|
rescue Exception => e
|
|
110
185
|
puts e
|
|
111
186
|
exit
|
|
@@ -113,10 +188,15 @@ class Promiscuous::CLI
|
|
|
113
188
|
|
|
114
189
|
def load_app
|
|
115
190
|
if options[:require]
|
|
116
|
-
|
|
191
|
+
begin
|
|
192
|
+
require options[:require]
|
|
193
|
+
rescue LoadError
|
|
194
|
+
require "./#{options[:require]}"
|
|
195
|
+
end
|
|
117
196
|
else
|
|
118
197
|
require 'rails'
|
|
119
|
-
require
|
|
198
|
+
require 'promiscuous/railtie'
|
|
199
|
+
require File.expand_path("./config/environment")
|
|
120
200
|
::Rails.application.eager_load!
|
|
121
201
|
end
|
|
122
202
|
end
|
|
@@ -131,8 +211,12 @@ class Promiscuous::CLI
|
|
|
131
211
|
def run
|
|
132
212
|
trap_signals
|
|
133
213
|
case options[:action]
|
|
134
|
-
when :publish
|
|
214
|
+
when :publish then publish
|
|
135
215
|
when :subscribe then subscribe
|
|
216
|
+
when :record then record
|
|
217
|
+
when :replay then replay
|
|
218
|
+
when :mocks then generate_mocks
|
|
219
|
+
when :publisher_recovery then publisher_recovery
|
|
136
220
|
end
|
|
137
221
|
end
|
|
138
222
|
|
|
@@ -146,6 +230,6 @@ class Promiscuous::CLI
|
|
|
146
230
|
|
|
147
231
|
def print_status(msg)
|
|
148
232
|
Promiscuous.info msg
|
|
149
|
-
|
|
233
|
+
STDERR.puts msg
|
|
150
234
|
end
|
|
151
235
|
end
|