promiscuous 0.90.0 → 0.91.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,68 @@
|
|
1
|
+
class Promiscuous::Subscriber::Operation::Base
|
2
|
+
attr_accessor :model, :id, :operation, :attributes
|
3
|
+
delegate :message, :to => :message_processor
|
4
|
+
|
5
|
+
def initialize(payload)
|
6
|
+
if payload.is_a?(Hash)
|
7
|
+
self.id = payload['id']
|
8
|
+
self.operation = payload['operation'].try(:to_sym)
|
9
|
+
self.attributes = payload['attributes']
|
10
|
+
self.model = self.get_subscribed_model(payload) if payload['types']
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_subscribed_model(payload)
|
15
|
+
[message.app, '*'].each do |app|
|
16
|
+
app_mapping = Promiscuous::Subscriber::Model.mapping[app] || {}
|
17
|
+
payload['types'].to_a.each do |ancestor|
|
18
|
+
model = app_mapping[ancestor]
|
19
|
+
return model if model
|
20
|
+
end
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def warn(msg)
|
26
|
+
Promiscuous.warn "[receive] #{msg} #{message.payload}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def create(options={})
|
30
|
+
model.__promiscuous_fetch_new(id).tap do |instance|
|
31
|
+
instance.__promiscuous_update(self)
|
32
|
+
instance.save!
|
33
|
+
end
|
34
|
+
rescue Exception => e
|
35
|
+
# TODO Abstract the duplicated index error message
|
36
|
+
dup_index_error = true if defined?(Mongoid) && e.message =~ /E11000/
|
37
|
+
# # TODO Ensure that it's on the pk
|
38
|
+
dup_index_error = true if defined?(ActiveRecord) && e.is_a?(ActiveRecord::RecordNotUnique)
|
39
|
+
|
40
|
+
if dup_index_error
|
41
|
+
if options[:upsert]
|
42
|
+
update
|
43
|
+
else
|
44
|
+
warn "ignoring already created record"
|
45
|
+
end
|
46
|
+
else
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def update
|
52
|
+
model.__promiscuous_fetch_existing(id).tap do |instance|
|
53
|
+
instance.__promiscuous_update(self)
|
54
|
+
instance.save!
|
55
|
+
end
|
56
|
+
rescue model.__promiscuous_missing_record_exception
|
57
|
+
warn "upserting #{message.payload}"
|
58
|
+
create
|
59
|
+
end
|
60
|
+
|
61
|
+
def destroy
|
62
|
+
model.__promiscuous_fetch_existing(id).tap do |instance|
|
63
|
+
instance.destroy
|
64
|
+
end
|
65
|
+
rescue model.__promiscuous_missing_record_exception
|
66
|
+
warn "ignoring missing record"
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Promiscuous::Subscriber::Operation::Bootstrap < Promiscuous::Subscriber::Operation::Base
|
2
|
+
# TODO Here's what's left to do:
|
3
|
+
# - Automatic switching from pass1, pass2, live
|
4
|
+
# - Unbinding the bootstrap exchange when going live, and reset prefetch
|
5
|
+
# during the version bootstrap phase.
|
6
|
+
# - CLI interface and progress bars
|
7
|
+
|
8
|
+
def bootstrap_versions
|
9
|
+
operations = message.parsed_payload['operations']
|
10
|
+
|
11
|
+
operations.map { |op| op['keys'] }.flatten.map { |k| Promiscuous::Dependency.parse(k, :owner => message.app) }.group_by(&:redis_node).each do |node, deps|
|
12
|
+
node.mset(deps.map { |dep| [dep.key(:sub).join('rw').to_s, dep.version] }.flatten)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def bootstrap_data
|
17
|
+
create(:upsert => true)
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_bootstrap_operation(wanted_operation, options={})
|
21
|
+
if operation == wanted_operation
|
22
|
+
yield
|
23
|
+
options[:always_postpone] ? message.postpone : message.ack
|
24
|
+
else
|
25
|
+
message.postpone
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute
|
30
|
+
case Promiscuous::Config.bootstrap
|
31
|
+
when :pass1
|
32
|
+
# The first thing to do is to receive and save an non atomic snapshot of
|
33
|
+
# the publisher's versions.
|
34
|
+
on_bootstrap_operation(:bootstrap_versions) { bootstrap_versions }
|
35
|
+
|
36
|
+
when :pass2
|
37
|
+
# Then we move on to save the raw data, but skipping the message if we get
|
38
|
+
# a mismatch on the version.
|
39
|
+
on_bootstrap_operation(:bootstrap_data) { bootstrap_data }
|
40
|
+
|
41
|
+
when :pass3
|
42
|
+
# Finally, we create the rows that we've skipped, we postpone them to make
|
43
|
+
# our lives easier. We'll detect the message as duplicates when re-processed.
|
44
|
+
# on_bootstrap_operation(:update, :always_postpone => true) { bootstrap_missing_data if model }
|
45
|
+
# TODO unbind the bootstrap exchange
|
46
|
+
else
|
47
|
+
raise "Invalid operation received: #{operation}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def message_processor
|
52
|
+
@message_processor ||= Promiscuous::Subscriber::MessageProcessor::Bootstrap.current
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Promiscuous::Subscriber::Operation::Regular < Promiscuous::Subscriber::Operation::Base
|
2
|
+
def execute
|
3
|
+
case operation
|
4
|
+
when :create then create if model
|
5
|
+
when :update then update if model
|
6
|
+
when :destroy then destroy if model
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def message_processor
|
11
|
+
@message_processor ||= Promiscuous::Subscriber::MessageProcessor::Regular.current
|
12
|
+
end
|
13
|
+
end
|
@@ -1,167 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
delegate :model, :id, :operation, :message, :to => :payload
|
5
|
-
|
6
|
-
def initialize(payload)
|
7
|
-
self.payload = payload
|
8
|
-
end
|
9
|
-
|
10
|
-
# XXX TODO Code is not tolerent to losing a lock.
|
11
|
-
|
12
|
-
def update_dependencies_single(node_with_deps)
|
13
|
-
master_node = node_with_deps[0]
|
14
|
-
deps = node_with_deps[1]
|
15
|
-
|
16
|
-
@@update_script ||= Promiscuous::Redis::Script.new <<-SCRIPT
|
17
|
-
for i, key in ipairs(KEYS) do
|
18
|
-
local v = redis.call('incr', key .. ':rw')
|
19
|
-
redis.call('publish', key .. ':rw', v)
|
20
|
-
end
|
21
|
-
SCRIPT
|
22
|
-
keys = deps.map { |dep| dep.key(:sub).to_s }
|
23
|
-
@@update_script.eval(master_node, :keys => keys)
|
24
|
-
end
|
25
|
-
|
26
|
-
def update_dependencies_multi(nodes_with_deps, options={})
|
27
|
-
# With multi nodes, we have to do a 2pc for the lock recovery mechanism:
|
28
|
-
# 1) We do the secondaries first, with a recovery payload.
|
29
|
-
# 2) Then we do the master.
|
30
|
-
# 3) Then we cleanup the secondaries.
|
31
|
-
# We use a recovery_key unique to the operation to avoid any trouble of
|
32
|
-
# touching another operation.
|
33
|
-
secondary_nodes_with_deps = nodes_with_deps[1..-1]
|
34
|
-
recovery_key = @instance_dep.key(:sub).join(@instance_dep.version).to_s
|
35
|
-
|
36
|
-
secondary_nodes_with_deps.each do |node, deps|
|
37
|
-
@@update_script_secondary ||= Promiscuous::Redis::Script.new <<-SCRIPT
|
38
|
-
local recovery_key = ARGV[1]
|
39
|
-
|
40
|
-
if redis.call('get', recovery_key) == 'done' then
|
41
|
-
return
|
42
|
-
end
|
43
|
-
|
44
|
-
for i, key in ipairs(KEYS) do
|
45
|
-
local v = redis.call('incr', key .. ':rw')
|
46
|
-
redis.call('publish', key .. ':rw', v)
|
47
|
-
end
|
48
|
-
|
49
|
-
redis.call('set', recovery_key, 'done')
|
50
|
-
SCRIPT
|
51
|
-
keys = deps.map { |dep| dep.key(:sub).to_s }
|
52
|
-
@@update_script_secondary.eval(node, :keys => keys, :argv => [recovery_key])
|
53
|
-
after_secondary_update_hook
|
54
|
-
end
|
55
|
-
|
56
|
-
update_dependencies_single(nodes_with_deps.first) unless options[:skip_master]
|
57
|
-
|
58
|
-
secondary_nodes_with_deps.each do |node, deps|
|
59
|
-
node.del(recovery_key)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def after_secondary_update_hook; end # for tests
|
64
|
-
|
65
|
-
def update_dependencies
|
66
|
-
nodes_with_deps.size == 1 ? update_dependencies_single(nodes_with_deps.first) :
|
67
|
-
update_dependencies_multi(nodes_with_deps)
|
68
|
-
end
|
69
|
-
|
70
|
-
def verify_dependencies
|
71
|
-
key = @instance_dep.key(:sub).join('rw').to_s
|
72
|
-
|
73
|
-
if @instance_dep.redis_node.get(key).to_i + 1 > @instance_dep.version
|
74
|
-
if nodes_with_deps.size != 1
|
75
|
-
update_dependencies_multi(nodes_with_deps, :skip_master => true)
|
76
|
-
end
|
77
|
-
|
78
|
-
raise Promiscuous::Error::AlreadyProcessed
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def nodes_with_deps
|
83
|
-
@nodes_with_deps ||= dependencies.group_by(&:redis_node).to_a
|
84
|
-
end
|
85
|
-
|
86
|
-
def dependencies
|
87
|
-
@dependencies ||= message.dependencies[:write] + message.dependencies[:read]
|
88
|
-
end
|
89
|
-
|
90
|
-
LOCK_OPTIONS = { :timeout => 1.5.minute, # after 1.5 minute , we give up
|
91
|
-
:sleep => 0.1, # polling every 100ms.
|
92
|
-
:expire => 1.minute } # after one minute, we are considered dead
|
93
|
-
|
94
|
-
def with_instance_dependencies
|
95
|
-
return yield unless message && message.has_dependencies?
|
96
|
-
|
97
|
-
@instance_dep = message.dependencies[:write].first
|
98
|
-
lock_options = LOCK_OPTIONS.merge(:node => @instance_dep.redis_node)
|
99
|
-
mutex = Promiscuous::Redis::Mutex.new(@instance_dep.key(:sub).to_s, lock_options)
|
100
|
-
|
101
|
-
unless mutex.lock
|
102
|
-
raise Promiscuous::Error::LockUnavailable.new(mutex.key)
|
103
|
-
end
|
104
|
-
|
105
|
-
begin
|
106
|
-
verify_dependencies
|
107
|
-
result = yield
|
108
|
-
update_dependencies
|
109
|
-
result
|
110
|
-
ensure
|
111
|
-
unless mutex.unlock
|
112
|
-
# TODO Be safe in case we have a duplicate message and lost the lock on it
|
113
|
-
raise "The subscriber lost the lock during its operation. It means that someone else\n"+
|
114
|
-
"received a duplicate message, and we got screwed.\n"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def create
|
120
|
-
model.__promiscuous_fetch_new(id).tap do |instance|
|
121
|
-
instance.__promiscuous_update(payload)
|
122
|
-
instance.save!
|
123
|
-
end
|
124
|
-
rescue Exception => e
|
125
|
-
# TODO Abstract the duplicated index error message
|
126
|
-
if e.message =~ /E11000 duplicate key error index: .*\.\$_id_ +dup key/
|
127
|
-
Promiscuous.warn "[receive] ignoring already created record #{message.payload}"
|
128
|
-
else
|
129
|
-
raise e
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def update
|
134
|
-
model.__promiscuous_fetch_existing(id).tap do |instance|
|
135
|
-
instance.__promiscuous_update(payload)
|
136
|
-
instance.save!
|
137
|
-
end
|
138
|
-
rescue model.__promiscuous_missing_record_exception
|
139
|
-
Promiscuous.warn "[receive] upserting #{message.payload}"
|
140
|
-
create
|
141
|
-
end
|
142
|
-
|
143
|
-
def destroy
|
144
|
-
model.__promiscuous_fetch_existing(id).tap do |instance|
|
145
|
-
instance.destroy
|
146
|
-
end
|
147
|
-
rescue model.__promiscuous_missing_record_exception
|
148
|
-
Promiscuous.warn "[receive] ignoring missing record #{message.payload}"
|
149
|
-
end
|
150
|
-
|
151
|
-
def operation
|
152
|
-
# We must process messages with versions to stay in sync even if we
|
153
|
-
# don't have a subscriber.
|
154
|
-
payload.model.nil? ? :dummy : payload.operation
|
155
|
-
end
|
156
|
-
|
157
|
-
def commit
|
158
|
-
with_instance_dependencies do
|
159
|
-
case operation
|
160
|
-
when :create then create
|
161
|
-
when :update then update
|
162
|
-
when :destroy then destroy
|
163
|
-
when :dummy then nil
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
1
|
+
module Promiscuous::Subscriber::Operation
|
2
|
+
extend Promiscuous::Autoload
|
3
|
+
autoload :Base, :Regular, :Bootstrap
|
167
4
|
end
|
@@ -11,8 +11,12 @@ class Promiscuous::Subscriber::Worker::Message
|
|
11
11
|
@parsed_payload ||= MultiJson.load(payload)
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
parsed_payload['
|
14
|
+
def context
|
15
|
+
parsed_payload['context']
|
16
|
+
end
|
17
|
+
|
18
|
+
def app
|
19
|
+
parsed_payload['app']
|
16
20
|
end
|
17
21
|
|
18
22
|
def timestamp
|
@@ -20,52 +24,70 @@ class Promiscuous::Subscriber::Worker::Message
|
|
20
24
|
end
|
21
25
|
|
22
26
|
def dependencies
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
@dependencies ||= begin
|
28
|
+
dependencies = parsed_payload['dependencies'] || {}
|
29
|
+
deps = dependencies['read'].to_a.map { |dep| Promiscuous::Dependency.parse(dep, :type => :read, :owner => app) } +
|
30
|
+
dependencies['write'].to_a.map { |dep| Promiscuous::Dependency.parse(dep, :type => :write, :owner => app) }
|
31
|
+
|
32
|
+
deps
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
|
-
def
|
33
|
-
|
36
|
+
def write_dependencies
|
37
|
+
@write_dependencies ||= dependencies.select(&:write?)
|
38
|
+
end
|
34
39
|
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
def read_dependencies
|
41
|
+
@read_dependencies ||= dependencies.select(&:read?)
|
42
|
+
end
|
43
|
+
|
44
|
+
def happens_before_dependencies
|
45
|
+
@happens_before_dependencies ||= begin
|
46
|
+
deps = []
|
47
|
+
deps += read_dependencies
|
48
|
+
deps += write_dependencies.map { |dep| dep.dup.tap { |d| d.version -= 1 } }
|
38
49
|
|
39
|
-
|
40
|
-
|
50
|
+
# We return the most difficult condition to satisfy first
|
51
|
+
deps.uniq.reverse
|
52
|
+
end
|
41
53
|
end
|
42
54
|
|
43
55
|
def has_dependencies?
|
44
|
-
return false if Promiscuous::Config.
|
45
|
-
dependencies
|
56
|
+
return false if Promiscuous::Config.no_deps
|
57
|
+
dependencies.present?
|
46
58
|
end
|
47
59
|
|
48
60
|
def to_s
|
49
|
-
"#{
|
61
|
+
"#{app}/#{context} -> #{happens_before_dependencies.join(', ')}"
|
50
62
|
end
|
51
63
|
|
52
64
|
def ack
|
53
65
|
time = Time.now
|
54
|
-
|
55
|
-
@
|
66
|
+
Promiscuous.debug "[receive] #{payload}"
|
67
|
+
@metadata.try(:ack)
|
68
|
+
@root_worker.stats.notify_processed_message(self, time) if @root_worker
|
56
69
|
rescue Exception => e
|
57
70
|
# We don't care if we fail, the message will be redelivered at some point
|
58
71
|
Promiscuous.warn "[receive] Some exception happened, but it's okay: #{e}\n#{e.backtrace.join("\n")}"
|
59
|
-
Promiscuous::Config.error_notifier.
|
72
|
+
Promiscuous::Config.error_notifier.call(e)
|
73
|
+
end
|
74
|
+
|
75
|
+
def postpone
|
76
|
+
# Only used during bootstrapping
|
77
|
+
@metadata.postpone
|
78
|
+
rescue Exception => e
|
79
|
+
# We don't care if we fail
|
80
|
+
Promiscuous.warn "[receive] (postpone) Some exception happened, but it's okay: #{e}\n#{e.backtrace.join("\n")}"
|
81
|
+
Promiscuous::Config.error_notifier.call(e)
|
60
82
|
end
|
61
83
|
|
62
84
|
def unit_of_work(type, &block)
|
63
85
|
# type is used by the new relic agent, by monkey patching.
|
64
86
|
# middleware?
|
65
87
|
if defined?(Mongoid)
|
66
|
-
Mongoid.unit_of_work {
|
88
|
+
Mongoid.unit_of_work { yield_and_catch_already_processsed(&block) }
|
67
89
|
else
|
68
|
-
|
90
|
+
yield_and_catch_already_processsed(&block)
|
69
91
|
end
|
70
92
|
ensure
|
71
93
|
if defined?(ActiveRecord)
|
@@ -73,20 +95,24 @@ class Promiscuous::Subscriber::Worker::Message
|
|
73
95
|
end
|
74
96
|
end
|
75
97
|
|
76
|
-
def
|
77
|
-
Promiscuous.
|
78
|
-
unit_of_work(endpoint) do
|
79
|
-
payload = Promiscuous::Subscriber::Payload.new(parsed_payload, self)
|
80
|
-
Promiscuous::Subscriber::Operation.new(payload).commit
|
81
|
-
end
|
82
|
-
ack
|
98
|
+
def yield_and_catch_already_processsed
|
99
|
+
Promiscuous.context { yield }
|
83
100
|
rescue Promiscuous::Error::AlreadyProcessed => orig_e
|
84
101
|
e = Promiscuous::Error::Subscriber.new(orig_e, :payload => payload)
|
85
|
-
Promiscuous.debug "[receive] #{e}"
|
86
|
-
|
102
|
+
Promiscuous.debug "[receive] #{payload} #{e}\n#{e.backtrace.join("\n")}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def process
|
106
|
+
unit_of_work(context) do
|
107
|
+
if Promiscuous::Config.bootstrap
|
108
|
+
Promiscuous::Subscriber::MessageProcessor::Bootstrap.process(self)
|
109
|
+
else
|
110
|
+
Promiscuous::Subscriber::MessageProcessor::Regular.process(self)
|
111
|
+
end
|
112
|
+
end
|
87
113
|
rescue Exception => orig_e
|
88
114
|
e = Promiscuous::Error::Subscriber.new(orig_e, :payload => payload)
|
89
|
-
Promiscuous.warn "[receive] #{
|
90
|
-
Promiscuous::Config.error_notifier.
|
115
|
+
Promiscuous.warn "[receive] #{payload} #{e}\n#{e.backtrace.join("\n")}"
|
116
|
+
Promiscuous::Config.error_notifier.call(e)
|
91
117
|
end
|
92
118
|
end
|