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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/promiscuous.rb +5 -1
  3. data/lib/promiscuous/config.rb +6 -5
  4. data/lib/promiscuous/dsl.rb +0 -4
  5. data/lib/promiscuous/loader.rb +0 -5
  6. data/lib/promiscuous/mongoid.rb +15 -5
  7. data/lib/promiscuous/publisher.rb +1 -1
  8. data/lib/promiscuous/publisher/model/active_record.rb +6 -1
  9. data/lib/promiscuous/publisher/model/base.rb +8 -11
  10. data/lib/promiscuous/publisher/model/mock.rb +2 -2
  11. data/lib/promiscuous/publisher/model/mongoid.rb +3 -4
  12. data/lib/promiscuous/publisher/operation/active_record.rb +13 -69
  13. data/lib/promiscuous/publisher/operation/atomic.rb +15 -158
  14. data/lib/promiscuous/publisher/operation/base.rb +13 -381
  15. data/lib/promiscuous/publisher/operation/ephemeral.rb +12 -8
  16. data/lib/promiscuous/publisher/operation/mongoid.rb +22 -92
  17. data/lib/promiscuous/publisher/operation/non_persistent.rb +0 -9
  18. data/lib/promiscuous/publisher/operation/proxy_for_query.rb +8 -6
  19. data/lib/promiscuous/publisher/operation/transaction.rb +4 -56
  20. data/lib/promiscuous/publisher/transport.rb +14 -0
  21. data/lib/promiscuous/publisher/transport/batch.rb +138 -0
  22. data/lib/promiscuous/publisher/transport/persistence.rb +14 -0
  23. data/lib/promiscuous/publisher/transport/persistence/active_record.rb +33 -0
  24. data/lib/promiscuous/publisher/transport/persistence/mongoid.rb +22 -0
  25. data/lib/promiscuous/publisher/transport/worker.rb +36 -0
  26. data/lib/promiscuous/publisher/worker.rb +3 -12
  27. data/lib/promiscuous/redis.rb +5 -0
  28. data/lib/promiscuous/subscriber/message.rb +1 -29
  29. data/lib/promiscuous/subscriber/model/base.rb +3 -2
  30. data/lib/promiscuous/subscriber/model/mongoid.rb +16 -1
  31. data/lib/promiscuous/subscriber/model/observer.rb +0 -1
  32. data/lib/promiscuous/subscriber/operation.rb +9 -3
  33. data/lib/promiscuous/subscriber/unit_of_work.rb +7 -7
  34. data/lib/promiscuous/subscriber/worker/eventual_destroyer.rb +1 -1
  35. data/lib/promiscuous/version.rb +1 -1
  36. metadata +39 -35
  37. data/lib/promiscuous/dependency.rb +0 -78
  38. 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