promiscuous 0.50.4 → 0.51.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/promiscuous.rb +24 -0
- data/lib/promiscuous/amqp/ruby_amqp.rb +3 -1
- data/lib/promiscuous/cli.rb +14 -6
- data/lib/promiscuous/config.rb +7 -2
- data/lib/promiscuous/error.rb +1 -1
- data/lib/promiscuous/error/recover.rb +1 -0
- data/lib/promiscuous/subscriber/worker/message_synchronizer.rb +73 -1
- data/lib/promiscuous/version.rb +1 -1
- metadata +6 -5
data/lib/promiscuous.rb
CHANGED
@@ -46,5 +46,29 @@ module Promiscuous
|
|
46
46
|
end
|
47
47
|
end
|
48
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
|
+
|
55
|
+
def global_version
|
56
|
+
Promiscuous::Redis.get(global_key).to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
def global_version=(value)
|
60
|
+
Promiscuous::Redis.set(global_key, value)
|
61
|
+
Promiscuous::Redis.publish(global_key, value)
|
62
|
+
value
|
63
|
+
end
|
64
|
+
|
65
|
+
def global_version!
|
66
|
+
version = Promiscuous::Redis.incr(global_key)
|
67
|
+
Promiscuous::Redis.publish(global_key, version)
|
68
|
+
version
|
69
|
+
end
|
70
|
+
end
|
71
|
+
extend ConsoleHelpers
|
72
|
+
|
49
73
|
at_exit { self.disconnect rescue nil }
|
50
74
|
end
|
@@ -20,7 +20,9 @@ module Promiscuous::AMQP::RubyAMQP
|
|
20
20
|
end
|
21
21
|
|
22
22
|
connection = ::AMQP.connect(amqp_options)
|
23
|
-
self.channel = ::AMQP::Channel.new(connection,
|
23
|
+
self.channel = ::AMQP::Channel.new(connection,
|
24
|
+
:auto_recovery => true,
|
25
|
+
:prefetch => Promiscuous::Config.prefetch)
|
24
26
|
|
25
27
|
connection.on_tcp_connection_loss do |conn|
|
26
28
|
unless conn.reconnecting?
|
data/lib/promiscuous/cli.rb
CHANGED
@@ -51,13 +51,22 @@ class Promiscuous::CLI
|
|
51
51
|
opts.separator "Options:"
|
52
52
|
|
53
53
|
opts.on "-b", "--bareback", "Bareback mode aka no dependencies. Use with extreme caution" do
|
54
|
-
|
54
|
+
Promiscuous::Config.bareback = true
|
55
55
|
end
|
56
56
|
|
57
|
-
opts.on "-
|
57
|
+
opts.on "-l", "--require FILE", "File to require to load your app. Don't worry about it with rails" do |file|
|
58
58
|
options[:require] = file
|
59
59
|
end
|
60
60
|
|
61
|
+
opts.on "-r", "--recovery [TIMEOUT]", "Run in recovery mode. Defaults to 10 seconds before recovering" do |timeout|
|
62
|
+
Promiscuous::Config.recovery_timeout = (timeout || 10).to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
opts.on "-p", "--prefetch [NUM]", "Number of messages to prefetch" do |prefetch|
|
66
|
+
exit 1 if prefetch.to_i == 0
|
67
|
+
Promiscuous::Config.prefetch = prefetch.to_i
|
68
|
+
end
|
69
|
+
|
61
70
|
opts.on("-h", "--help", "Show this message") do
|
62
71
|
puts opts
|
63
72
|
exit
|
@@ -106,7 +115,7 @@ class Promiscuous::CLI
|
|
106
115
|
def boot
|
107
116
|
self.options = parse_args(ARGV)
|
108
117
|
load_app
|
109
|
-
|
118
|
+
show_bareback_warnings
|
110
119
|
run
|
111
120
|
end
|
112
121
|
|
@@ -117,9 +126,8 @@ class Promiscuous::CLI
|
|
117
126
|
end
|
118
127
|
end
|
119
128
|
|
120
|
-
def
|
121
|
-
if
|
122
|
-
Promiscuous::Config.bareback = true
|
129
|
+
def show_bareback_warnings
|
130
|
+
if Promiscuous::Config.bareback == true
|
123
131
|
print_status "WARNING: --- BAREBACK MODE ----"
|
124
132
|
print_status "WARNING: You are replicating without protection, you can get out of sync in no time"
|
125
133
|
print_status "WARNING: --- BAREBACK MODE ----"
|
data/lib/promiscuous/config.rb
CHANGED
@@ -1,20 +1,25 @@
|
|
1
1
|
module Promiscuous::Config
|
2
|
-
mattr_accessor :app, :logger, :error_notifier, :backend, :amqp_url,
|
2
|
+
mattr_accessor :app, :logger, :error_notifier, :backend, :amqp_url,
|
3
|
+
:redis_url, :queue_options, :heartbeat, :bareback,
|
4
|
+
:recovery_timeout, :prefetch
|
3
5
|
|
4
6
|
def self.backend=(value)
|
5
7
|
@@backend = value
|
6
8
|
Promiscuous::AMQP.backend = value unless value.nil?
|
7
9
|
end
|
8
10
|
|
9
|
-
def self.
|
11
|
+
def self.reset
|
10
12
|
class_variables.each { |var| class_variable_set(var, nil) }
|
13
|
+
end
|
11
14
|
|
15
|
+
def self.configure(&block)
|
12
16
|
block.call(self)
|
13
17
|
|
14
18
|
self.backend ||= defined?(EventMachine) && EventMachine.reactor_running? ? :rubyamqp : :bunny
|
15
19
|
self.logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
|
16
20
|
self.queue_options ||= {:durable => true, :arguments => {'x-ha-policy' => 'all'}}
|
17
21
|
self.heartbeat ||= 60
|
22
|
+
self.prefetch ||= 1000
|
18
23
|
|
19
24
|
Promiscuous.connect
|
20
25
|
end
|
data/lib/promiscuous/error.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
class Promiscuous::Error::Recover < RuntimeError; end
|
@@ -17,6 +17,7 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def connect
|
20
|
+
@queued_messages = 0
|
20
21
|
@subscriptions = {}
|
21
22
|
self.redis = Promiscuous::Redis.new_celluloid_connection
|
22
23
|
end
|
@@ -106,6 +107,8 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
|
|
106
107
|
# when calling worker.pump.start
|
107
108
|
return unless self.redis
|
108
109
|
|
110
|
+
bump_message_counter!
|
111
|
+
|
109
112
|
return process_message!(msg) unless msg.has_dependencies?
|
110
113
|
|
111
114
|
# The message synchronizer only takes care of happens before (>=) dependencies.
|
@@ -115,7 +118,76 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
|
|
115
118
|
end
|
116
119
|
end
|
117
120
|
|
121
|
+
def bump_message_counter!
|
122
|
+
@queued_messages += 1
|
123
|
+
maybe_recover
|
124
|
+
end
|
125
|
+
|
126
|
+
def maybe_recover
|
127
|
+
recovery_timeout = Promiscuous::Config.recovery_timeout
|
128
|
+
return unless recovery_timeout
|
129
|
+
|
130
|
+
reset_recovery_timer
|
131
|
+
if should_recover?
|
132
|
+
# We've reached the amount of messages the amqp queue is willing to give us.
|
133
|
+
# We also know that we are not processing messages (@queued_messages is
|
134
|
+
# decremented before we send the message to the runners).
|
135
|
+
|
136
|
+
Promiscuous.warn "[receive] Recovering in #{recovery_timeout} seconds..."
|
137
|
+
@recover_timer = after(recovery_timeout) { reset_recovery_timer; recover }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def reset_recovery_timer
|
142
|
+
@recover_timer.try(:reset)
|
143
|
+
@recover_timer = nil
|
144
|
+
end
|
145
|
+
|
146
|
+
def should_recover?
|
147
|
+
@queued_messages == Promiscuous::Config.prefetch
|
148
|
+
end
|
149
|
+
|
150
|
+
def recover
|
151
|
+
return unless should_recover?
|
152
|
+
|
153
|
+
global_key = Promiscuous::Redis.sub_key('global')
|
154
|
+
current_version = Promiscuous::Redis.get(global_key).to_i
|
155
|
+
|
156
|
+
global_callbacks = get_subscription(global_key).callbacks
|
157
|
+
expected_next_msg_version = global_callbacks.values.map(&:version).min
|
158
|
+
version_to_allow_progress = expected_next_msg_version - 1
|
159
|
+
|
160
|
+
num_messages_to_skip = version_to_allow_progress - current_version
|
161
|
+
|
162
|
+
if num_messages_to_skip > 0
|
163
|
+
recovery_msg = "Recovering. Moving current version from #{current_version} " +
|
164
|
+
"to #{version_to_allow_progress}. " +
|
165
|
+
"Skipping #{num_messages_to_skip} messages..."
|
166
|
+
else
|
167
|
+
recovery_msg = "Not recovering. current version is #{current_version}, " +
|
168
|
+
"while we just need #{version_to_allow_progress}. " +
|
169
|
+
"Offset is #{num_messages_to_skip} message."
|
170
|
+
end
|
171
|
+
|
172
|
+
e = Promiscuous::Error::Recover.new(recovery_msg)
|
173
|
+
if current_version > 0
|
174
|
+
Promiscuous.error "[receive] #{e}"
|
175
|
+
Promiscuous::Config.error_notifier.try(:call, e)
|
176
|
+
else
|
177
|
+
Promiscuous.info "[receive] #{e}"
|
178
|
+
# Initial sync, nothing to worry about
|
179
|
+
end
|
180
|
+
|
181
|
+
if num_messages_to_skip > 0
|
182
|
+
Promiscuous::Redis.set(global_key, version_to_allow_progress)
|
183
|
+
Promiscuous::Redis.publish(global_key, version_to_allow_progress)
|
184
|
+
end
|
185
|
+
ensure
|
186
|
+
reset_recovery_timer
|
187
|
+
end
|
188
|
+
|
118
189
|
def process_message!(msg)
|
190
|
+
@queued_messages -= 1
|
119
191
|
Celluloid::Actor[:runners].process!(msg)
|
120
192
|
end
|
121
193
|
|
@@ -136,7 +208,7 @@ class Promiscuous::Subscriber::Worker::MessageSynchronizer
|
|
136
208
|
end
|
137
209
|
|
138
210
|
class Subscription
|
139
|
-
attr_accessor :parent, :key
|
211
|
+
attr_accessor :parent, :key, :callbacks
|
140
212
|
|
141
213
|
def initialize(parent, key)
|
142
214
|
self.parent = parent
|
data/lib/promiscuous/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: promiscuous
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.51.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-02-
|
13
|
+
date: 2013-02-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -221,19 +221,20 @@ files:
|
|
221
221
|
- lib/promiscuous/error/subscriber.rb
|
222
222
|
- lib/promiscuous/error/publisher.rb
|
223
223
|
- lib/promiscuous/error/connection.rb
|
224
|
+
- lib/promiscuous/error/recover.rb
|
224
225
|
- lib/promiscuous/ephemeral.rb
|
225
226
|
- lib/promiscuous/autoload.rb
|
226
|
-
- lib/promiscuous/error.rb
|
227
227
|
- lib/promiscuous/loader.rb
|
228
228
|
- lib/promiscuous/railtie.rb
|
229
229
|
- lib/promiscuous/common.rb
|
230
230
|
- lib/promiscuous/publisher.rb
|
231
231
|
- lib/promiscuous/amqp.rb
|
232
232
|
- lib/promiscuous/subscriber.rb
|
233
|
-
- lib/promiscuous/config.rb
|
234
|
-
- lib/promiscuous/cli.rb
|
235
233
|
- lib/promiscuous/redis.rb
|
236
234
|
- lib/promiscuous/observer.rb
|
235
|
+
- lib/promiscuous/cli.rb
|
236
|
+
- lib/promiscuous/error.rb
|
237
|
+
- lib/promiscuous/config.rb
|
237
238
|
- lib/promiscuous/version.rb
|
238
239
|
- lib/promiscuous.rb
|
239
240
|
- bin/promiscuous
|