bulk_dependency_eraser 4.2.3 → 4.4.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/bulk_dependency_eraser/builder.rb +14 -2
- data/lib/bulk_dependency_eraser/deleter.rb +99 -26
- data/lib/bulk_dependency_eraser/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcd62dc450607a490d23f589f457c2e2ad30b6ba4096b775266372d2f3952445
|
4
|
+
data.tar.gz: 8e430d8e74cf2d401b75d335a7fc3da82672aa4fedbc0446024bf94c1f632116
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9added303e9a0982bd9cc026ef03937e0c095421fe89ba7edbdf24c7af27af61cce2f1e3c84b8f56d79fbf441ab5a4d72362fe9053b536af0286cb917c1abbc
|
7
|
+
data.tar.gz: 794831e885f3659586f5ffd075e93aee67d15e919bc2915023e733460e8739d4e5d7f86079f23feaf1cecb0a4a9ffaa6be5ccf885376203464fa7641952413c7
|
@@ -58,6 +58,7 @@ module BulkDependencyEraser
|
|
58
58
|
# Using PG system column CTID can result in a 10x deletion speed increase
|
59
59
|
# - is used in combination with the primary key column to ensure CTID hasn't changed.
|
60
60
|
use_pg_system_column_ctid: false,
|
61
|
+
delete_ctids_by_partions: false,
|
61
62
|
}.freeze
|
62
63
|
|
63
64
|
# write access so that these can be edited in-place by end-users who might need to manually adjust deletion order.
|
@@ -177,8 +178,14 @@ module BulkDependencyEraser
|
|
177
178
|
end
|
178
179
|
|
179
180
|
# Only pluck CTID if we're plucking column :id (the only primary key supported)
|
180
|
-
if columns == [:id] &&
|
181
|
-
columns << :ctid
|
181
|
+
if columns == [:id] && skip_ctid_check == false
|
182
|
+
columns << :ctid if opts_c.use_pg_system_column_ctid
|
183
|
+
columns << 'tableoid::regclass' if opts_c.delete_ctids_by_partions # to support partioned tables
|
184
|
+
end
|
185
|
+
|
186
|
+
# Gem test ENV only! Swap out SQL column value with SQLite3 mocked, compatible value
|
187
|
+
if ENV['TEST_GEM_ENV'] == 'bulk_dependency_eraser' && columns.include?('tableoid::regclass')
|
188
|
+
columns[columns.index('tableoid::regclass')] = 'tableoid'
|
182
189
|
end
|
183
190
|
|
184
191
|
set_current_klass_name(query)
|
@@ -222,6 +229,11 @@ module BulkDependencyEraser
|
|
222
229
|
# - query_results would just be an array of IDs
|
223
230
|
return [query_results, nil] if columns.count == 1
|
224
231
|
|
232
|
+
# Gem test ENV only! Swap out SQL column value with SQLite3 mocked, compatible value
|
233
|
+
if ENV['TEST_GEM_ENV'] == 'bulk_dependency_eraser' && columns.include?('tableoid')
|
234
|
+
columns[columns.index('tableoid')] = 'tableoid::regclass'
|
235
|
+
end
|
236
|
+
|
225
237
|
transposed_results = query_results.transpose
|
226
238
|
|
227
239
|
query_primary_keys = transposed_results.shift || [] # could be an empty id set
|
@@ -54,6 +54,11 @@ module BulkDependencyEraser
|
|
54
54
|
# Applied to deletion queries
|
55
55
|
# - 1st priority of scopes
|
56
56
|
deletion_proc_scopes_per_class_name: {},
|
57
|
+
use_ctid_over_primary_key: false,
|
58
|
+
# Using PG system column CTID can result in a 10x deletion speed increase
|
59
|
+
# - is used in combination with the primary key column to ensure CTID hasn't changed.
|
60
|
+
use_pg_system_column_ctid: false,
|
61
|
+
delete_ctids_by_partions: false,
|
57
62
|
}.freeze
|
58
63
|
|
59
64
|
def initialize class_names_and_ids: {}, additional_identifiers_by_id: {}, opts: {}
|
@@ -130,49 +135,117 @@ module BulkDependencyEraser
|
|
130
135
|
end
|
131
136
|
|
132
137
|
# @param additional_identifiers [Hash] can be nil, if chosen to be so by implementers
|
138
|
+
# - Key: <ID> (primary_key)
|
139
|
+
# - Val: Hash of column/value pairings
|
133
140
|
def delete_by_klass_and_ids(klass, ids, additional_identifiers:)
|
134
141
|
puts "Deleting #{klass.name}'s IDs: #{ids}" if opts_c.verbose
|
135
142
|
query = klass.unscoped
|
136
143
|
query = custom_scope_for_query(query)
|
137
144
|
|
145
|
+
# Get column-names/keys of any additional identifer columns
|
146
|
+
detected_additional_identifier_columns = additional_identifiers&.values&.flat_map(&:keys)&.uniq || []
|
147
|
+
|
148
|
+
if opts_c.use_pg_system_column_ctid && opts_c.delete_ctids_by_partions && (detected_additional_identifier_columns & ['ctid', 'tableoid::regclass']).size == 2
|
149
|
+
partioned_tables = additional_identifiers.values.pluck('tableoid::regclass').uniq
|
150
|
+
end
|
151
|
+
|
138
152
|
if batching_disabled?
|
139
153
|
puts "Deleting without batching" if opts_c.verbose
|
140
|
-
#
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
154
|
+
# Delete by partioned CTIDs
|
155
|
+
if opts_c.use_pg_system_column_ctid && opts_c.delete_ctids_by_partions && (detected_additional_identifier_columns & ['ctid', 'tableoid::regclass']).size == 2
|
156
|
+
puts "Deleting via CTID and table partitions" if opts_c.verbose
|
157
|
+
partioned_tables.each do |table_partition|
|
158
|
+
identifiers_by_partition = additional_identifiers.filter { |id, identifiers| identifiers['tableoid::regclass'] == table_partition }
|
159
|
+
ctids = identifiers_by_partition.values.pluck('ctid')
|
160
|
+
delete_in_db do
|
161
|
+
sql = <<~SQL
|
162
|
+
DELETE FROM #{table_partition}
|
163
|
+
WHERE ctid IN (#{ctids.map { |c| "'#{c}'" }.join(', ')});
|
164
|
+
SQL
|
165
|
+
# Due to the way we've mocked/aliased the partition table in this gem test, we need to also add the partition name as a where clause.
|
166
|
+
if ENV['TEST_GEM_ENV'] == 'bulk_dependency_eraser'
|
167
|
+
sql.sub!(/;\s*$/, "") # remove terminating semicolon
|
168
|
+
sql << " AND tableoid = '#{table_partition}';"
|
169
|
+
end
|
170
|
+
deleted_count = klass.connection.delete(sql)
|
171
|
+
[deleted_count, sql, identifiers_by_partition.keys, identifiers_by_partition]
|
172
|
+
end
|
147
173
|
end
|
148
|
-
|
149
|
-
# Perform Deletion
|
150
|
-
deletion_result = deletion_query.delete_all
|
151
|
-
# Returning the following data in the event that the gem-implementer wants to insert their own db_delete_wrapper proc
|
152
|
-
# and have access to these objects in their proc.
|
153
|
-
# - query can give them access to the klass and table_name
|
154
|
-
[deletion_result, query, ids, additional_identifiers]
|
155
|
-
end
|
156
|
-
else
|
157
|
-
puts "Deleting with batching" if opts_c.verbose
|
158
|
-
ids.each_slice(batch_size) do |ids_subset|
|
159
|
-
additional_identifiers_subset = additional_identifiers&.slice(*ids_subset)
|
160
|
-
# Get column-names/keys of any additional identifer columns
|
161
|
-
detected_additional_identifier_columns = additional_identifiers_subset&.values&.flat_map(&:keys)&.uniq || []
|
174
|
+
else
|
162
175
|
delete_in_db do
|
163
|
-
deletion_query = query
|
176
|
+
deletion_query = query
|
177
|
+
if opts_c.use_ctid_over_primary_key && detected_additional_identifier_columns.include?('ctid')
|
178
|
+
# Do nothing, query will be deleted via ctid
|
179
|
+
else
|
180
|
+
deletion_query = deletion_query.where(id: ids)
|
181
|
+
end
|
182
|
+
|
164
183
|
# Apply any additional query identifiers (i.e. :ctid column)
|
165
184
|
detected_additional_identifier_columns.each do |column|
|
166
|
-
deletion_query = deletion_query.where(column =>
|
185
|
+
deletion_query = deletion_query.where(column => additional_identifiers.values.pluck(column))
|
167
186
|
end
|
168
187
|
|
169
188
|
# Perform Deletion
|
170
189
|
deletion_result = deletion_query.delete_all
|
171
|
-
|
172
190
|
# Returning the following data in the event that the gem-implementer wants to insert their own db_delete_wrapper proc
|
173
191
|
# and have access to these objects in their proc.
|
174
|
-
|
175
|
-
[deletion_result, query,
|
192
|
+
# - query can give them access to the klass and table_name
|
193
|
+
[deletion_result, query, ids, additional_identifiers]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
else
|
197
|
+
puts "Deleting with batching" if opts_c.verbose
|
198
|
+
if opts_c.use_pg_system_column_ctid && opts_c.delete_ctids_by_partions && (detected_additional_identifier_columns & ['ctid', 'tableoid::regclass']).size == 2
|
199
|
+
puts "Deleting via CTID and table partitions" if opts_c.verbose
|
200
|
+
partioned_tables.each do |table_partition|
|
201
|
+
identifiers_by_partition = additional_identifiers.filter { |id, identifiers| identifiers['tableoid::regclass'] == table_partition }
|
202
|
+
ids = identifiers_by_partition.keys
|
203
|
+
ids.each_slice(batch_size) do |ids_subset|
|
204
|
+
additional_identifiers_subset = identifiers_by_partition.slice(*ids_subset)
|
205
|
+
ctids = additional_identifiers_subset.values.pluck('ctid')
|
206
|
+
delete_in_db do
|
207
|
+
sql = <<~SQL
|
208
|
+
DELETE FROM #{table_partition}
|
209
|
+
WHERE ctid IN (#{ctids.map { |c| "'#{c}'" }.join(', ')});
|
210
|
+
SQL
|
211
|
+
|
212
|
+
# Due to the way we've mocked/aliased the partition table in this gem test, we need to also add the partition name as a where clause.
|
213
|
+
if ENV['TEST_GEM_ENV'] == 'bulk_dependency_eraser'
|
214
|
+
sql.sub!(/;\s*$/, "") # remove terminating semicolon
|
215
|
+
sql << " AND tableoid = '#{table_partition}';"
|
216
|
+
end
|
217
|
+
|
218
|
+
deleted_count = klass.connection.delete(sql)
|
219
|
+
[deleted_count, sql, ids_subset, additional_identifiers_subset]
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
else
|
224
|
+
ids.each_slice(batch_size) do |ids_subset|
|
225
|
+
additional_identifiers_subset = additional_identifiers&.slice(*ids_subset)
|
226
|
+
# Get column-names/keys of any additional identifer columns
|
227
|
+
detected_additional_identifier_columns = additional_identifiers_subset&.values&.flat_map(&:keys)&.uniq || []
|
228
|
+
delete_in_db do
|
229
|
+
deletion_query = query
|
230
|
+
if opts_c.use_ctid_over_primary_key && detected_additional_identifier_columns.include?('ctid')
|
231
|
+
# Do nothing, query will be deleted via ctid
|
232
|
+
else
|
233
|
+
deletion_query = deletion_query.where(id: ids_subset)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Apply any additional query identifiers (i.e. :ctid column)
|
237
|
+
detected_additional_identifier_columns.each do |column|
|
238
|
+
deletion_query = deletion_query.where(column => additional_identifiers_subset.values.pluck(column))
|
239
|
+
end
|
240
|
+
|
241
|
+
# Perform Deletion
|
242
|
+
deletion_result = deletion_query.delete_all
|
243
|
+
|
244
|
+
# Returning the following data in the event that the gem-implementer wants to insert their own db_delete_wrapper proc
|
245
|
+
# and have access to these objects in their proc.
|
246
|
+
# - query can give them access to the klass and table_name
|
247
|
+
[deletion_result, query, ids_subset, additional_identifiers_subset]
|
248
|
+
end
|
176
249
|
end
|
177
250
|
end
|
178
251
|
end
|