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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b49e20ef32de7aa2d0ff24941962adf673ab5332
|
4
|
+
data.tar.gz: 036b09d5ea13bc45751221693a7f8bb4edea4d1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ceaab91a632d2b7eaa71f0ae59f14d66a47e7c5f531582425f24b567b79530dc3f2602ca01b139001dd047001c1b5f54ed41f1beb798d9dc8976de93c26a29ae
|
7
|
+
data.tar.gz: 5d3313bc9aabb5f1da0a6e71216637dd326bc4d8c4a10cbfc3cbeb83b45733479c348f20a91b1b56160670d54e9653c70fd08e169c9a68dfaba8109aefddcad9
|
data/lib/promiscuous.rb
CHANGED
@@ -20,9 +20,6 @@ module Promiscuous
|
|
20
20
|
:CLI, :Error, :Loader, :AMQP, :Redis, :ZK, :Config, :DSL, :Key,
|
21
21
|
:Convenience, :Dependency, :Timer
|
22
22
|
|
23
|
-
# Shortcut for the middleware, TODO make load on demand
|
24
|
-
Middleware = Publisher::Context::Middleware
|
25
|
-
|
26
23
|
extend Promiscuous::DSL
|
27
24
|
|
28
25
|
Object.__send__(:include, Promiscuous::Convenience)
|
@@ -82,8 +79,8 @@ module Promiscuous
|
|
82
79
|
!!Thread.current[:promiscuous_disabled]
|
83
80
|
end
|
84
81
|
|
85
|
-
def context
|
86
|
-
Publisher::Context::Base.
|
82
|
+
def context
|
83
|
+
Publisher::Context::Base.current
|
87
84
|
end
|
88
85
|
end
|
89
86
|
|
data/lib/promiscuous/amqp.rb
CHANGED
data/lib/promiscuous/cli.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ruby-progressbar'
|
2
|
+
|
1
3
|
class Promiscuous::CLI
|
2
4
|
attr_accessor :options
|
3
5
|
|
@@ -17,14 +19,6 @@ class Promiscuous::CLI
|
|
17
19
|
print_status "Thread #{thread} #{thread['label']} -- no backtrace"
|
18
20
|
end
|
19
21
|
end
|
20
|
-
|
21
|
-
if @worker && @worker.respond_to?(:message_synchronizer)
|
22
|
-
if blocked_messages = @worker.message_synchronizer.try(:blocked_messages)
|
23
|
-
print_status '----[ Pending Dependencies ]----' + '-' * (100-32)
|
24
|
-
blocked_messages.reverse_each { |msg| print_status msg }
|
25
|
-
end
|
26
|
-
print_status '-' * 100
|
27
|
-
end
|
28
22
|
end
|
29
23
|
end
|
30
24
|
end
|
@@ -61,7 +55,7 @@ class Promiscuous::CLI
|
|
61
55
|
bar = ProgressBar.create(:format => '%t |%b>%i| %c/%C %e', :title => title, :total => criteria.count)
|
62
56
|
criteria.each do |doc|
|
63
57
|
break if @stop
|
64
|
-
|
58
|
+
doc.promiscuous.sync
|
65
59
|
bar.increment
|
66
60
|
end
|
67
61
|
end
|
@@ -100,16 +94,6 @@ class Promiscuous::CLI
|
|
100
94
|
print_status "Replayed #{@num_msg} messages"
|
101
95
|
end
|
102
96
|
|
103
|
-
def bootstrap
|
104
|
-
phase = options[:criterias][0].to_sym
|
105
|
-
raise "Subscriber bootstrap must be one of [setup|run|finalize|status]" unless [:setup, :run, :finalize, :status].include?(phase)
|
106
|
-
if phase == :setup && criteria = options[:bootstrap_criteria]
|
107
|
-
Promiscuous::Publisher::Bootstrap.setup(:models => eval(criteria).to_a)
|
108
|
-
else
|
109
|
-
Promiscuous::Publisher::Bootstrap.__send__(phase)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
97
|
def subscribe
|
114
98
|
@worker = Promiscuous::Subscriber::Worker.new
|
115
99
|
@worker.start
|
@@ -143,7 +127,6 @@ class Promiscuous::CLI
|
|
143
127
|
opts.separator " promiscuous publish \"Model1.where(:updated_at.gt => 1.day.ago)\" [Model2 Model3...]"
|
144
128
|
opts.separator " promiscuous publisher_recovery"
|
145
129
|
opts.separator " promiscuous subscribe"
|
146
|
-
opts.separator " promiscuous bootstrap phase"
|
147
130
|
opts.separator " promiscuous mocks"
|
148
131
|
opts.separator " promiscuous record logfile"
|
149
132
|
opts.separator " promiscuous replay logfile"
|
@@ -154,10 +137,6 @@ class Promiscuous::CLI
|
|
154
137
|
Promiscuous::Config.no_deps = true
|
155
138
|
end
|
156
139
|
|
157
|
-
opts.on "-x", "--ignore-exceptions", "Ignore exceptions and continue to process messages" do
|
158
|
-
Promiscuous::Config.ignore_exceptions = true
|
159
|
-
end
|
160
|
-
|
161
140
|
opts.on "-l", "--require FILE", "File to require to load your app. Don't worry about it with rails" do |file|
|
162
141
|
options[:require] = file
|
163
142
|
end
|
@@ -175,20 +154,6 @@ class Promiscuous::CLI
|
|
175
154
|
Promiscuous::Config.stats_interval = duration.to_f
|
176
155
|
end
|
177
156
|
|
178
|
-
opts.on "-b", "--bootstrap [pass1|pass2]", "Run subscriber in bootstrap mode" do |mode|
|
179
|
-
mode = mode.to_sym
|
180
|
-
raise "Subscriber bootstrap must be run in pass1 or pass2 mode" unless [:pass1, :pass2].include?(mode)
|
181
|
-
Promiscuous::Config.bootstrap = mode.to_sym
|
182
|
-
end
|
183
|
-
|
184
|
-
opts.on "-t", "--threads [NUM]", "Number of subscriber worker threads to run. Defaults to 10." do |threads|
|
185
|
-
Promiscuous::Config.subscriber_threads = threads.to_i
|
186
|
-
end
|
187
|
-
|
188
|
-
opts.on "-c", "--criteria FILE", "Criteria to bootstrap a subset of data" do |criteria|
|
189
|
-
options[:bootstrap_criteria] = criteria
|
190
|
-
end
|
191
|
-
|
192
157
|
opts.on "-D", "--daemonize", "Daemonize process" do
|
193
158
|
options[:daemonize] = true
|
194
159
|
end
|
@@ -210,15 +175,12 @@ class Promiscuous::CLI
|
|
210
175
|
options[:action] = args.shift.try(:to_sym)
|
211
176
|
options[:criterias] = args
|
212
177
|
options[:log_file] = args.first
|
213
|
-
options[:publisher_bootstrap] = args.first
|
214
178
|
|
215
179
|
case options[:action]
|
216
180
|
when :publish then raise "Please specify one or more criterias" unless options[:criterias].present?
|
217
181
|
when :subscribe then raise "Why are you specifying a criteria?" if options[:criterias].present?
|
218
|
-
when :bootstrap then raise "You must specify one of [setup|start|finalize]" unless options[:criterias].present?
|
219
182
|
when :record then raise "Please specify a log file to record" unless options[:log_file].present?
|
220
183
|
when :replay then raise "Please specify a log file to replay" unless options[:log_file].present?
|
221
|
-
when :publisher_bootstrap then raise "Please specify 'on' or 'off'" unless options[:publisher_bootstrap].present?
|
222
184
|
when :publisher_recovery
|
223
185
|
when :mocks
|
224
186
|
else puts parser; exit 1
|
@@ -270,12 +232,10 @@ class Promiscuous::CLI
|
|
270
232
|
case options[:action]
|
271
233
|
when :publish then publish
|
272
234
|
when :subscribe then subscribe
|
273
|
-
when :bootstrap then bootstrap
|
274
235
|
when :record then record
|
275
236
|
when :replay then replay
|
276
237
|
when :mocks then generate_mocks
|
277
238
|
when :publisher_recovery then publisher_recovery
|
278
|
-
when :publisher_bootstrap then set_publisher_bootstrap
|
279
239
|
end
|
280
240
|
end
|
281
241
|
|
data/lib/promiscuous/config.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module Promiscuous::Config
|
2
|
-
mattr_accessor :app, :
|
2
|
+
mattr_accessor :app, :backend, :amqp_url,
|
3
3
|
:publisher_amqp_url, :subscriber_amqp_url, :publisher_exchange,
|
4
4
|
:subscriber_exchanges, :queue_name, :queue_options, :redis_url,
|
5
5
|
:redis_urls, :redis_stats_url, :stats_interval,
|
6
6
|
:socket_timeout, :heartbeat, :no_deps, :hash_size,
|
7
7
|
:prefetch, :recovery_timeout, :logger, :subscriber_threads,
|
8
8
|
:version_field, :error_notifier, :recovery_on_boot,
|
9
|
-
:on_stats, :
|
9
|
+
:on_stats, :max_retries, :generation, :destroy_timeout, :destroy_check_interval
|
10
10
|
|
11
11
|
def self.backend=(value)
|
12
12
|
@@backend = value
|
@@ -35,8 +35,6 @@ module Promiscuous::Config
|
|
35
35
|
block.call(self) if block
|
36
36
|
|
37
37
|
self.app ||= Rails.application.class.parent_name.underscore rescue nil if defined?(Rails)
|
38
|
-
self.bootstrap ||= false
|
39
|
-
self.bootstrap_chunk_size ||= 10000
|
40
38
|
self.backend ||= best_amqp_backend
|
41
39
|
self.amqp_url ||= 'amqp://guest:guest@localhost:5672'
|
42
40
|
self.publisher_amqp_url ||= self.amqp_url
|
@@ -54,7 +52,7 @@ module Promiscuous::Config
|
|
54
52
|
self.heartbeat ||= 60
|
55
53
|
self.no_deps ||= false
|
56
54
|
self.hash_size ||= 2**20 # one million keys ~ 200Mb.
|
57
|
-
self.prefetch ||=
|
55
|
+
self.prefetch ||= 1000
|
58
56
|
self.recovery_timeout ||= 10
|
59
57
|
self.logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
|
60
58
|
self.subscriber_threads ||= 10
|
@@ -62,10 +60,10 @@ module Promiscuous::Config
|
|
62
60
|
self.version_field ||= '_v'
|
63
61
|
self.recovery_on_boot = true if self.recovery_on_boot.nil?
|
64
62
|
self.on_stats ||= proc { |rate, latency| }
|
65
|
-
self.ignore_exceptions ||= false
|
66
|
-
self.consistency ||= :eventual
|
67
63
|
self.max_retries ||= 10
|
68
64
|
self.generation ||= 1
|
65
|
+
self.destroy_timeout ||= 1.hour
|
66
|
+
self.destroy_check_interval ||= 10.minutes
|
69
67
|
end
|
70
68
|
|
71
69
|
def self.configure(&block)
|
@@ -1,9 +1,8 @@
|
|
1
1
|
class Promiscuous::Error::Dependency < Promiscuous::Error::Base
|
2
|
-
attr_accessor :dependency_solutions, :operation
|
2
|
+
attr_accessor :dependency_solutions, :operation
|
3
3
|
|
4
4
|
def initialize(options={})
|
5
5
|
self.operation = options[:operation]
|
6
|
-
self.context = Promiscuous::Publisher::Context.current
|
7
6
|
end
|
8
7
|
|
9
8
|
# TODO Convert all that with Erb
|
@@ -69,7 +68,6 @@ class Promiscuous::Error::Dependency < Promiscuous::Error::Base
|
|
69
68
|
when :update then msg += 'multi update'
|
70
69
|
when :destroy then msg += 'multi destroy'
|
71
70
|
end
|
72
|
-
msg += " in the '#{context.name}' context:\n\n"
|
73
71
|
msg += " #{self.class.explain_operation(self.operation)}"
|
74
72
|
msg += "\n\nProTip: Try again with TRACE=2 in the shell or ENV['TRACE']='2' in the console.\n" unless ENV['TRACE']
|
75
73
|
msg
|
@@ -2,44 +2,13 @@ class Promiscuous::Publisher::Context::Base
|
|
2
2
|
# XXX Context are not sharable among threads
|
3
3
|
|
4
4
|
def self.current
|
5
|
-
Thread.current[:promiscuous_context]
|
5
|
+
Thread.current[:promiscuous_context] ||= self.new
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
Thread.current[:promiscuous_context] = value
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.with_context(*args, &block)
|
13
|
-
raise "You cannot nest contexts" if self.current
|
14
|
-
|
15
|
-
self.current = new(*args)
|
16
|
-
begin
|
17
|
-
self.current.trace "<<< open <<<", :level => 1
|
18
|
-
yield
|
19
|
-
ensure
|
20
|
-
self.current.trace "<<< close <<<", :level => 1
|
21
|
-
self.current = nil
|
22
|
-
|
23
|
-
ActiveRecord::Base.clear_active_connections! if defined?(ActiveRecord::Base)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
attr_accessor :name, :read_operations, :extra_dependencies, :current_user
|
28
|
-
|
29
|
-
def initialize(name=nil, options={})
|
30
|
-
@name = name.try(:to_s) || 'anonymous'
|
31
|
-
@current_user = options[:current_user]
|
32
|
-
@read_operations = []
|
33
|
-
@extra_dependencies = []
|
34
|
-
@transaction_managers = {}
|
35
|
-
|
36
|
-
Promiscuous::AMQP.ensure_connected
|
37
|
-
|
38
|
-
Mongoid::IdentityMap.clear if defined?(Mongoid::IdentityMap)
|
39
|
-
ActiveRecord::IdentityMap.clear if defined?(ActiveRecord::IdentityMap)
|
40
|
-
end
|
8
|
+
attr_accessor :current_user
|
41
9
|
|
42
10
|
def transaction_context_of(driver)
|
11
|
+
@transaction_managers ||= {}
|
43
12
|
@transaction_managers[driver] ||= Promiscuous::Publisher::Context::Transaction.new(driver)
|
44
13
|
end
|
45
14
|
|
@@ -2,12 +2,10 @@ module Promiscuous::Publisher::Model::Base
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
class_attribute :published_attrs
|
5
|
+
class_attribute :published_attrs
|
6
6
|
cattr_accessor :published_db_fields # There is one on each root class, none on the subclasses
|
7
7
|
self.published_attrs = []
|
8
|
-
self.tracked_attrs = []
|
9
8
|
self.published_db_fields = []
|
10
|
-
track_dependencies_of :id
|
11
9
|
Promiscuous::Publisher::Model.publishers[self.promiscuous_collection_name] = self
|
12
10
|
end
|
13
11
|
|
@@ -38,26 +36,13 @@ module Promiscuous::Publisher::Model::Base
|
|
38
36
|
value
|
39
37
|
end
|
40
38
|
|
41
|
-
def get_dependency
|
42
|
-
return nil unless value
|
39
|
+
def get_dependency
|
43
40
|
@collection ||= @instance.class.promiscuous_collection_name
|
44
|
-
Promiscuous::Dependency.new(@collection,
|
41
|
+
Promiscuous::Dependency.new(@collection, :id, id)
|
45
42
|
end
|
46
43
|
|
47
|
-
def
|
48
|
-
|
49
|
-
# values in case of an update.
|
50
|
-
# Note that the caller expect the id dependency to come first
|
51
|
-
@instance.class.tracked_attrs.map do |attr|
|
52
|
-
begin
|
53
|
-
[attr, @instance.__send__(attr)]
|
54
|
-
rescue Exception => e
|
55
|
-
# Don't care about missing attributes for read dependencies.
|
56
|
-
raise e unless options[:allow_missing_attributes] && e.is_a?(ActiveModel::MissingAttributeError)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
.map { |attr, value| get_dependency(attr, value) }
|
60
|
-
.compact
|
44
|
+
def id
|
45
|
+
@instance.id
|
61
46
|
end
|
62
47
|
end
|
63
48
|
|
@@ -110,10 +95,6 @@ module Promiscuous::Publisher::Model::Base
|
|
110
95
|
@in_publish_block.to_i > 0
|
111
96
|
end
|
112
97
|
|
113
|
-
def track_dependencies_of(*attributes)
|
114
|
-
([self] + descendants).each { |klass| klass.tracked_attrs |= attributes.map(&:to_sym) }
|
115
|
-
end
|
116
|
-
|
117
98
|
def promiscuous_collection_name
|
118
99
|
self.name.pluralize.underscore
|
119
100
|
end
|
@@ -129,7 +110,6 @@ module Promiscuous::Publisher::Model::Base
|
|
129
110
|
def inherited(subclass)
|
130
111
|
super
|
131
112
|
subclass.published_attrs = self.published_attrs.dup
|
132
|
-
subclass.tracked_attrs = self.tracked_attrs.dup
|
133
113
|
# no copy for published_db_fields
|
134
114
|
end
|
135
115
|
end
|
@@ -25,14 +25,12 @@ module Promiscuous::Publisher::Model::Mock
|
|
25
25
|
def save_operation(operation)
|
26
26
|
payload = nil
|
27
27
|
|
28
|
-
Promiscuous::Publisher::
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
payload = op.generate_payload
|
33
|
-
end
|
28
|
+
op = Promiscuous::Publisher::Operation::Ephemeral.new(:instance => self, :operation => operation)
|
29
|
+
# TODO FIX the mocks to populate app name, also we need to hook before the
|
30
|
+
# json dump.
|
31
|
+
payload = op.generate_payload
|
34
32
|
|
35
|
-
Promiscuous::Subscriber::
|
33
|
+
Promiscuous::Subscriber::Message.new(payload).process
|
36
34
|
end
|
37
35
|
|
38
36
|
module ClassMethods
|
@@ -64,8 +64,7 @@ class ActiveRecord::Base
|
|
64
64
|
alias_method :release_savepoint_without_promiscuous, :release_savepoint
|
65
65
|
|
66
66
|
def with_promiscuous_transaction_context(&block)
|
67
|
-
|
68
|
-
block.call(ctx.transaction_context_of(:active_record)) if ctx
|
67
|
+
block.call(Promiscuous::Publisher::Context.current.transaction_context_of(:active_record))
|
69
68
|
end
|
70
69
|
|
71
70
|
def begin_db_transaction
|
@@ -106,24 +105,10 @@ class ActiveRecord::Base
|
|
106
105
|
with_promiscuous_transaction_context { |tx| tx.commit }
|
107
106
|
end
|
108
107
|
|
109
|
-
alias_method :select_all_without_promiscuous, :select_all
|
110
|
-
alias_method :select_values_without_promiscuous, :select_values
|
111
108
|
alias_method :insert_without_promiscuous, :insert
|
112
109
|
alias_method :update_without_promiscuous, :update
|
113
110
|
alias_method :delete_without_promiscuous, :delete
|
114
111
|
|
115
|
-
def select_all(arel, name = nil, binds = [])
|
116
|
-
PromiscuousSelectOperation.new(arel, name, binds, :connection => self).execute do
|
117
|
-
select_all_without_promiscuous(arel, name, binds)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def select_values(arel, name = nil)
|
122
|
-
PromiscuousSelectOperation.new(arel, name, [], :connection => self).execute do
|
123
|
-
select_values_without_promiscuous(arel, name)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
112
|
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
128
113
|
PromiscuousInsertOperation.new(arel, name, pk, id_value, sequence_name, binds, :connection => self).execute do
|
129
114
|
insert_without_promiscuous(arel, name, pk, id_value, sequence_name, binds)
|
@@ -157,11 +142,11 @@ class ActiveRecord::Base
|
|
157
142
|
end
|
158
143
|
|
159
144
|
def transaction_context
|
160
|
-
|
145
|
+
Promiscuous::Publisher::Context.current.transaction_context_of(:active_record)
|
161
146
|
end
|
162
147
|
|
163
148
|
def ensure_transaction!
|
164
|
-
if
|
149
|
+
if !transaction_context.in_transaction?
|
165
150
|
raise "You need to write to the database within an ActiveRecord transaction"
|
166
151
|
end
|
167
152
|
end
|
@@ -180,7 +165,7 @@ class ActiveRecord::Base
|
|
180
165
|
query.non_instrumented { db_operation.call }
|
181
166
|
query.instrumented do
|
182
167
|
db_operation_and_select.tap do
|
183
|
-
transaction_context.add_write_operation(self) if
|
168
|
+
transaction_context.add_write_operation(self) if !@instances.empty?
|
184
169
|
end
|
185
170
|
end
|
186
171
|
end
|
@@ -277,56 +262,6 @@ class ActiveRecord::Base
|
|
277
262
|
end
|
278
263
|
end
|
279
264
|
|
280
|
-
class PromiscuousSelectOperation < PromiscousOperation
|
281
|
-
def initialize(arel, name, binds, options={})
|
282
|
-
super
|
283
|
-
@operation = :read
|
284
|
-
@result = []
|
285
|
-
end
|
286
|
-
|
287
|
-
def model
|
288
|
-
@model ||= begin
|
289
|
-
case @arel
|
290
|
-
when Arel::SelectManager
|
291
|
-
raise "SQL statement too complicated (joins?)" if @arel.ast.cores.size != 1
|
292
|
-
model = @arel.ast.cores.first.source.left.engine
|
293
|
-
when ActiveRecord::Relation
|
294
|
-
return nil # TODO
|
295
|
-
else
|
296
|
-
raise "What is this query?" unless @arel.is_a?(Arel::SelectManager)
|
297
|
-
end
|
298
|
-
|
299
|
-
model = nil unless model < Promiscuous::Publisher::Model::ActiveRecord
|
300
|
-
model
|
301
|
-
end
|
302
|
-
rescue
|
303
|
-
# TODO Track dependencies of complex queries properly...
|
304
|
-
nil
|
305
|
-
end
|
306
|
-
|
307
|
-
def get_selector_instance
|
308
|
-
attrs = @arel.ast.cores.first.wheres.map { |w| [w.children.first.left.name, w.children.first.right] }
|
309
|
-
model.instantiate(Hash[attrs])
|
310
|
-
end
|
311
|
-
|
312
|
-
def query_dependencies
|
313
|
-
deps = dependencies_for(get_selector_instance)
|
314
|
-
deps.empty? ? super : deps
|
315
|
-
end
|
316
|
-
|
317
|
-
def execute(&db_operation)
|
318
|
-
# We dup because ActiveRecord modifies our return value
|
319
|
-
super.tap { @result = @result.dup }
|
320
|
-
end
|
321
|
-
|
322
|
-
def db_operation_and_select
|
323
|
-
# XXX This is only supported by Postgres.
|
324
|
-
@connection.exec_query("#{@connection.to_sql(@arel, @binds)}", @operation_name, @binds).to_a.tap do |result|
|
325
|
-
@instances = result.map { |row| model.instantiate(row) }
|
326
|
-
end
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
265
|
class PromiscuousTransaction < Promiscuous::Publisher::Operation::Transaction
|
331
266
|
attr_accessor :connection
|
332
267
|
|