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/config.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module Promiscuous::Config
|
2
2
|
mattr_accessor :app, :logger, :error_notifier, :backend, :amqp_url,
|
3
|
-
:redis_url, :
|
4
|
-
:
|
3
|
+
:redis_url, :redis_urls, :redis_slave_url, :redis_stats_url,
|
4
|
+
:stats_interval, :queue_options, :heartbeat, :bareback,
|
5
|
+
:hash_size, :recovery, :prefetch, :recovery_timeout,
|
6
|
+
:socket_timeout, :subscriber_threads
|
5
7
|
|
6
8
|
def self.backend=(value)
|
7
9
|
@@backend = value
|
@@ -13,17 +15,44 @@ module Promiscuous::Config
|
|
13
15
|
class_variables.each { |var| class_variable_set(var, nil) }
|
14
16
|
end
|
15
17
|
|
16
|
-
def self.
|
17
|
-
|
18
|
+
def self.best_amqp_backend
|
19
|
+
if RUBY_PLATFORM == 'java'
|
20
|
+
begin
|
21
|
+
require 'hot_bunnies'
|
22
|
+
:hot_bunnies
|
23
|
+
rescue LoadError
|
24
|
+
:bunny
|
25
|
+
end
|
26
|
+
else
|
27
|
+
:bunny
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self._configure(&block)
|
32
|
+
block.call(self) if block
|
18
33
|
|
19
|
-
self.app
|
20
|
-
self.amqp_url
|
21
|
-
self.redis_url
|
22
|
-
self.
|
23
|
-
self.
|
24
|
-
self.
|
25
|
-
self.
|
26
|
-
self.
|
34
|
+
self.app ||= Rails.application.class.parent_name.underscore rescue nil if defined?(Rails)
|
35
|
+
self.amqp_url ||= 'amqp://guest:guest@localhost:5672'
|
36
|
+
self.redis_url ||= 'redis://localhost/'
|
37
|
+
self.redis_urls ||= [self.redis_url]
|
38
|
+
#self.redis_slave_url ||= nil
|
39
|
+
self.redis_stats_url ||= self.redis_urls.first
|
40
|
+
self.stats_interval ||= 0
|
41
|
+
self.socket_timeout ||= 10
|
42
|
+
self.backend ||= best_amqp_backend
|
43
|
+
self.queue_options ||= {:durable => true, :arguments => {'x-ha-policy' => 'all'}}
|
44
|
+
self.heartbeat ||= 60
|
45
|
+
self.bareback ||= false
|
46
|
+
self.hash_size ||= 2**20 # one million keys ~ 200Mb.
|
47
|
+
self.recovery ||= false
|
48
|
+
self.prefetch ||= 1000
|
49
|
+
self.recovery_timeout ||= 10
|
50
|
+
self.logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
|
51
|
+
self.subscriber_threads ||= 10
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.configure(&block)
|
55
|
+
self._configure(&block)
|
27
56
|
|
28
57
|
unless self.app
|
29
58
|
raise "Promiscuous.configure: please give a name to your app with \"config.app = 'your_app_name'\""
|
@@ -31,6 +60,38 @@ module Promiscuous::Config
|
|
31
60
|
|
32
61
|
# amqp connection is done in when setting the backend
|
33
62
|
Promiscuous::Redis.connect
|
63
|
+
|
64
|
+
hook_fork
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.hook_fork
|
68
|
+
return if @fork_hooked
|
69
|
+
|
70
|
+
Kernel.module_eval do
|
71
|
+
alias_method :fork_without_promiscuous, :fork
|
72
|
+
|
73
|
+
def fork(&block)
|
74
|
+
Promiscuous.disconnect
|
75
|
+
pid = if block
|
76
|
+
fork_without_promiscuous do
|
77
|
+
Promiscuous.connect
|
78
|
+
block.call
|
79
|
+
end
|
80
|
+
else
|
81
|
+
fork_without_promiscuous
|
82
|
+
end
|
83
|
+
Promiscuous.connect
|
84
|
+
pid
|
85
|
+
rescue Exception => e
|
86
|
+
puts e
|
87
|
+
puts e.backtrace.join("\n")
|
88
|
+
raise e
|
89
|
+
end
|
90
|
+
|
91
|
+
module_function :fork
|
92
|
+
end
|
93
|
+
|
94
|
+
@fork_hooked = true
|
34
95
|
end
|
35
96
|
|
36
97
|
def self.configured?
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Promiscuous::Convenience
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def without_promiscuous
|
5
|
+
raise "No block given" unless block_given?
|
6
|
+
old_disabled, Promiscuous.disabled = Promiscuous.disabled, true
|
7
|
+
yield
|
8
|
+
ensure
|
9
|
+
Promiscuous.disabled = old_disabled
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class ::Array
|
14
|
+
def without_promiscuous
|
15
|
+
raise "What is this block?" if block_given?
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fnv'
|
2
|
+
|
3
|
+
class Promiscuous::Dependency
|
4
|
+
attr_accessor :internal_key, :version
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@internal_key = args.join('/')
|
8
|
+
|
9
|
+
if @internal_key =~ /^[0-9]+$/
|
10
|
+
@internal_key = @internal_key.to_i
|
11
|
+
@hash = @internal_key
|
12
|
+
else
|
13
|
+
@hash = FNV.new.fnv1a_32(@internal_key)
|
14
|
+
|
15
|
+
if Promiscuous::Config.hash_size.to_i > 0
|
16
|
+
# We hash dependencies to have a O(1) memory footprint in Redis.
|
17
|
+
# The hashing needs to be deterministic across instances in order to
|
18
|
+
# function properly.
|
19
|
+
@internal_key = @hash % Promiscuous::Config.hash_size.to_i
|
20
|
+
@hash = @internal_key
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def key(role)
|
26
|
+
Promiscuous::Key.new(role).join(@internal_key)
|
27
|
+
end
|
28
|
+
|
29
|
+
def redis_node(distributed_redis=nil)
|
30
|
+
distributed_redis ||= Promiscuous::Redis.master
|
31
|
+
distributed_redis.nodes[@hash % distributed_redis.nodes.size]
|
32
|
+
end
|
33
|
+
|
34
|
+
def as_json(options={})
|
35
|
+
@version ? [@internal_key, @version].join(':') : @internal_key
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parse(payload)
|
39
|
+
case payload
|
40
|
+
when /^(.+):([0-9]+)$/ then new($1).tap { |d| d.version = $2.to_i }
|
41
|
+
when /^(.+)$/ then new($1)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
as_json.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
# We need the eql? method to function properly (we use ==, uniq, ...) in operation
|
50
|
+
# XXX The version is not taken in account.
|
51
|
+
def eql?(other)
|
52
|
+
self.internal_key == other.internal_key
|
53
|
+
end
|
54
|
+
alias == eql?
|
55
|
+
|
56
|
+
def hash
|
57
|
+
self.internal_key.hash
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Promiscuous::DSL
|
2
|
+
def define(&block)
|
3
|
+
instance_eval(&block)
|
4
|
+
end
|
5
|
+
|
6
|
+
def publish(model, options={}, &block)
|
7
|
+
Definition.new(:publish, model, options).instance_eval(&block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def subscribe(model, options={}, &block)
|
11
|
+
Definition.new(:subscribe, model, options).instance_eval(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Definition
|
15
|
+
def initialize(mode, model, options)
|
16
|
+
@mode = mode
|
17
|
+
@model = model
|
18
|
+
@options = options
|
19
|
+
@model_class = @model.to_s.singularize.classify.constantize
|
20
|
+
|
21
|
+
promiscuous_include = mode == :publish ? Promiscuous::Publisher : Promiscuous::Subscriber
|
22
|
+
@model_class.class_eval { include promiscuous_include }
|
23
|
+
end
|
24
|
+
|
25
|
+
def attributes(*fields)
|
26
|
+
options = fields.extract_options!
|
27
|
+
@model_class.__send__(@mode, *fields, @options.merge(options))
|
28
|
+
end
|
29
|
+
|
30
|
+
def track_dependencies_of(field)
|
31
|
+
@model_class.track_dependencies_of(field)
|
32
|
+
end
|
33
|
+
|
34
|
+
alias attribute attributes
|
35
|
+
end
|
36
|
+
end
|
data/lib/promiscuous/error.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Promiscuous::Error
|
2
2
|
extend Promiscuous::Autoload
|
3
|
-
autoload :Connection, :Publisher, :Subscriber, :
|
3
|
+
autoload :Base, :Connection, :Publisher, :Subscriber, :Recovery,
|
4
|
+
:Dependency, :MissingContext, :AlreadyProcessed,
|
5
|
+
:LockUnavailable, :LostLock
|
4
6
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class Promiscuous::Error::Base < RuntimeError; end
|
@@ -1,17 +1,19 @@
|
|
1
|
-
class Promiscuous::Error::Connection <
|
1
|
+
class Promiscuous::Error::Connection < Promiscuous::Error::Base
|
2
2
|
attr_accessor :service, :url
|
3
3
|
|
4
4
|
def initialize(options={})
|
5
5
|
super(nil)
|
6
6
|
self.service = options[:service]
|
7
|
-
self.url =
|
7
|
+
self.url = case service
|
8
|
+
when :zookeeper then "zookeeper://#{Promiscuous::Config.zookeeper_hosts}"
|
9
|
+
when :redis then Promiscuous::Config.redis_url
|
10
|
+
when :amqp then Promiscuous::Config.amqp_url
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
14
|
def message
|
11
15
|
"Lost connection with #{url}"
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
message
|
16
|
-
end
|
18
|
+
alias to_s message
|
17
19
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
class Promiscuous::Error::Dependency < Promiscuous::Error::Base
|
2
|
+
attr_accessor :dependency_solutions, :operation, :context
|
3
|
+
|
4
|
+
def initialize(options={})
|
5
|
+
self.operation = options[:operation]
|
6
|
+
self.context = Promiscuous::Publisher::Context.current
|
7
|
+
end
|
8
|
+
|
9
|
+
# TODO Convert all that with Erb
|
10
|
+
|
11
|
+
def message
|
12
|
+
msg = nil
|
13
|
+
case operation.operation
|
14
|
+
when :read
|
15
|
+
msg = "Promiscuous doesn't have any tracked dependencies to perform this multi read operation.\n" +
|
16
|
+
"This is what you can do:\n\n" +
|
17
|
+
" 1. Bypass Promiscuous\n\n" +
|
18
|
+
" If you don't use the result of this operation in your following writes,\n" +
|
19
|
+
" you can wrap your read query in a 'without_promiscuous { }' block.\n" +
|
20
|
+
" This is the preferred solution when you are sure that the read doesn't\n" +
|
21
|
+
" influence the value of a published attribute.\n\n" +
|
22
|
+
" Rule of thumb: Predicates (methods ending with ?) are often suitable for this use case.\n\n"
|
23
|
+
cnt = 2
|
24
|
+
if operation.operation_ext != :count
|
25
|
+
msg += " #{cnt}. Synchronize on individual instances\n\n" +
|
26
|
+
" If the collection you are iterating through is small (<10), it becomes intersting\n" +
|
27
|
+
" to track instances through their ids instead of the query selector. Example:\n\n" +
|
28
|
+
" criteria.without_promiscuous.each do |doc|\n" +
|
29
|
+
" next if doc.should_do_something?\n" +
|
30
|
+
" doc.reload # tell promiscuous to track the instance\n" +
|
31
|
+
" doc.do_something!\n" +
|
32
|
+
" end\n\n"
|
33
|
+
cnt += 1
|
34
|
+
end
|
35
|
+
if operation.selector_keys.present?
|
36
|
+
msg += " #{cnt}. Track New Dependencies\n\n" +
|
37
|
+
" Add #{operation.selector_keys.count == 1 ? "the following line" : "one of the following lines"} " +
|
38
|
+
"in the #{operation.instance.class} model:\n\n" +
|
39
|
+
" class #{operation.instance.class}\n" +
|
40
|
+
operation.selector_keys.map { |field| " track_dependencies_of :#{field}" }.join("\n") + "\n" +
|
41
|
+
" end\n\n" +
|
42
|
+
(operation.selector_keys.count > 1 ?
|
43
|
+
" The more specific field, the better. Promiscuous works better when working with small subsets\n" +
|
44
|
+
" For example, tracking something like 'member_id' is a fairly safe choice.\n\n" : "") +
|
45
|
+
" Note that dependency tracking slows down your writes. It can be seen as the analogous\n" +
|
46
|
+
" of an index on a regular database.\n" +
|
47
|
+
" You may find more information about the implications in the Promiscuous wiki (TODO:link).\n\n"
|
48
|
+
end
|
49
|
+
when :update
|
50
|
+
msg = "Promiscuous cannot track dependencies of a multi update operation.\n" +
|
51
|
+
"This is what you can do:\n\n" +
|
52
|
+
" 1. Instead of doing a multi updates, update each instance separately\n\n" +
|
53
|
+
" 2. Do not assign has_many associations directly, but use the << operator instead.\n\n"
|
54
|
+
when :destroy
|
55
|
+
msg = "Promiscuous cannot track dependencies of a multi delete operation.\n" +
|
56
|
+
"This is what you can do:\n\n" +
|
57
|
+
" 1. Instead of doing a multi delete, delete each instance separatly.\n\n" +
|
58
|
+
" 2. Use destroy_all instead of destroy_all.\n\n" +
|
59
|
+
" 3. Declare your has_many relationships with :dependent => :destroy instead of :delete.\n\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
msg += "#{"-" * 100}\n\n"
|
63
|
+
|
64
|
+
msg += "Promiscuous cannot allow the following "
|
65
|
+
case operation.operation_ext || operation.operation
|
66
|
+
when :count then msg += 'count'
|
67
|
+
when :mapreduce then msg += 'mapreduce'
|
68
|
+
when :read then msg += 'each loop'
|
69
|
+
when :update then msg += 'multi update'
|
70
|
+
when :destroy then msg += 'multi destroy'
|
71
|
+
end
|
72
|
+
msg += " in the '#{context.name}' context:\n\n"
|
73
|
+
msg += " #{self.class.explain_operation(self.operation)}"
|
74
|
+
msg += "\n\nProTip: Try again with TRACE=2 in the shell or ENV['TRACE']='2' in the console.\n" unless ENV['TRACE']
|
75
|
+
msg
|
76
|
+
rescue Exception => e
|
77
|
+
"#{e.to_s}\n#{e.backtrace.join("\n")}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.explain_operation(operation, limit=100)
|
81
|
+
instance = operation.instance
|
82
|
+
selector = instance ? get_selector(instance.attributes, limit) : ""
|
83
|
+
class_name = instance ? instance.class : "Unknown"
|
84
|
+
|
85
|
+
if operation.operation == :create
|
86
|
+
"#{instance.class}.create(#{selector})"
|
87
|
+
else
|
88
|
+
case operation.operation_ext || operation.operation
|
89
|
+
when :count then verb = 'count'
|
90
|
+
when :mapreduce then verb = 'mapreduce(...)'
|
91
|
+
when :read then verb = operation.multi? ? 'each { ... }' : 'first'
|
92
|
+
when :update then verb = operation.multi? ? 'update_all' : 'update'
|
93
|
+
when :destroy then verb = operation.multi? ? 'delete_all' : 'delete'
|
94
|
+
end
|
95
|
+
msg = "#{class_name}#{selector.present? ? ".where(#{selector})" : ""}.#{verb}"
|
96
|
+
if operation.operation == :update && operation.respond_to?(:change) && operation.change
|
97
|
+
msg += "(#{get_selector(operation.change, limit)})"
|
98
|
+
end
|
99
|
+
msg
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.get_selector(attributes, limit=100)
|
104
|
+
attributes = attributes['$set'] if attributes.count == 1 && attributes['$set']
|
105
|
+
selector = attributes.map { |k,v| ":#{k} => #{v}" }.join(", ")
|
106
|
+
selector = "#{selector[0...(limit-3)]}..." if selector.size > limit
|
107
|
+
selector
|
108
|
+
end
|
109
|
+
|
110
|
+
alias to_s message
|
111
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Promiscuous::Error::LockUnavailable < Promiscuous::Error::Base
|
2
|
+
def initialize(lock)
|
3
|
+
@lock = lock
|
4
|
+
end
|
5
|
+
|
6
|
+
def message
|
7
|
+
"The lock is not available on #{@lock}\n" +
|
8
|
+
"If an app instance died, the lock will expire in less than a minute."
|
9
|
+
end
|
10
|
+
|
11
|
+
alias to_s message
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Promiscuous::Error::LostLock < Promiscuous::Error::Base
|
2
|
+
def initialize(lock)
|
3
|
+
@lock = lock
|
4
|
+
end
|
5
|
+
|
6
|
+
def message
|
7
|
+
"The following lock was lost during the operation and will be recovered if not already done:\n" +
|
8
|
+
" #{@lock}"
|
9
|
+
end
|
10
|
+
|
11
|
+
alias to_s message
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Promiscuous::Error::MissingContext < Promiscuous::Error::Base
|
2
|
+
def message
|
3
|
+
require 'erb'
|
4
|
+
ERB.new(<<-ERB.gsub(/^\s+<%/, '<%').gsub(/^ {6}/, ''), nil, '-').result(binding)
|
5
|
+
Promiscuous needs to execute all your read/write queries in a context for publishing.
|
6
|
+
This is what you can do:
|
7
|
+
1. Wrap your operations in a Promiscuous context yourself (jobs, etc.):
|
8
|
+
|
9
|
+
Promiscuous::Middleware.with_context 'jobs/name' do
|
10
|
+
# Code including all your read and write queries
|
11
|
+
end
|
12
|
+
|
13
|
+
2. Disable Promiscuous completely (only for testing):
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.around do |example|
|
17
|
+
without_promiscuous { example.run }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Note that opening a context will reactivate promiscuous temporarily
|
22
|
+
even if it was disabled.
|
23
|
+
|
24
|
+
3. You are in render() in the Rails controller, and you should not write.
|
25
|
+
ERB
|
26
|
+
end
|
27
|
+
|
28
|
+
alias to_s message
|
29
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
class Promiscuous::Error::Publisher <
|
2
|
-
attr_accessor :inner, :instance, :payload
|
1
|
+
class Promiscuous::Error::Publisher < Promiscuous::Error::Base
|
2
|
+
attr_accessor :inner, :instance, :payload
|
3
3
|
|
4
4
|
def initialize(inner, options={})
|
5
5
|
super(nil)
|
@@ -8,24 +8,14 @@ class Promiscuous::Error::Publisher < RuntimeError
|
|
8
8
|
self.inner = inner
|
9
9
|
self.instance = options[:instance]
|
10
10
|
self.payload = options[:payload]
|
11
|
-
self.out_of_sync = options[:out_of_sync]
|
12
11
|
end
|
13
12
|
|
14
13
|
def message
|
15
14
|
msg = "#{inner.class}: #{inner.message}"
|
16
|
-
if instance
|
17
|
-
|
18
|
-
msg = "FATAL (out of sync) #{msg}" if out_of_sync
|
19
|
-
end
|
20
|
-
|
21
|
-
if payload
|
22
|
-
msg = "#{msg} payload: #{payload}"
|
23
|
-
end
|
24
|
-
|
15
|
+
msg = "#{msg} while publishing #{instance.inspect}" if instance
|
16
|
+
msg = "#{msg} payload: #{payload}" if payload
|
25
17
|
msg
|
26
18
|
end
|
27
19
|
|
28
|
-
|
29
|
-
message
|
30
|
-
end
|
20
|
+
alias to_s message
|
31
21
|
end
|