promiscuous 0.92.0 → 0.100.0
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 +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
|
|