activerecord-batch_touching 1.0.pre.beta4 → 1.0.pre.rc1
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
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
|
- - ">"
|