promiscuous 0.92.0 → 0.100.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/promiscuous.rb +2 -5
- data/lib/promiscuous/amqp.rb +1 -2
- data/lib/promiscuous/cli.rb +3 -43
- data/lib/promiscuous/config.rb +5 -7
- data/lib/promiscuous/error/dependency.rb +1 -3
- data/lib/promiscuous/publisher/context.rb +1 -1
- data/lib/promiscuous/publisher/context/base.rb +3 -34
- data/lib/promiscuous/publisher/model/base.rb +5 -25
- data/lib/promiscuous/publisher/model/mock.rb +5 -7
- data/lib/promiscuous/publisher/operation/active_record.rb +4 -69
- data/lib/promiscuous/publisher/operation/atomic.rb +1 -3
- data/lib/promiscuous/publisher/operation/base.rb +33 -123
- data/lib/promiscuous/publisher/operation/mongoid.rb +0 -67
- data/lib/promiscuous/publisher/operation/non_persistent.rb +0 -1
- data/lib/promiscuous/publisher/operation/transaction.rb +1 -3
- data/lib/promiscuous/railtie.rb +0 -31
- data/lib/promiscuous/subscriber.rb +1 -1
- data/lib/promiscuous/subscriber/{worker/message.rb → message.rb} +12 -40
- data/lib/promiscuous/subscriber/model/active_record.rb +1 -1
- data/lib/promiscuous/subscriber/model/base.rb +4 -4
- data/lib/promiscuous/subscriber/model/mongoid.rb +3 -3
- data/lib/promiscuous/subscriber/operation.rb +74 -3
- data/lib/promiscuous/subscriber/unit_of_work.rb +110 -0
- data/lib/promiscuous/subscriber/worker.rb +3 -7
- data/lib/promiscuous/subscriber/worker/eventual_destroyer.rb +2 -6
- data/lib/promiscuous/subscriber/worker/pump.rb +2 -11
- data/lib/promiscuous/version.rb +1 -1
- metadata +18 -36
- data/lib/promiscuous/error/missing_context.rb +0 -29
- data/lib/promiscuous/publisher/bootstrap.rb +0 -27
- data/lib/promiscuous/publisher/bootstrap/connection.rb +0 -25
- data/lib/promiscuous/publisher/bootstrap/data.rb +0 -127
- data/lib/promiscuous/publisher/bootstrap/mode.rb +0 -19
- data/lib/promiscuous/publisher/bootstrap/status.rb +0 -40
- data/lib/promiscuous/publisher/bootstrap/version.rb +0 -46
- data/lib/promiscuous/publisher/context/middleware.rb +0 -94
- data/lib/promiscuous/resque.rb +0 -12
- data/lib/promiscuous/sidekiq.rb +0 -15
- data/lib/promiscuous/subscriber/message_processor.rb +0 -4
- data/lib/promiscuous/subscriber/message_processor/base.rb +0 -54
- data/lib/promiscuous/subscriber/message_processor/bootstrap.rb +0 -17
- data/lib/promiscuous/subscriber/message_processor/regular.rb +0 -238
- data/lib/promiscuous/subscriber/operation/base.rb +0 -66
- data/lib/promiscuous/subscriber/operation/bootstrap.rb +0 -60
- data/lib/promiscuous/subscriber/operation/regular.rb +0 -19
- data/lib/promiscuous/subscriber/worker/message_synchronizer.rb +0 -333
@@ -1,94 +0,0 @@
|
|
1
|
-
class Promiscuous::Publisher::Context::Middleware < Promiscuous::Publisher::Context::Base
|
2
|
-
module Controller
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
def process_action(*args)
|
6
|
-
full_name = "#{self.class.controller_path}/#{self.action_name}"
|
7
|
-
current_user = self.current_user if self.respond_to?(:current_user)
|
8
|
-
Promiscuous::Publisher::Context::Middleware.with_context(full_name, :current_user => current_user) { super }
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.with_context(*args, &block)
|
13
|
-
# XXX We turn off the disabled flag when entering a middleware.
|
14
|
-
# It has priority because it's much simpler to use for testing.
|
15
|
-
old_disabled, Promiscuous.disabled = Promiscuous.disabled?, false
|
16
|
-
super
|
17
|
-
rescue Exception => e
|
18
|
-
$promiscuous_last_exception = e if e.is_a? Promiscuous::Error::Base
|
19
|
-
pretty_print_exception(e) unless e.is_a? ActionView::MissingTemplate
|
20
|
-
raise e
|
21
|
-
ensure
|
22
|
-
Promiscuous.disabled = old_disabled
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.pretty_print_exception(e)
|
26
|
-
return if $promiscuous_pretty_print_exception_once == :disable || ENV['RAILS_ENV'] == 'production'
|
27
|
-
return if e.is_a?(SystemExit)
|
28
|
-
|
29
|
-
e = e.original_exception if defined?(ActionView::Template::Error) && e.is_a?(ActionView::Template::Error)
|
30
|
-
|
31
|
-
STDERR.puts
|
32
|
-
STDERR.puts "\e[0;#{36}m/---[ Exception: #{e.class} ]#{'-'*[0, 84 - e.class.name.size].max}\e[0m"
|
33
|
-
STDERR.puts "\e[0;#{36}m|"
|
34
|
-
|
35
|
-
highlight_indent = false
|
36
|
-
msg = e.to_s.split("\n").map do |line|
|
37
|
-
highlight_indent = true if line =~ /The problem comes from the following/ ||
|
38
|
-
line =~ /Promiscuous is tracking this read/
|
39
|
-
line = "\e[1;#{31}m#{line}\e[0;#{31}m" if highlight_indent && line =~ /^ /
|
40
|
-
"\e[0;#{36}m| \e[0;#{31}m#{line}\e[0m"
|
41
|
-
end
|
42
|
-
|
43
|
-
STDERR.puts msg.join("\n")
|
44
|
-
STDERR.puts "\e[0;#{36}m|"
|
45
|
-
STDERR.puts "\e[0;#{36}m+---[ Backtrace ]--------------------------------------------------------------------------------------\e[0m"
|
46
|
-
STDERR.puts "\e[0;#{36}m|"
|
47
|
-
|
48
|
-
expand = ENV['TRACE'].to_i > 1
|
49
|
-
bt = e.backtrace.map do |line|
|
50
|
-
line = case line
|
51
|
-
when /(rspec-core|instrumentation)/
|
52
|
-
"\e[1;30m#{line}\e[0m" if expand
|
53
|
-
when /#{Rails.root}\/app\/controllers/
|
54
|
-
"\e[1;35m#{line}\e[0m"
|
55
|
-
when /#{Rails.root}\/app\/models/
|
56
|
-
"\e[1;33m#{line}\e[0m"
|
57
|
-
when /#{Rails.root}\/lib/
|
58
|
-
"\e[1;34m#{line}\e[0m"
|
59
|
-
when /(mongoid|active_record).*`(count|distinct|each|first|last)'$/
|
60
|
-
"\e[1;32m#{line}\e[0m"
|
61
|
-
when /(mongoid|active_record).*`(create|insert|save|update|modify|remove|remove_all)'$/
|
62
|
-
"\e[1;31m#{line}\e[0m"
|
63
|
-
when /#{Rails.root}/
|
64
|
-
if line =~ /\/support\//
|
65
|
-
"\e[1;30m#{line}\e[0m" if expand
|
66
|
-
else
|
67
|
-
"\e[1;36m#{line}\e[0m"
|
68
|
-
end
|
69
|
-
else
|
70
|
-
"\e[1;30m#{line}\e[0m" if expand
|
71
|
-
end
|
72
|
-
"\e[0;#{36}m| #{line}" if line
|
73
|
-
end
|
74
|
-
.compact
|
75
|
-
.join("\n")
|
76
|
-
STDERR.puts bt
|
77
|
-
STDERR.puts "\e[0;#{36}m|"
|
78
|
-
|
79
|
-
if $cucumber_extra
|
80
|
-
STDERR.puts "\e[0;#{36}m+---[ Cucumber ]--------------------------------------------------------------------------------------\e[0m"
|
81
|
-
STDERR.puts "\e[0;#{36}m|"
|
82
|
-
$cucumber_extra.each_with_index do |line, i|
|
83
|
-
line = line.gsub(/([^:]*: )(.*)$/, "\\1\e[1;36m\\2")
|
84
|
-
STDERR.puts "\e[0;#{36}m| \e[0;36m#{line}\e[0m"
|
85
|
-
STDERR.puts "\e[0;#{36}m|" if i.zero?
|
86
|
-
end
|
87
|
-
STDERR.puts "\e[0;#{36}m|"
|
88
|
-
end
|
89
|
-
|
90
|
-
STDERR.puts "\e[0;#{36}m\\------------------------------------------------------------------------------------------------------\e[0m"
|
91
|
-
STDERR.puts
|
92
|
-
$promiscuous_pretty_print_exception_once = :disable if $promiscuous_pretty_print_exception_once
|
93
|
-
end
|
94
|
-
end
|
data/lib/promiscuous/resque.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'resque/job'
|
2
|
-
|
3
|
-
class Resque::Job
|
4
|
-
alias_method :perform_without_promiscuous, :perform
|
5
|
-
|
6
|
-
def perform
|
7
|
-
name = "resque/#{payload_class.name.underscore}"
|
8
|
-
Promiscuous::Middleware.with_context(name) do
|
9
|
-
perform_without_promiscuous
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
data/lib/promiscuous/sidekiq.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
module Sidekiq
|
2
|
-
class Promiscuous
|
3
|
-
def call(worker_class, item, queue)
|
4
|
-
::Promiscuous::Middleware.with_context "sidekiq/#{item['queue']}/#{worker_class.class.to_s.underscore}" do
|
5
|
-
yield
|
6
|
-
end
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
Sidekiq.configure_server do |config|
|
12
|
-
config.server_middleware do |chain|
|
13
|
-
chain.add Sidekiq::Promiscuous
|
14
|
-
end
|
15
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
class Promiscuous::Subscriber::MessageProcessor::Base
|
2
|
-
attr_accessor :message
|
3
|
-
|
4
|
-
def initialize(message)
|
5
|
-
self.message = message
|
6
|
-
end
|
7
|
-
|
8
|
-
def operations
|
9
|
-
message.parsed_payload['operations'].map { |op| operation_class.new(op) }
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.process(*args)
|
13
|
-
raise "Same thread is processing a message?" if self.current
|
14
|
-
|
15
|
-
begin
|
16
|
-
self.current = new(*args)
|
17
|
-
self.current.process_message
|
18
|
-
ensure
|
19
|
-
self.current = nil
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.current
|
24
|
-
Thread.current[:promiscuous_message_processor]
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.current=(value)
|
28
|
-
Thread.current[:promiscuous_message_processor] = value
|
29
|
-
end
|
30
|
-
|
31
|
-
def process_message
|
32
|
-
begin
|
33
|
-
on_message
|
34
|
-
rescue Exception => e
|
35
|
-
@fail_count ||= 0; @fail_count += 1
|
36
|
-
|
37
|
-
if @fail_count <= Promiscuous::Config.max_retries
|
38
|
-
Promiscuous.warn("[receive] #{e.message} #{@fail_count.ordinalize} retry: #{@message}")
|
39
|
-
sleep @fail_count ** 2
|
40
|
-
process_message
|
41
|
-
else
|
42
|
-
raise e
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def on_message
|
48
|
-
raise "Must be implemented"
|
49
|
-
end
|
50
|
-
|
51
|
-
def operation_class
|
52
|
-
raise "Must be implemented"
|
53
|
-
end
|
54
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
class Promiscuous::Subscriber::MessageProcessor::Bootstrap < Promiscuous::Subscriber::MessageProcessor::Base
|
2
|
-
def on_message
|
3
|
-
if bootstrap_operation?
|
4
|
-
operations.each(&:execute)
|
5
|
-
else
|
6
|
-
# Postpone message by doing nothing
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def bootstrap_operation?
|
11
|
-
operations.first.try(:operation) =~ /bootstrap/
|
12
|
-
end
|
13
|
-
|
14
|
-
def operation_class
|
15
|
-
Promiscuous::Subscriber::Operation::Bootstrap
|
16
|
-
end
|
17
|
-
end
|
@@ -1,238 +0,0 @@
|
|
1
|
-
class Promiscuous::Subscriber::MessageProcessor::Regular < Promiscuous::Subscriber::MessageProcessor::Base
|
2
|
-
delegate :write_dependencies, :read_dependencies, :dependencies, :to => :message
|
3
|
-
|
4
|
-
def nodes_with_deps
|
5
|
-
@nodes_with_deps ||= dependencies.group_by(&:redis_node)
|
6
|
-
end
|
7
|
-
|
8
|
-
def instance_dep
|
9
|
-
@instance_dep ||= write_dependencies.first
|
10
|
-
end
|
11
|
-
|
12
|
-
def master_node
|
13
|
-
@master_node ||= instance_dep.redis_node
|
14
|
-
end
|
15
|
-
|
16
|
-
def master_node_with_deps
|
17
|
-
@master_node_with_deps ||= nodes_with_deps.select { |node| node == master_node }.first
|
18
|
-
end
|
19
|
-
|
20
|
-
def secondary_nodes_with_deps
|
21
|
-
@secondary_nodes_with_deps ||= nodes_with_deps.reject { |node| node == master_node }.to_a
|
22
|
-
end
|
23
|
-
|
24
|
-
def recovery_key
|
25
|
-
# We use a recovery_key unique to the operation to avoid any trouble of
|
26
|
-
# touching another operation.
|
27
|
-
@recovery_key ||= instance_dep.key(:sub).join(instance_dep.version).to_s
|
28
|
-
end
|
29
|
-
|
30
|
-
def get_current_instance_version
|
31
|
-
master_node.get(instance_dep.key(:sub).join('rw').to_s).to_i
|
32
|
-
end
|
33
|
-
|
34
|
-
# XXX TODO Code is not tolerant to losing a lock.
|
35
|
-
|
36
|
-
def update_dependencies_non_atomic_bootstrap(node, r_deps, w_deps, options={})
|
37
|
-
argv = []
|
38
|
-
argv << MultiJson.dump([r_deps.map { |dep| dep.key(:sub) },
|
39
|
-
w_deps.map { |dep| dep.key(:sub) },
|
40
|
-
w_deps.map { |dep| dep.version }])
|
41
|
-
argv << recovery_key if options[:with_recovery]
|
42
|
-
|
43
|
-
@@update_script_bootstrap ||= Promiscuous::Redis::Script.new <<-SCRIPT
|
44
|
-
local _args = cjson.decode(ARGV[1])
|
45
|
-
local read_deps = _args[1]
|
46
|
-
local write_deps = _args[2]
|
47
|
-
local write_versions = _args[3]
|
48
|
-
local recovery_key = ARGV[2]
|
49
|
-
|
50
|
-
if recovery_key and redis.call('exists', recovery_key) == 1 then
|
51
|
-
return
|
52
|
-
end
|
53
|
-
|
54
|
-
for i, _key in ipairs(read_deps) do
|
55
|
-
local key = _key .. ':rw'
|
56
|
-
local v = redis.call('incr', key)
|
57
|
-
redis.call('publish', key, v)
|
58
|
-
end
|
59
|
-
|
60
|
-
for i, _key in ipairs(write_deps) do
|
61
|
-
local key = _key .. ':rw'
|
62
|
-
local v = write_versions[i]
|
63
|
-
local current_version = tonumber(redis.call('get', key)) or 0
|
64
|
-
if current_version < v then
|
65
|
-
redis.call('set', key, v)
|
66
|
-
redis.call('publish', key, v)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
if recovery_key then
|
71
|
-
redis.call('set', recovery_key, 'done')
|
72
|
-
end
|
73
|
-
SCRIPT
|
74
|
-
|
75
|
-
@@update_script_bootstrap.eval(node, :argv => argv)
|
76
|
-
end
|
77
|
-
|
78
|
-
def update_dependencies_fast(node, r_deps, w_deps, options={})
|
79
|
-
keys = (r_deps + w_deps).map { |dep| dep.key(:sub) }
|
80
|
-
argv = options[:with_recovery] ? [recovery_key] : []
|
81
|
-
|
82
|
-
@@update_script_fast ||= Promiscuous::Redis::Script.new <<-SCRIPT
|
83
|
-
local deps = KEYS
|
84
|
-
local recovery_key = ARGV[1]
|
85
|
-
|
86
|
-
if recovery_key and redis.call('exists', recovery_key) == 1 then
|
87
|
-
return
|
88
|
-
end
|
89
|
-
|
90
|
-
for i, _key in ipairs(deps) do
|
91
|
-
local key = _key .. ':rw'
|
92
|
-
local v = redis.call('incr', key)
|
93
|
-
redis.call('publish', key, v)
|
94
|
-
end
|
95
|
-
|
96
|
-
if recovery_key then
|
97
|
-
redis.call('set', recovery_key, 'done')
|
98
|
-
end
|
99
|
-
SCRIPT
|
100
|
-
|
101
|
-
@@update_script_fast.eval(node, :keys => keys, :argv => argv)
|
102
|
-
end
|
103
|
-
|
104
|
-
def update_dependencies_on_node(node_with_deps, options={})
|
105
|
-
# Read and write dependencies are not handled the same way:
|
106
|
-
# * Read dependencies are just incremented (which allow parallelization).
|
107
|
-
# * Write dependencies are set to be max(current_version, received_version).
|
108
|
-
# This allow the version bootstrapping process to be non-atomic.
|
109
|
-
# Publishers upgrade their reads dependencies to write dependencies
|
110
|
-
# during bootstrapping to permit the mechanism to function properly.
|
111
|
-
|
112
|
-
# TODO Evaluate the performance hit of this heavy mechanism, and see if it's
|
113
|
-
# worth optimizing it for the non-bootstrap case.
|
114
|
-
|
115
|
-
node = node_with_deps[0]
|
116
|
-
r_deps = node_with_deps[1].select(&:read?)
|
117
|
-
w_deps = node_with_deps[1].select(&:write?)
|
118
|
-
|
119
|
-
if message.was_during_bootstrap?
|
120
|
-
raise "Message should not have any read deps" unless r_deps.empty?
|
121
|
-
update_dependencies_non_atomic_bootstrap(node, r_deps, w_deps, options)
|
122
|
-
else
|
123
|
-
update_dependencies_fast(node, r_deps, w_deps, options)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def update_dependencies_master(options={})
|
128
|
-
update_dependencies_on_node(master_node_with_deps, options)
|
129
|
-
end
|
130
|
-
|
131
|
-
def update_dependencies_secondaries(options={})
|
132
|
-
secondary_nodes_with_deps.each do |node_with_deps|
|
133
|
-
update_dependencies_on_node(node_with_deps, options.merge(:with_recovery => true))
|
134
|
-
after_secondary_update_hook
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def after_secondary_update_hook
|
139
|
-
# Hook only used for testing
|
140
|
-
end
|
141
|
-
|
142
|
-
def cleanup_dependency_secondaries
|
143
|
-
secondary_nodes_with_deps.each do |node, deps|
|
144
|
-
node.del(recovery_key)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def update_dependencies(options={})
|
149
|
-
# With multi nodes, we have to do a 2pc for the lock recovery mechanism:
|
150
|
-
# 1) We do the secondaries first, with a recovery token.
|
151
|
-
# 2) Then we do the master.
|
152
|
-
# 3) Then we cleanup the recovery token on secondaries.
|
153
|
-
update_dependencies_secondaries(options)
|
154
|
-
update_dependencies_master(options)
|
155
|
-
cleanup_dependency_secondaries
|
156
|
-
end
|
157
|
-
|
158
|
-
def duplicate_message?
|
159
|
-
unless instance_dep.version >= get_current_instance_version + 1
|
160
|
-
# We happen to get a duplicate message, or we are recovering a dead
|
161
|
-
# worker. During regular operations, we just need to cleanup the 2pc (from
|
162
|
-
# the dead worker), and ack the message to rabbit.
|
163
|
-
# TODO Test cleanup
|
164
|
-
cleanup_dependency_secondaries
|
165
|
-
|
166
|
-
# But, if the message was generated during bootstrap, we don't really know
|
167
|
-
# if the other dependencies are up to date (because of the non-atomic
|
168
|
-
# bootstrapping process), so we do the max() trick (see in update_dependencies_on_node).
|
169
|
-
# Since such messages can come arbitrary late, we never really know if we
|
170
|
-
# can assume regular operations, thus we always assume that such message
|
171
|
-
# can originate from the bootstrapping period.
|
172
|
-
# Note that we are not in the happy path. Such duplicates messages are
|
173
|
-
# seldom: either (1) the publisher recovered a payload that didn't need
|
174
|
-
# recovery, or (2) a subscriber worker died after # update_dependencies_master,
|
175
|
-
# but before the message acking).
|
176
|
-
update_dependencies if message.was_during_bootstrap?
|
177
|
-
|
178
|
-
true
|
179
|
-
else
|
180
|
-
false
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
LOCK_OPTIONS = { :timeout => 1.5.minute, # after 1.5 minute, we give up
|
185
|
-
:sleep => 0.1, # polling every 100ms.
|
186
|
-
:expire => 1.minute } # after one minute, we are considered dead
|
187
|
-
|
188
|
-
def check_duplicate_and_update_dependencies
|
189
|
-
if duplicate_message?
|
190
|
-
Promiscuous.debug "[receive] Skipping message (already processed) #{message}"
|
191
|
-
return
|
192
|
-
end
|
193
|
-
|
194
|
-
yield
|
195
|
-
|
196
|
-
update_dependencies
|
197
|
-
end
|
198
|
-
|
199
|
-
def with_instance_locked(&block)
|
200
|
-
return yield unless message.has_dependencies?
|
201
|
-
|
202
|
-
lock_options = LOCK_OPTIONS.merge(:node => master_node)
|
203
|
-
mutex = Promiscuous::Redis::Mutex.new(instance_dep.key(:sub).to_s, lock_options)
|
204
|
-
|
205
|
-
unless mutex.lock
|
206
|
-
raise Promiscuous::Error::LockUnavailable.new(mutex.key)
|
207
|
-
end
|
208
|
-
|
209
|
-
begin
|
210
|
-
yield
|
211
|
-
ensure
|
212
|
-
unless mutex.unlock
|
213
|
-
# TODO Be safe in case we have a duplicate message and lost the lock on it
|
214
|
-
raise "The subscriber lost the lock during its operation. It means that someone else\n"+
|
215
|
-
"received a duplicate message, and we got screwed.\n"
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def execute_operations
|
221
|
-
self.operations.each(&:execute)
|
222
|
-
end
|
223
|
-
|
224
|
-
def on_message
|
225
|
-
with_instance_locked do
|
226
|
-
if Promiscuous::Config.consistency == :causal && message.has_dependencies?
|
227
|
-
self.check_duplicate_and_update_dependencies { execute_operations }
|
228
|
-
else
|
229
|
-
execute_operations
|
230
|
-
end
|
231
|
-
end
|
232
|
-
message.ack
|
233
|
-
end
|
234
|
-
|
235
|
-
def operation_class
|
236
|
-
Promiscuous::Subscriber::Operation::Regular
|
237
|
-
end
|
238
|
-
end
|
@@ -1,66 +0,0 @@
|
|
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
|
-
if model.__promiscuous_duplicate_key_exception?(e)
|
36
|
-
options[:on_already_created] ||= proc { warn "ignoring already created record" }
|
37
|
-
options[:on_already_created].call
|
38
|
-
else
|
39
|
-
raise e
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def update(should_create_on_failure=true)
|
44
|
-
model.__promiscuous_fetch_existing(id).tap do |instance|
|
45
|
-
if instance.__promiscuous_eventual_consistency_update(self)
|
46
|
-
instance.__promiscuous_update(self)
|
47
|
-
instance.save!
|
48
|
-
end
|
49
|
-
end
|
50
|
-
rescue model.__promiscuous_missing_record_exception
|
51
|
-
warn "upserting"
|
52
|
-
create :on_already_created => proc { update(false) if should_create_on_failure }
|
53
|
-
end
|
54
|
-
|
55
|
-
def destroy
|
56
|
-
if Promiscuous::Config.consistency == :eventual
|
57
|
-
Promiscuous::Subscriber::Worker::EventualDestroyer.postpone_destroy(model, id)
|
58
|
-
end
|
59
|
-
|
60
|
-
model.__promiscuous_fetch_existing(id).tap do |instance|
|
61
|
-
instance.destroy
|
62
|
-
end
|
63
|
-
rescue model.__promiscuous_missing_record_exception
|
64
|
-
warn "ignoring missing record"
|
65
|
-
end
|
66
|
-
end
|