promiscuous 0.100.5 → 1.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|