activerecord-batch_touching 1.0.pre.beta4 → 1.0.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06aa6d889559fed96fed448ffb100324496a79d1e97e147607067f1a3275ab92
|
4
|
+
data.tar.gz: 5a737bed02b7812f0feba746bf2ea19bfece091959bdaa25a465dae634b1c38c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0add569ae81b020e75b5168b00e550d4f69a67cf8e24e5adbb6f45808339ac0d126f69a59bbd0c8620dd073fc762a9cc98283362f9534f3e20197b95da9dc33
|
7
|
+
data.tar.gz: 2d23e326e341927fa8196c566b3101f8850f56632ab93bdb9bf6506e87a451f9c6141801e40c2c6f2743e7a9cfacc535c555161d06003467e1bd29fa59c0f673
|
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record Batch Touching
|
3
5
|
module BatchTouching
|
4
|
-
|
5
6
|
# Tracking of the touch state. This class has no class-level data, so you can
|
6
7
|
# store per-thread instances in thread-local variables.
|
7
8
|
class State # :nodoc:
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "activerecord/batch_touching/version"
|
2
4
|
require "activerecord/batch_touching/state"
|
3
5
|
|
@@ -67,7 +69,7 @@ module ActiveRecord
|
|
67
69
|
|
68
70
|
# Disable batch touching for a block
|
69
71
|
def disable
|
70
|
-
Thread.current[:batch_touching_disabled] =
|
72
|
+
Thread.current[:batch_touching_disabled] = true
|
71
73
|
yield
|
72
74
|
ensure
|
73
75
|
Thread.current[:batch_touching_disabled] = false
|
@@ -77,34 +79,36 @@ module ActiveRecord
|
|
77
79
|
Thread.current[:batch_touching_disabled] || @disabled
|
78
80
|
end
|
79
81
|
|
80
|
-
def states
|
81
|
-
Thread.current[:batch_touching_states] ||= []
|
82
|
-
end
|
83
|
-
|
84
|
-
def current_state
|
85
|
-
states.last
|
86
|
-
end
|
87
|
-
|
88
|
-
delegate :add_record, to: :current_state
|
89
|
-
|
90
|
-
def batch_touching?
|
91
|
-
states.present? && !disabled?
|
92
|
-
end
|
93
|
-
|
94
82
|
# Start batching all touches. When done, apply them. (Unless nested.)
|
95
|
-
def start(
|
83
|
+
def start(requires_new:)
|
96
84
|
states.push State.new
|
97
85
|
yield.tap do
|
98
86
|
apply_touches if states.length == 1
|
99
87
|
end
|
100
88
|
ensure
|
101
|
-
merge_transactions unless $! &&
|
89
|
+
merge_transactions unless $! && requires_new
|
102
90
|
|
103
91
|
# Decrement nesting even if +apply_touches+ raised an error. To ensure the stack of States
|
104
92
|
# is empty after the top-level transaction exits.
|
105
93
|
states.pop
|
106
94
|
end
|
107
95
|
|
96
|
+
delegate :add_record, to: :current_state
|
97
|
+
|
98
|
+
def batch_touching?
|
99
|
+
states.present? && !disabled?
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def states
|
105
|
+
Thread.current[:batch_touching_states] ||= []
|
106
|
+
end
|
107
|
+
|
108
|
+
def current_state
|
109
|
+
states.last
|
110
|
+
end
|
111
|
+
|
108
112
|
# When exiting a nested transaction, merge the nested transaction's
|
109
113
|
# touched records with the outer transaction's touched records.
|
110
114
|
def merge_transactions
|
@@ -114,53 +118,57 @@ module ActiveRecord
|
|
114
118
|
# Apply the touches that were batched. We're in a transaction already so there's no need to open one.
|
115
119
|
def apply_touches
|
116
120
|
current_time = ActiveRecord::Base.current_time_from_proper_timezone
|
117
|
-
|
121
|
+
already_touched = Set.new
|
118
122
|
all_states = State.new
|
119
123
|
while current_state.more_records?
|
120
124
|
all_states.merge!(current_state)
|
121
125
|
state_records = current_state.records
|
122
126
|
current_state.clear_records!
|
123
127
|
state_records.each do |(_klass, columns), records|
|
124
|
-
soft_touch_records(columns, records, current_time,
|
128
|
+
soft_touch_records(columns, records, current_time, already_touched)
|
125
129
|
end
|
126
130
|
end
|
127
131
|
|
128
132
|
# Sort by class name. Having a consistent order can help mitigate deadlocks.
|
129
|
-
sorted_records = all_states.records.keys.sort_by { |k| k.first.name }.
|
133
|
+
sorted_records = all_states.records.keys.sort_by { |k| k.first.name }.to_h { |k| [k, all_states.records[k]] }
|
130
134
|
sorted_records.each do |(klass, columns), records|
|
131
135
|
records.reject!(&:destroyed?)
|
132
|
-
touch_records klass, columns, records, current_time
|
136
|
+
touch_records klass, columns, records, current_time
|
133
137
|
end
|
134
138
|
end
|
135
139
|
|
136
|
-
#
|
137
|
-
|
138
|
-
|
140
|
+
# Perform DB update to touch records
|
141
|
+
def touch_records(klass, columns, records, time)
|
142
|
+
return if columns.blank? || records.blank?
|
143
|
+
|
144
|
+
sql = columns.map { |column| "#{klass.connection.quote_column_name(column)} = :time" }.join(", ")
|
145
|
+
sql += ", #{klass.locking_column} = #{klass.locking_column} + 1" if klass.locking_enabled?
|
146
|
+
|
147
|
+
klass.unscoped.where(klass.primary_key => records.to_a).update_all([sql, time: time])
|
148
|
+
end
|
149
|
+
|
150
|
+
# Set new timestamp in memory, without updating the DB just yet.
|
151
|
+
def soft_touch_records(columns, records, time, already_touched)
|
152
|
+
return if columns.blank? || records.blank?
|
153
|
+
|
139
154
|
records.each do |record|
|
140
|
-
record.
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
clear_attribute_changes(columns)
|
148
|
-
end
|
149
|
-
unless callbacks_run.include?(record)
|
150
|
-
record._run_touch_callbacks
|
151
|
-
callbacks_run.add(record)
|
152
|
-
end
|
153
|
-
end
|
155
|
+
next if record.destroyed? || already_touched.include?(record)
|
156
|
+
|
157
|
+
soft_touch_record(columns, record, time)
|
158
|
+
|
159
|
+
# Running callbacks also allows us to collect more touches (i.e. touch: true for associations).
|
160
|
+
record._run_touch_callbacks
|
161
|
+
already_touched.add(record)
|
154
162
|
end
|
155
163
|
end
|
156
164
|
|
157
|
-
|
158
|
-
|
159
|
-
if
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
165
|
+
def soft_touch_record(columns, record, time)
|
166
|
+
columns.each { |column| record.write_attribute column, time }
|
167
|
+
if record.locking_enabled?
|
168
|
+
record[record.class.locking_column] += 1
|
169
|
+
record.clear_attribute_changes(columns + [record.class.locking_column])
|
170
|
+
else
|
171
|
+
record.clear_attribute_changes(columns)
|
164
172
|
end
|
165
173
|
end
|
166
174
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-batch_touching
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.pre.
|
4
|
+
version: 1.0.pre.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Morearty
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-07-
|
12
|
+
date: 2022-07-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
57
|
+
name: rspec-rails
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
60
|
- - ">="
|
@@ -68,7 +68,7 @@ dependencies:
|
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
71
|
+
name: rubocop
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
74
|
- - ">="
|
@@ -82,7 +82,35 @@ dependencies:
|
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '0'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
|
-
name:
|
85
|
+
name: rubocop-performance
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: rubocop-rake
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: rubocop-rspec
|
86
114
|
requirement: !ruby/object:Gem::Requirement
|
87
115
|
requirements:
|
88
116
|
- - ">="
|
@@ -123,6 +151,34 @@ dependencies:
|
|
123
151
|
- - ">="
|
124
152
|
- !ruby/object:Gem::Version
|
125
153
|
version: '0'
|
154
|
+
- !ruby/object:Gem::Dependency
|
155
|
+
name: sqlite3
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
- !ruby/object:Gem::Dependency
|
169
|
+
name: timecop
|
170
|
+
requirement: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
type: :development
|
176
|
+
prerelease: false
|
177
|
+
version_requirements: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
126
182
|
description: Batch up your ActiveRecord "touch" operations for better performance.
|
127
183
|
All accumulated "touch" calls will be consolidated into as few database round trips
|
128
184
|
as possible.
|
@@ -136,10 +192,11 @@ files:
|
|
136
192
|
- lib/activerecord/batch_touching.rb
|
137
193
|
- lib/activerecord/batch_touching/state.rb
|
138
194
|
- lib/activerecord/batch_touching/version.rb
|
139
|
-
homepage:
|
195
|
+
homepage: https://github.com/irphilli/activerecord-batch_touching
|
140
196
|
licenses:
|
141
197
|
- MIT
|
142
|
-
metadata:
|
198
|
+
metadata:
|
199
|
+
rubygems_mfa_required: 'true'
|
143
200
|
post_install_message:
|
144
201
|
rdoc_options: []
|
145
202
|
require_paths:
|
@@ -148,7 +205,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
205
|
requirements:
|
149
206
|
- - ">="
|
150
207
|
- !ruby/object:Gem::Version
|
151
|
-
version:
|
208
|
+
version: 2.7.0
|
152
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
210
|
requirements:
|
154
211
|
- - ">"
|