promiscuous 0.53.1 → 0.90.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|