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.
- checksums.yaml +7 -0
- data/lib/promiscuous/amqp/bunny.rb +63 -36
- data/lib/promiscuous/amqp/fake.rb +3 -1
- data/lib/promiscuous/amqp/hot_bunnies.rb +26 -16
- data/lib/promiscuous/amqp/null.rb +1 -0
- data/lib/promiscuous/amqp.rb +12 -12
- data/lib/promiscuous/cli.rb +70 -29
- data/lib/promiscuous/config.rb +54 -29
- data/lib/promiscuous/convenience.rb +1 -1
- data/lib/promiscuous/dependency.rb +25 -6
- data/lib/promiscuous/error/connection.rb +11 -9
- data/lib/promiscuous/error/dependency.rb +8 -1
- data/lib/promiscuous/loader.rb +4 -2
- data/lib/promiscuous/publisher/bootstrap/connection.rb +25 -0
- data/lib/promiscuous/publisher/bootstrap/data.rb +127 -0
- data/lib/promiscuous/publisher/bootstrap/mode.rb +19 -0
- data/lib/promiscuous/publisher/bootstrap/status.rb +40 -0
- data/lib/promiscuous/publisher/bootstrap/version.rb +46 -0
- data/lib/promiscuous/publisher/bootstrap.rb +27 -0
- data/lib/promiscuous/publisher/context/base.rb +67 -0
- data/lib/promiscuous/{middleware.rb → publisher/context/middleware.rb} +16 -13
- data/lib/promiscuous/publisher/context/transaction.rb +36 -0
- data/lib/promiscuous/publisher/context.rb +4 -88
- data/lib/promiscuous/publisher/mock_generator.rb +9 -9
- data/lib/promiscuous/publisher/model/active_record.rb +7 -7
- data/lib/promiscuous/publisher/model/base.rb +29 -29
- data/lib/promiscuous/publisher/model/ephemeral.rb +5 -3
- data/lib/promiscuous/publisher/model/mock.rb +9 -5
- data/lib/promiscuous/publisher/model/mongoid.rb +5 -22
- data/lib/promiscuous/publisher/operation/active_record.rb +360 -0
- data/lib/promiscuous/publisher/operation/atomic.rb +167 -0
- data/lib/promiscuous/publisher/operation/base.rb +279 -474
- data/lib/promiscuous/publisher/operation/mongoid.rb +153 -145
- data/lib/promiscuous/publisher/operation/non_persistent.rb +28 -0
- data/lib/promiscuous/publisher/operation/proxy_for_query.rb +42 -0
- data/lib/promiscuous/publisher/operation/transaction.rb +85 -0
- data/lib/promiscuous/publisher/operation.rb +1 -1
- data/lib/promiscuous/publisher/worker.rb +7 -7
- data/lib/promiscuous/publisher.rb +1 -1
- data/lib/promiscuous/railtie.rb +20 -5
- data/lib/promiscuous/redis.rb +104 -56
- data/lib/promiscuous/subscriber/message_processor/base.rb +38 -0
- data/lib/promiscuous/subscriber/message_processor/bootstrap.rb +17 -0
- data/lib/promiscuous/subscriber/message_processor/regular.rb +192 -0
- data/lib/promiscuous/subscriber/message_processor.rb +4 -0
- data/lib/promiscuous/subscriber/model/base.rb +20 -15
- data/lib/promiscuous/subscriber/model/mongoid.rb +4 -4
- data/lib/promiscuous/subscriber/model/observer.rb +16 -2
- data/lib/promiscuous/subscriber/operation/base.rb +68 -0
- data/lib/promiscuous/subscriber/operation/bootstrap.rb +54 -0
- data/lib/promiscuous/subscriber/operation/regular.rb +13 -0
- data/lib/promiscuous/subscriber/operation.rb +3 -166
- data/lib/promiscuous/subscriber/worker/message.rb +61 -35
- data/lib/promiscuous/subscriber/worker/message_synchronizer.rb +90 -59
- data/lib/promiscuous/subscriber/worker/pump.rb +17 -5
- data/lib/promiscuous/subscriber/worker/recorder.rb +4 -1
- data/lib/promiscuous/subscriber/worker/runner.rb +49 -9
- data/lib/promiscuous/subscriber/worker/stats.rb +2 -2
- data/lib/promiscuous/subscriber/worker.rb +6 -0
- data/lib/promiscuous/subscriber.rb +1 -1
- data/lib/promiscuous/timer.rb +31 -18
- data/lib/promiscuous/version.rb +1 -1
- data/lib/promiscuous.rb +23 -3
- metadata +104 -89
- data/lib/promiscuous/subscriber/payload.rb +0 -34
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b84ccb396c096ef9d2164d4400356606f90c3954
|
4
|
+
data.tar.gz: 31da27f6d1163623df3014dc15a0641d6e93c0c3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2cec057b926337d6940a8d3144b88ccc3dcf200a35dc74d88f69bf4ad388d034b6e68a04dd2f270f5c581382bef3ae5985b6e4ad9e7d9cfe44babf6cdacabd02
|
7
|
+
data.tar.gz: d84a82ecdcb22041d72c08e8c9fb443e77c2c9a74c49589a6d209079df7055f1adc484ca12b32d6f75f9a04e8f66bcfbaefeff020eb47b38c6fecbe3e1bc0c0b
|
@@ -6,7 +6,7 @@ class Promiscuous::AMQP::Bunny
|
|
6
6
|
|
7
7
|
def handle_network_failure(e)
|
8
8
|
Promiscuous.warn "[amqp] #{e}. Reconnecting..."
|
9
|
-
Promiscuous::Config.error_notifier.
|
9
|
+
Promiscuous::Config.error_notifier.call(e)
|
10
10
|
handle_network_failure_without_promiscuous(e)
|
11
11
|
end
|
12
12
|
end
|
@@ -27,21 +27,40 @@ class Promiscuous::AMQP::Bunny
|
|
27
27
|
@callback_mapping = {}
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
def raw_new_connection(options={})
|
31
|
+
connection = ::Bunny.new(options[:url], :heartbeat_interval => Promiscuous::Config.heartbeat,
|
32
|
+
:socket_timeout => Promiscuous::Config.socket_timeout,
|
33
|
+
:connect_timeout => Promiscuous::Config.socket_timeout)
|
34
|
+
connection.start
|
35
|
+
connection
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
:socket_timeout => Promiscuous::Config.socket_timeout,
|
40
|
-
:connect_timeout => Promiscuous::Config.socket_timeout)
|
41
|
-
connection.start
|
38
|
+
def raw_confirm_select(channel, &callback)
|
39
|
+
channel.confirm_select(callback)
|
40
|
+
end
|
42
41
|
|
42
|
+
def new_connection(options={})
|
43
|
+
connection = raw_new_connection(options)
|
43
44
|
channel = connection.create_channel
|
44
|
-
[
|
45
|
+
channel.basic_qos(options[:prefetch]) if options[:prefetch]
|
46
|
+
raw_confirm_select(channel, &method(:on_confirm)) if options[:confirm]
|
47
|
+
|
48
|
+
if options[:exchanges]
|
49
|
+
exchanges = options[:exchanges].map do |exchange_name|
|
50
|
+
channel.exchange(exchange_name, :type => :topic, :durable => true)
|
51
|
+
end
|
52
|
+
[connection, channel, exchanges]
|
53
|
+
else
|
54
|
+
exchange = channel.exchange(options[:exchange], :type => :topic, :durable => true)
|
55
|
+
[connection, channel, exchange]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def connect
|
60
|
+
connection_options = { :url => Promiscuous::Config.publisher_amqp_url,
|
61
|
+
:exchange => Promiscuous::Config.publisher_exchange,
|
62
|
+
:confirm => true }
|
63
|
+
@connection, @channel, @exchange = new_connection(connection_options)
|
45
64
|
end
|
46
65
|
|
47
66
|
def disconnect
|
@@ -57,16 +76,13 @@ class Promiscuous::AMQP::Bunny
|
|
57
76
|
end
|
58
77
|
|
59
78
|
def raw_publish(options)
|
60
|
-
|
61
|
-
end
|
62
|
-
|
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)
|
79
|
+
options[:exchange].publish(options[:payload], :key => options[:key], :persistent => true)
|
67
80
|
end
|
68
81
|
|
69
82
|
def publish(options={})
|
83
|
+
options[:exchange] ||= @exchange
|
84
|
+
Promiscuous.debug "[publish] #{options[:exchange].name}/#{options[:key]} #{options[:payload]}"
|
85
|
+
|
70
86
|
@connection_lock.synchronize do
|
71
87
|
tag = @channel.next_publish_seq_no if options[:on_confirm]
|
72
88
|
raw_publish(options)
|
@@ -74,12 +90,8 @@ class Promiscuous::AMQP::Bunny
|
|
74
90
|
end
|
75
91
|
rescue Exception => e
|
76
92
|
e = Promiscuous::Error::Publisher.new(e, :payload => options[:payload])
|
77
|
-
Promiscuous.warn "[publish] #{e}
|
78
|
-
Promiscuous::Config.error_notifier.
|
79
|
-
end
|
80
|
-
|
81
|
-
def confirm_select(channel, &callback)
|
82
|
-
channel.confirm_select(callback)
|
93
|
+
Promiscuous.warn "[publish] #{e}\n#{e.backtrace.join("\n")}"
|
94
|
+
Promiscuous::Config.error_notifier.call(e)
|
83
95
|
end
|
84
96
|
|
85
97
|
def on_confirm(tag, multiple, nack=false)
|
@@ -96,18 +108,20 @@ class Promiscuous::AMQP::Bunny
|
|
96
108
|
|
97
109
|
module Subscriber
|
98
110
|
def subscribe(options={}, &block)
|
99
|
-
queue_name = options[:queue_name]
|
100
|
-
bindings = options[:bindings]
|
101
|
-
Promiscuous::AMQP.ensure_connected
|
102
|
-
|
103
111
|
@lock = Mutex.new
|
104
|
-
@
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
112
|
+
@prefetch = Promiscuous::Config.prefetch
|
113
|
+
|
114
|
+
connection_options = { :url => Promiscuous::Config.subscriber_amqp_url,
|
115
|
+
:exchanges => options[:bindings].keys,
|
116
|
+
:prefetch => @prefetch }
|
117
|
+
@connection, @channel, exchanges = Promiscuous::AMQP.new_connection(connection_options)
|
118
|
+
|
119
|
+
@queue = @channel.queue(Promiscuous::Config.queue_name, Promiscuous::Config.queue_options)
|
120
|
+
exchanges.zip(options[:bindings].values).each do |exchange, bindings|
|
121
|
+
bindings.each do |binding|
|
122
|
+
@queue.bind(exchange, :routing_key => binding)
|
123
|
+
Promiscuous.debug "[bind] #{exchange.name}/#{binding}/#{Promiscuous::Config.queue_name}"
|
124
|
+
end
|
111
125
|
end
|
112
126
|
|
113
127
|
@subscription = subscribe_queue(@queue, &block)
|
@@ -128,12 +142,25 @@ class Promiscuous::AMQP::Bunny
|
|
128
142
|
def ack
|
129
143
|
@subscriber.ack_message(@delivery_info.delivery_tag)
|
130
144
|
end
|
145
|
+
|
146
|
+
def postpone
|
147
|
+
@subscriber.postpone_message
|
148
|
+
end
|
131
149
|
end
|
132
150
|
|
133
151
|
def ack_message(tag)
|
134
152
|
@lock.synchronize { @channel.ack(tag) } if @channel
|
135
153
|
end
|
136
154
|
|
155
|
+
def postpone_message
|
156
|
+
# Not using nacks, because the message gets sent back right away so this
|
157
|
+
# is a no-op.
|
158
|
+
|
159
|
+
# TODO: Even though the prefetch window is set to 10mil we should still
|
160
|
+
# check that the unacked messages doesn't exceed this limit and increase
|
161
|
+
# the prefetch window.
|
162
|
+
end
|
163
|
+
|
137
164
|
def recover
|
138
165
|
@lock.synchronize { @channel.basic_recover(true) } if @channel
|
139
166
|
end
|
@@ -20,6 +20,7 @@ class Promiscuous::AMQP::Fake
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def publish(options={})
|
23
|
+
Promiscuous.debug "[publish (fake)] #{options[:exchange].try(:name) || "default"}/#{options[:key]} #{options[:payload]}"
|
23
24
|
@messages << options
|
24
25
|
options[:on_confirm].try(:call)
|
25
26
|
end
|
@@ -33,7 +34,8 @@ class Promiscuous::AMQP::Fake
|
|
33
34
|
end
|
34
35
|
|
35
36
|
def get_next_payload
|
36
|
-
|
37
|
+
msg = get_next_message
|
38
|
+
msg && JSON.parse(msg[:payload])
|
37
39
|
end
|
38
40
|
|
39
41
|
def open_queue(options={}, &block)
|
@@ -7,13 +7,19 @@ class Promiscuous::AMQP::HotBunnies < Promiscuous::AMQP::Bunny
|
|
7
7
|
|
8
8
|
# TODO auto reconnect
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def raw_new_connection(options={})
|
11
|
+
::HotBunnies.connect(:uri => options[:url],
|
12
|
+
:heartbeat_interval => Promiscuous::Config.heartbeat,
|
13
|
+
:connection_timeout => Promiscuous::Config.socket_timeout)
|
14
|
+
end
|
15
|
+
|
16
|
+
def raw_confirm_select(channel, &callback)
|
17
|
+
channel.add_confirm_listener(&callback)
|
18
|
+
channel.confirm_select
|
19
|
+
end
|
14
20
|
|
15
|
-
|
16
|
-
[
|
21
|
+
def raw_publish(options={})
|
22
|
+
options[:exchange].publish(options[:payload], :routing_key => options[:key], :persistent => true)
|
17
23
|
end
|
18
24
|
|
19
25
|
def disconnect
|
@@ -29,20 +35,24 @@ class Promiscuous::AMQP::HotBunnies < Promiscuous::AMQP::Bunny
|
|
29
35
|
!!@connection.try(:is_open)
|
30
36
|
end
|
31
37
|
|
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
38
|
module Subscriber
|
42
39
|
include Promiscuous::AMQP::Bunny::Subscriber
|
43
40
|
|
44
41
|
def subscribe_queue(queue, &block)
|
45
|
-
queue.subscribe(:ack => true, :blocking => false,
|
42
|
+
queue.subscribe(:ack => true, :blocking => false) do |metadata, payload|
|
43
|
+
block.call(MetaData.new(self, metadata), payload)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class MetaData < Promiscuous::AMQP::Bunny::Subscriber::MetaData
|
48
|
+
def initialize(subscriber, metadata)
|
49
|
+
@subscriber = subscriber
|
50
|
+
@metadata = metadata
|
51
|
+
end
|
52
|
+
|
53
|
+
def ack
|
54
|
+
@metadata.ack
|
55
|
+
end
|
46
56
|
end
|
47
57
|
|
48
58
|
def disconnect
|
data/lib/promiscuous/amqp.rb
CHANGED
@@ -2,8 +2,8 @@ module Promiscuous::AMQP
|
|
2
2
|
extend Promiscuous::Autoload
|
3
3
|
autoload :HotBunnies, :Bunny, :Null, :Fake
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
LIVE_EXCHANGE = 'promiscuous'
|
6
|
+
BOOTSTRAP_EXCHANGE = 'promiscuous.bootstrap'
|
7
7
|
|
8
8
|
class << self
|
9
9
|
attr_accessor :backend
|
@@ -12,21 +12,16 @@ module Promiscuous::AMQP
|
|
12
12
|
def backend=(value)
|
13
13
|
disconnect
|
14
14
|
@backend_class = value.nil? ? nil : "Promiscuous::AMQP::#{value.to_s.camelize.gsub(/amqp/, 'AMQP')}".constantize
|
15
|
-
connect if @backend_class
|
16
15
|
end
|
17
16
|
|
18
|
-
def lost_connection_exception
|
19
|
-
Promiscuous::Error::Connection.new(
|
17
|
+
def lost_connection_exception(options={})
|
18
|
+
Promiscuous::Error::Connection.new(Promiscuous::Config.publisher_amqp_url, options)
|
20
19
|
end
|
21
20
|
|
22
21
|
def ensure_connected
|
23
|
-
|
24
|
-
end
|
22
|
+
Promiscuous.ensure_connected
|
25
23
|
|
26
|
-
|
27
|
-
ensure_connected
|
28
|
-
Promiscuous.debug "[publish] #{options[:key]} -> #{options[:payload]}"
|
29
|
-
backend.respond_to?(:async) ? backend.async.publish(options) : backend.publish(options)
|
24
|
+
raise lost_connection_exception unless connected?
|
30
25
|
end
|
31
26
|
|
32
27
|
def connect
|
@@ -42,7 +37,12 @@ module Promiscuous::AMQP
|
|
42
37
|
@backend = nil
|
43
38
|
end
|
44
39
|
|
45
|
-
|
40
|
+
def new_connection(*args)
|
41
|
+
ensure_connected
|
42
|
+
backend.new_connection(*args)
|
43
|
+
end
|
44
|
+
|
45
|
+
delegate :publish, :connected?, :to => :backend
|
46
46
|
|
47
47
|
def const_missing(sym)
|
48
48
|
backend_class.const_get(sym)
|
data/lib/promiscuous/cli.rb
CHANGED
@@ -18,7 +18,7 @@ class Promiscuous::CLI
|
|
18
18
|
print_status '----[ Pending Dependencies ]----' + '-' * (100-32)
|
19
19
|
blocked_messages.reverse_each { |msg| print_status msg }
|
20
20
|
end
|
21
|
-
print_status '-' *
|
21
|
+
print_status '-' * 100
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -26,11 +26,14 @@ class Promiscuous::CLI
|
|
26
26
|
def trap_exit_signals
|
27
27
|
%w(SIGTERM SIGINT).each do |signal|
|
28
28
|
Signal.trap(signal) do
|
29
|
-
exit 1 if @stop
|
30
29
|
print_status "Exiting..."
|
31
|
-
@
|
32
|
-
|
33
|
-
|
30
|
+
if @stop
|
31
|
+
@worker.try(:show_stop_status)
|
32
|
+
else
|
33
|
+
@stop = true
|
34
|
+
@worker.try(:stop)
|
35
|
+
@worker = nil
|
36
|
+
end
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
@@ -46,7 +49,7 @@ class Promiscuous::CLI
|
|
46
49
|
title = criteria.name
|
47
50
|
title = "#{title}#{' ' * [0, 20 - title.size].max}"
|
48
51
|
bar = ProgressBar.create(:format => '%t |%b>%i| %c/%C %e', :title => title, :total => criteria.count)
|
49
|
-
criteria.
|
52
|
+
criteria.each do |doc|
|
50
53
|
break if @stop
|
51
54
|
Promiscuous.context("cli/sync") { doc.promiscuous.sync }
|
52
55
|
bar.increment
|
@@ -84,13 +87,25 @@ class Promiscuous::CLI
|
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
87
|
-
|
90
|
+
print_status "Replayed #{@num_msg} messages"
|
91
|
+
end
|
92
|
+
|
93
|
+
def bootstrap
|
94
|
+
phase = options[:criterias][0].to_sym
|
95
|
+
raise "Subscriber bootstrap must be one of [setup|run|finalize|status]" unless [:setup, :run, :finalize, :status].include?(phase)
|
96
|
+
if phase == :setup && criteria = options[:bootstrap_criteria]
|
97
|
+
Promiscuous::Publisher::Bootstrap.setup(:models => eval(criteria).to_a)
|
98
|
+
else
|
99
|
+
Promiscuous::Publisher::Bootstrap.__send__(phase)
|
100
|
+
end
|
88
101
|
end
|
89
102
|
|
90
103
|
def subscribe
|
91
104
|
@worker = Promiscuous::Subscriber::Worker.new
|
92
105
|
@worker.start
|
93
|
-
|
106
|
+
Promiscuous::Config.subscriber_threads.tap do |threads|
|
107
|
+
print_status "Replicating [#{threads} thread#{'s' if threads > 1}]..."
|
108
|
+
end
|
94
109
|
sleep 0.2 until !@worker
|
95
110
|
end
|
96
111
|
|
@@ -115,17 +130,18 @@ class Promiscuous::CLI
|
|
115
130
|
|
116
131
|
opts.separator ""
|
117
132
|
opts.separator "Actions:"
|
118
|
-
opts.separator " promiscuous publish \"Model1.where(:updated_at.gt => 1.day.ago)\" Model2 Model3..."
|
133
|
+
opts.separator " promiscuous publish \"Model1.where(:updated_at.gt => 1.day.ago)\" [Model2 Model3...]"
|
134
|
+
opts.separator " promiscuous publisher_recovery"
|
119
135
|
opts.separator " promiscuous subscribe"
|
136
|
+
opts.separator " promiscuous bootstrap phase"
|
120
137
|
opts.separator " promiscuous mocks"
|
121
138
|
opts.separator " promiscuous record logfile"
|
122
139
|
opts.separator " promiscuous replay logfile"
|
123
|
-
opts.separator " promiscuous publisher_recovery"
|
124
140
|
opts.separator ""
|
125
141
|
opts.separator "Options:"
|
126
142
|
|
127
|
-
opts.on "-
|
128
|
-
Promiscuous::Config.
|
143
|
+
opts.on "-d", "--no-deps", "Skip dependency tracking (subscribe only option)" do
|
144
|
+
Promiscuous::Config.no_deps = true
|
129
145
|
end
|
130
146
|
|
131
147
|
opts.on "-l", "--require FILE", "File to require to load your app. Don't worry about it with rails" do |file|
|
@@ -149,9 +165,26 @@ class Promiscuous::CLI
|
|
149
165
|
Promiscuous::Config.stats_interval = duration.to_f
|
150
166
|
end
|
151
167
|
|
152
|
-
opts.on
|
153
|
-
|
154
|
-
|
168
|
+
opts.on "-b", "--bootstrap [pass1|pass2]", "Run subscriber in bootstrap mode" do |mode|
|
169
|
+
mode = mode.to_sym
|
170
|
+
raise "Subscriber bootstrap must be run in pass1 or pass2 mode" unless [:pass1, :pass2].include?(mode)
|
171
|
+
Promiscuous::Config.bootstrap = mode.to_sym
|
172
|
+
end
|
173
|
+
|
174
|
+
opts.on "-t", "--threads [NUM]", "Number of subscriber worker threads to run. Defaults to 10." do |threads|
|
175
|
+
Promiscuous::Config.subscriber_threads = threads.to_i
|
176
|
+
end
|
177
|
+
|
178
|
+
opts.on "-c", "--criteria FILE", "Criteria to bootstrap a subset of data" do |criteria|
|
179
|
+
options[:bootstrap_criteria] = criteria
|
180
|
+
end
|
181
|
+
|
182
|
+
opts.on "-D", "--daemonize", "Daemonize process" do
|
183
|
+
options[:daemonize] = true
|
184
|
+
end
|
185
|
+
|
186
|
+
opts.on "-P", "--pid-file [pid_file]", "Set a pid-file" do |pid_file|
|
187
|
+
options[:pid_file] = pid_file
|
155
188
|
end
|
156
189
|
|
157
190
|
opts.on("-V", "--version", "Show version") do
|
@@ -167,12 +200,15 @@ class Promiscuous::CLI
|
|
167
200
|
options[:action] = args.shift.try(:to_sym)
|
168
201
|
options[:criterias] = args
|
169
202
|
options[:log_file] = args.first
|
203
|
+
options[:publisher_bootstrap] = args.first
|
170
204
|
|
171
205
|
case options[:action]
|
172
|
-
when :publish
|
173
|
-
when :subscribe
|
174
|
-
when :
|
175
|
-
when :
|
206
|
+
when :publish then raise "Please specify one or more criterias" unless options[:criterias].present?
|
207
|
+
when :subscribe then raise "Why are you specifying a criteria?" if options[:criterias].present?
|
208
|
+
when :bootstrap then raise "You must specify one of [setup|start|finalize]" unless options[:criterias].present?
|
209
|
+
when :record then raise "Please specify a log file to record" unless options[:log_file].present?
|
210
|
+
when :replay then raise "Please specify a log file to replay" unless options[:log_file].present?
|
211
|
+
when :publisher_bootstrap then raise "Please specify 'on' or 'off'" unless options[:publisher_bootstrap].present?
|
176
212
|
when :publisher_recovery
|
177
213
|
when :mocks
|
178
214
|
else puts parser; exit 1
|
@@ -203,28 +239,33 @@ class Promiscuous::CLI
|
|
203
239
|
|
204
240
|
def boot
|
205
241
|
self.options = parse_args(ARGV)
|
242
|
+
daemonize if options[:daemonize]
|
243
|
+
write_pid if options[:pid_file]
|
206
244
|
load_app
|
207
|
-
show_bareback_warnings
|
208
245
|
run
|
209
246
|
end
|
210
247
|
|
248
|
+
def daemonize
|
249
|
+
Process.daemon(true)
|
250
|
+
end
|
251
|
+
|
252
|
+
def write_pid
|
253
|
+
File.open(options[:pid_file], 'w') do |f|
|
254
|
+
f.puts Process.pid
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
211
258
|
def run
|
212
259
|
trap_signals
|
213
260
|
case options[:action]
|
214
261
|
when :publish then publish
|
215
262
|
when :subscribe then subscribe
|
263
|
+
when :bootstrap then bootstrap
|
216
264
|
when :record then record
|
217
265
|
when :replay then replay
|
218
266
|
when :mocks then generate_mocks
|
219
|
-
when :publisher_recovery
|
220
|
-
|
221
|
-
end
|
222
|
-
|
223
|
-
def show_bareback_warnings
|
224
|
-
if Promiscuous::Config.bareback == true
|
225
|
-
print_status "WARNING: --- BAREBACK MODE ----"
|
226
|
-
print_status "WARNING: You are replicating without protection, you can get out of sync in no time"
|
227
|
-
print_status "WARNING: --- BAREBACK MODE ----"
|
267
|
+
when :publisher_recovery then publisher_recovery
|
268
|
+
when :publisher_bootstrap then set_publisher_bootstrap
|
228
269
|
end
|
229
270
|
end
|
230
271
|
|
data/lib/promiscuous/config.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module Promiscuous::Config
|
2
|
-
mattr_accessor :app, :
|
3
|
-
:
|
4
|
-
:
|
5
|
-
:
|
6
|
-
:socket_timeout, :
|
2
|
+
mattr_accessor :app, :bootstrap, :bootstrap_chunk_size, :backend, :amqp_url,
|
3
|
+
:publisher_amqp_url, :subscriber_amqp_url, :publisher_exchange,
|
4
|
+
:subscriber_exchanges, :queue_name, :queue_options, :redis_url,
|
5
|
+
:redis_urls, :redis_stats_url, :stats_interval,
|
6
|
+
:socket_timeout, :heartbeat, :no_deps, :hash_size, :recovery,
|
7
|
+
:prefetch, :recovery_timeout, :logger, :subscriber_threads,
|
8
|
+
:version_field, :error_notifier, :recovery_on_boot
|
7
9
|
|
8
10
|
def self.backend=(value)
|
9
11
|
@@backend = value
|
@@ -31,36 +33,45 @@ module Promiscuous::Config
|
|
31
33
|
def self._configure(&block)
|
32
34
|
block.call(self) if block
|
33
35
|
|
34
|
-
self.app
|
35
|
-
self.
|
36
|
-
self.
|
37
|
-
self.
|
38
|
-
|
39
|
-
self.
|
40
|
-
self.
|
41
|
-
self.
|
42
|
-
self.
|
43
|
-
self.
|
44
|
-
self.
|
45
|
-
self.
|
46
|
-
self.
|
47
|
-
self.
|
48
|
-
self.
|
49
|
-
self.
|
50
|
-
self.
|
51
|
-
self.
|
36
|
+
self.app ||= Rails.application.class.parent_name.underscore rescue nil if defined?(Rails)
|
37
|
+
self.bootstrap ||= false
|
38
|
+
self.bootstrap_chunk_size ||= 10000
|
39
|
+
self.backend ||= best_amqp_backend
|
40
|
+
self.amqp_url ||= 'amqp://guest:guest@localhost:5672'
|
41
|
+
self.publisher_amqp_url ||= self.amqp_url
|
42
|
+
self.subscriber_amqp_url ||= self.amqp_url
|
43
|
+
self.publisher_exchange ||= Promiscuous::AMQP::LIVE_EXCHANGE
|
44
|
+
self.subscriber_exchanges ||= [Promiscuous::AMQP::LIVE_EXCHANGE]
|
45
|
+
self.queue_name ||= "#{self.app}.promiscuous"
|
46
|
+
self.queue_options ||= {:durable => true, :arguments => {'x-ha-policy' => 'all'}}
|
47
|
+
self.redis_url ||= 'redis://localhost/'
|
48
|
+
self.redis_urls ||= [self.redis_url]
|
49
|
+
# TODO self.redis_slave_url ||= nil
|
50
|
+
self.redis_stats_url ||= self.redis_urls.first
|
51
|
+
self.stats_interval ||= 0
|
52
|
+
self.socket_timeout ||= 10
|
53
|
+
self.heartbeat ||= 60
|
54
|
+
self.no_deps ||= false
|
55
|
+
self.hash_size ||= 2**20 # one million keys ~ 200Mb.
|
56
|
+
self.recovery ||= false
|
57
|
+
self.prefetch ||= self.bootstrap ? 10000000 : 1000
|
58
|
+
self.recovery_timeout ||= 10
|
59
|
+
self.logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
|
60
|
+
self.subscriber_threads ||= 10
|
61
|
+
self.error_notifier ||= proc {}
|
62
|
+
self.version_field ||= '_v'
|
63
|
+
self.recovery_on_boot = true if self.recovery_on_boot.nil?
|
52
64
|
end
|
53
65
|
|
54
66
|
def self.configure(&block)
|
55
|
-
|
67
|
+
reconnect_if_connected do
|
68
|
+
self._configure(&block)
|
56
69
|
|
57
|
-
|
58
|
-
|
70
|
+
unless self.app
|
71
|
+
raise "Promiscuous.configure: please give a name to your app with \"config.app = 'your_app_name'\""
|
72
|
+
end
|
59
73
|
end
|
60
74
|
|
61
|
-
# amqp connection is done in when setting the backend
|
62
|
-
Promiscuous::Redis.connect
|
63
|
-
|
64
75
|
hook_fork
|
65
76
|
end
|
66
77
|
|
@@ -71,6 +82,8 @@ module Promiscuous::Config
|
|
71
82
|
alias_method :fork_without_promiscuous, :fork
|
72
83
|
|
73
84
|
def fork(&block)
|
85
|
+
return fork_without_promiscuous(&block) unless Promiscuous.should_be_connected?
|
86
|
+
|
74
87
|
Promiscuous.disconnect
|
75
88
|
pid = if block
|
76
89
|
fork_without_promiscuous do
|
@@ -97,4 +110,16 @@ module Promiscuous::Config
|
|
97
110
|
def self.configured?
|
98
111
|
self.app != nil
|
99
112
|
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def self.reconnect_if_connected(&block)
|
117
|
+
if Promiscuous.should_be_connected?
|
118
|
+
Promiscuous.disconnect
|
119
|
+
yield
|
120
|
+
Promiscuous.connect
|
121
|
+
else
|
122
|
+
yield
|
123
|
+
end
|
124
|
+
end
|
100
125
|
end
|
@@ -3,7 +3,7 @@ module Promiscuous::Convenience
|
|
3
3
|
|
4
4
|
def without_promiscuous
|
5
5
|
raise "No block given" unless block_given?
|
6
|
-
old_disabled, Promiscuous.disabled = Promiscuous.disabled
|
6
|
+
old_disabled, Promiscuous.disabled = Promiscuous.disabled?, true
|
7
7
|
yield
|
8
8
|
ensure
|
9
9
|
Promiscuous.disabled = old_disabled
|
@@ -1,9 +1,14 @@
|
|
1
1
|
require 'fnv'
|
2
2
|
|
3
3
|
class Promiscuous::Dependency
|
4
|
-
attr_accessor :internal_key, :version
|
4
|
+
attr_accessor :internal_key, :version, :type
|
5
5
|
|
6
6
|
def initialize(*args)
|
7
|
+
options = args.extract_options!
|
8
|
+
@type = options[:type]
|
9
|
+
@owner = options[:owner]
|
10
|
+
@dont_hash = options[:dont_hash]
|
11
|
+
|
7
12
|
@internal_key = args.join('/')
|
8
13
|
|
9
14
|
if @internal_key =~ /^[0-9]+$/
|
@@ -16,10 +21,24 @@ class Promiscuous::Dependency
|
|
16
21
|
# We hash dependencies to have a O(1) memory footprint in Redis.
|
17
22
|
# The hashing needs to be deterministic across instances in order to
|
18
23
|
# function properly.
|
19
|
-
@
|
20
|
-
@
|
24
|
+
@hash = @hash % Promiscuous::Config.hash_size.to_i
|
25
|
+
@internal_key = @hash unless @dont_hash
|
21
26
|
end
|
22
27
|
end
|
28
|
+
|
29
|
+
if @owner
|
30
|
+
@internal_key = "#{@owner}:#{@internal_key}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def read?
|
35
|
+
raise "Type not set" unless @type
|
36
|
+
@type == :read
|
37
|
+
end
|
38
|
+
|
39
|
+
def write?
|
40
|
+
raise "Type not set" unless @type
|
41
|
+
@type == :write
|
23
42
|
end
|
24
43
|
|
25
44
|
def key(role)
|
@@ -35,10 +54,10 @@ class Promiscuous::Dependency
|
|
35
54
|
@version ? [@internal_key, @version].join(':') : @internal_key
|
36
55
|
end
|
37
56
|
|
38
|
-
def self.parse(payload)
|
57
|
+
def self.parse(payload, options={})
|
39
58
|
case payload
|
40
|
-
when /^(.+):([0-9]+)$/ then new($1).tap { |d| d.version = $2.to_i }
|
41
|
-
when /^(.+)$/ then new($1)
|
59
|
+
when /^(.+):([0-9]+)$/ then new($1, options).tap { |d| d.version = $2.to_i }
|
60
|
+
when /^(.+)$/ then new($1, options)
|
42
61
|
end
|
43
62
|
end
|
44
63
|
|