promiscuous 0.100.5 → 1.0.0.beta1
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 +5 -1
- data/lib/promiscuous/config.rb +6 -5
- data/lib/promiscuous/dsl.rb +0 -4
- data/lib/promiscuous/loader.rb +0 -5
- data/lib/promiscuous/mongoid.rb +15 -5
- data/lib/promiscuous/publisher.rb +1 -1
- data/lib/promiscuous/publisher/model/active_record.rb +6 -1
- data/lib/promiscuous/publisher/model/base.rb +8 -11
- data/lib/promiscuous/publisher/model/mock.rb +2 -2
- data/lib/promiscuous/publisher/model/mongoid.rb +3 -4
- data/lib/promiscuous/publisher/operation/active_record.rb +13 -69
- data/lib/promiscuous/publisher/operation/atomic.rb +15 -158
- data/lib/promiscuous/publisher/operation/base.rb +13 -381
- data/lib/promiscuous/publisher/operation/ephemeral.rb +12 -8
- data/lib/promiscuous/publisher/operation/mongoid.rb +22 -92
- data/lib/promiscuous/publisher/operation/non_persistent.rb +0 -9
- data/lib/promiscuous/publisher/operation/proxy_for_query.rb +8 -6
- data/lib/promiscuous/publisher/operation/transaction.rb +4 -56
- data/lib/promiscuous/publisher/transport.rb +14 -0
- data/lib/promiscuous/publisher/transport/batch.rb +138 -0
- data/lib/promiscuous/publisher/transport/persistence.rb +14 -0
- data/lib/promiscuous/publisher/transport/persistence/active_record.rb +33 -0
- data/lib/promiscuous/publisher/transport/persistence/mongoid.rb +22 -0
- data/lib/promiscuous/publisher/transport/worker.rb +36 -0
- data/lib/promiscuous/publisher/worker.rb +3 -12
- data/lib/promiscuous/redis.rb +5 -0
- data/lib/promiscuous/subscriber/message.rb +1 -29
- data/lib/promiscuous/subscriber/model/base.rb +3 -2
- data/lib/promiscuous/subscriber/model/mongoid.rb +16 -1
- data/lib/promiscuous/subscriber/model/observer.rb +0 -1
- data/lib/promiscuous/subscriber/operation.rb +9 -3
- data/lib/promiscuous/subscriber/unit_of_work.rb +7 -7
- data/lib/promiscuous/subscriber/worker/eventual_destroyer.rb +1 -1
- data/lib/promiscuous/version.rb +1 -1
- metadata +39 -35
- data/lib/promiscuous/dependency.rb +0 -78
- data/lib/promiscuous/error/dependency.rb +0 -116
@@ -1,78 +0,0 @@
|
|
1
|
-
require 'fnv'
|
2
|
-
|
3
|
-
class Promiscuous::Dependency
|
4
|
-
attr_accessor :internal_key, :version, :type
|
5
|
-
|
6
|
-
def initialize(*args)
|
7
|
-
options = args.extract_options!
|
8
|
-
@type = options[:type]
|
9
|
-
@owner = options[:owner]
|
10
|
-
@dont_hash = options[:dont_hash]
|
11
|
-
|
12
|
-
@internal_key = args.join('/')
|
13
|
-
|
14
|
-
if @internal_key =~ /^[0-9]+$/
|
15
|
-
@internal_key = @internal_key.to_i
|
16
|
-
@hash = @internal_key
|
17
|
-
else
|
18
|
-
@hash = FNV.new.fnv1a_32(@internal_key)
|
19
|
-
|
20
|
-
if Promiscuous::Config.hash_size.to_i > 0
|
21
|
-
# We hash dependencies to have a O(1) memory footprint in Redis.
|
22
|
-
# The hashing needs to be deterministic across instances in order to
|
23
|
-
# function properly.
|
24
|
-
@hash = @hash % Promiscuous::Config.hash_size.to_i
|
25
|
-
@internal_key = @hash unless @dont_hash
|
26
|
-
end
|
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
|
42
|
-
end
|
43
|
-
|
44
|
-
def key(role)
|
45
|
-
Promiscuous::Key.new(role).join(@internal_key)
|
46
|
-
end
|
47
|
-
|
48
|
-
def redis_node(distributed_redis=nil)
|
49
|
-
distributed_redis ||= Promiscuous::Redis.master
|
50
|
-
distributed_redis.nodes[@hash % distributed_redis.nodes.size]
|
51
|
-
end
|
52
|
-
|
53
|
-
def as_json(options={})
|
54
|
-
@version ? [@internal_key, @version].join(':') : @internal_key
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.parse(payload, options={})
|
58
|
-
case payload
|
59
|
-
when /^(.+):([0-9]+)$/ then new($1, options).tap { |d| d.version = $2.to_i }
|
60
|
-
when /^(.+)$/ then new($1, options)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def to_s
|
65
|
-
as_json.to_s
|
66
|
-
end
|
67
|
-
|
68
|
-
# We need the eql? method to function properly (we use ==, uniq, ...) in operation
|
69
|
-
# XXX The version is not taken in account.
|
70
|
-
def eql?(other)
|
71
|
-
self.internal_key == other.internal_key
|
72
|
-
end
|
73
|
-
alias == eql?
|
74
|
-
|
75
|
-
def hash
|
76
|
-
self.internal_key.hash
|
77
|
-
end
|
78
|
-
end
|
@@ -1,116 +0,0 @@
|
|
1
|
-
class Promiscuous::Error::Dependency < Promiscuous::Error::Base
|
2
|
-
attr_accessor :dependency_solutions, :operation
|
3
|
-
|
4
|
-
def initialize(options={})
|
5
|
-
self.operation = options[:operation]
|
6
|
-
end
|
7
|
-
|
8
|
-
# TODO Convert all that with Erb
|
9
|
-
|
10
|
-
def message
|
11
|
-
msg = nil
|
12
|
-
case operation.operation
|
13
|
-
when :read
|
14
|
-
msg = "Promiscuous doesn't have any tracked dependencies to perform this multi read operation.\n" +
|
15
|
-
"This is what you can do:\n\n" +
|
16
|
-
" 1. Bypass Promiscuous\n\n" +
|
17
|
-
" If you don't use the result of this operation in your following writes,\n" +
|
18
|
-
" you can wrap your read query in a 'without_promiscuous { }' block.\n" +
|
19
|
-
" This is the preferred solution when you are sure that the read doesn't\n" +
|
20
|
-
" influence the value of a published attribute.\n\n" +
|
21
|
-
" Rule of thumb: Predicates (methods ending with ?) are often suitable for this use case.\n\n"
|
22
|
-
cnt = 2
|
23
|
-
if operation.operation_ext != :count
|
24
|
-
msg += " #{cnt}. Synchronize on individual instances\n\n" +
|
25
|
-
" If the collection you are iterating through is small (<10), it becomes intersting\n" +
|
26
|
-
" to track instances through their ids instead of the query selector. Example:\n\n" +
|
27
|
-
" criteria.without_promiscuous.each do |doc|\n" +
|
28
|
-
" next if doc.should_do_something?\n" +
|
29
|
-
" doc.reload # tell promiscuous to track the instance\n" +
|
30
|
-
" doc.do_something!\n" +
|
31
|
-
" end\n\n"
|
32
|
-
cnt += 1
|
33
|
-
end
|
34
|
-
if operation.selector_keys.present?
|
35
|
-
msg += " #{cnt}. Track New Dependencies\n\n" +
|
36
|
-
" Add #{operation.selector_keys.count == 1 ? "the following line" : "one of the following lines"} " +
|
37
|
-
"in the #{operation.instance.class} model:\n\n" +
|
38
|
-
" class #{operation.instance.class}\n" +
|
39
|
-
operation.selector_keys.map { |field| " track_dependencies_of :#{field}" }.join("\n") + "\n" +
|
40
|
-
" end\n\n" +
|
41
|
-
(operation.selector_keys.count > 1 ?
|
42
|
-
" The more specific field, the better. Promiscuous works better when working with small subsets\n" +
|
43
|
-
" For example, tracking something like 'member_id' is a fairly safe choice.\n\n" : "") +
|
44
|
-
" Note that dependency tracking slows down your writes. It can be seen as the analogous\n" +
|
45
|
-
" of an index on a regular database.\n" +
|
46
|
-
" You may find more information about the implications in the Promiscuous wiki (TODO:link).\n\n"
|
47
|
-
end
|
48
|
-
when :update
|
49
|
-
msg = "Promiscuous cannot track dependencies of a multi update operation.\n" +
|
50
|
-
"This is what you can do:\n\n" +
|
51
|
-
" 1. Instead of doing a multi updates, update each instance separately\n\n" +
|
52
|
-
" 2. Do not assign has_many associations directly, but use the << operator instead.\n\n"
|
53
|
-
when :destroy
|
54
|
-
msg = "Promiscuous cannot track dependencies of a multi delete operation.\n" +
|
55
|
-
"This is what you can do:\n\n" +
|
56
|
-
" 1. Instead of doing a multi delete, delete each instance separatly.\n\n" +
|
57
|
-
" 2. Use destroy_all instead of destroy_all.\n\n" +
|
58
|
-
" 3. Declare your has_many relationships with :dependent => :destroy instead of :delete.\n\n"
|
59
|
-
end
|
60
|
-
|
61
|
-
msg += "#{"-" * 100}\n\n"
|
62
|
-
|
63
|
-
msg += "Promiscuous cannot allow the following "
|
64
|
-
case operation.operation_ext || operation.operation
|
65
|
-
when :count then msg += 'count'
|
66
|
-
when :mapreduce then msg += 'mapreduce'
|
67
|
-
when :read then msg += 'each loop'
|
68
|
-
when :update then msg += 'multi update'
|
69
|
-
when :destroy then msg += 'multi destroy'
|
70
|
-
end
|
71
|
-
msg += " #{self.class.explain_operation(self.operation)}"
|
72
|
-
msg += "\n\nProTip: Try again with TRACE=2 in the shell or ENV['TRACE']='2' in the console.\n" unless ENV['TRACE']
|
73
|
-
msg
|
74
|
-
rescue Exception => e
|
75
|
-
"#{e}\n#{e.backtrace.join("\n")}"
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.explain_operation(operation, limit=100)
|
79
|
-
instance = operation.instance
|
80
|
-
selector = instance ? get_selector(instance.attributes, limit) : ""
|
81
|
-
class_name = instance ? instance.class : "Unknown"
|
82
|
-
|
83
|
-
if operation.operation == :create
|
84
|
-
"#{instance.class}.create(#{selector})"
|
85
|
-
else
|
86
|
-
case operation.operation_ext || operation.operation
|
87
|
-
when :count then verb = 'count'
|
88
|
-
when :mapreduce then verb = 'mapreduce(...)'
|
89
|
-
when :read then verb = operation.multi? ? 'each { ... }' : 'first'
|
90
|
-
when :update then verb = operation.multi? ? 'update_all' : 'update'
|
91
|
-
when :destroy then verb = operation.multi? ? 'delete_all' : 'delete'
|
92
|
-
end
|
93
|
-
msg = "#{class_name}#{selector.present? ? ".where(#{selector})" : ""}.#{verb}"
|
94
|
-
if operation.operation == :update && operation.respond_to?(:change) && operation.change
|
95
|
-
msg += "(#{get_selector(operation.change, limit)})"
|
96
|
-
end
|
97
|
-
|
98
|
-
if operation.operation == :commit
|
99
|
-
msg = "Transaction commit"
|
100
|
-
end
|
101
|
-
|
102
|
-
msg
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def self.get_selector(attributes, limit=100)
|
107
|
-
# TODO ActiveRecord?
|
108
|
-
attributes = attributes['$set'] if attributes.count == 1 && attributes['$set']
|
109
|
-
attributes.reject! { |k,v| v.nil? }
|
110
|
-
selector = attributes.map { |k,v| ":#{k} => #{v}" }.join(", ")
|
111
|
-
selector = "#{selector[0...(limit-3)]}..." if selector.size > limit
|
112
|
-
selector
|
113
|
-
end
|
114
|
-
|
115
|
-
alias to_s message
|
116
|
-
end
|