bulk_dependency_eraser 4.3.0 → 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 +92 -32
- 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
|
@@ -55,6 +55,10 @@ module BulkDependencyEraser
|
|
55
55
|
# - 1st priority of scopes
|
56
56
|
deletion_proc_scopes_per_class_name: {},
|
57
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,
|
58
62
|
}.freeze
|
59
63
|
|
60
64
|
def initialize class_names_and_ids: {}, additional_identifiers_by_id: {}, opts: {}
|
@@ -131,61 +135,117 @@ module BulkDependencyEraser
|
|
131
135
|
end
|
132
136
|
|
133
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
|
134
140
|
def delete_by_klass_and_ids(klass, ids, additional_identifiers:)
|
135
141
|
puts "Deleting #{klass.name}'s IDs: #{ids}" if opts_c.verbose
|
136
142
|
query = klass.unscoped
|
137
143
|
query = custom_scope_for_query(query)
|
138
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
|
+
|
139
152
|
if batching_disabled?
|
140
153
|
puts "Deleting without batching" if opts_c.verbose
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
154
173
|
end
|
155
|
-
|
156
|
-
# Perform Deletion
|
157
|
-
deletion_result = deletion_query.delete_all
|
158
|
-
# Returning the following data in the event that the gem-implementer wants to insert their own db_delete_wrapper proc
|
159
|
-
# and have access to these objects in their proc.
|
160
|
-
# - query can give them access to the klass and table_name
|
161
|
-
[deletion_result, query, ids, additional_identifiers]
|
162
|
-
end
|
163
|
-
else
|
164
|
-
puts "Deleting with batching" if opts_c.verbose
|
165
|
-
ids.each_slice(batch_size) do |ids_subset|
|
166
|
-
additional_identifiers_subset = additional_identifiers&.slice(*ids_subset)
|
167
|
-
# Get column-names/keys of any additional identifer columns
|
168
|
-
detected_additional_identifier_columns = additional_identifiers_subset&.values&.flat_map(&:keys)&.uniq || []
|
174
|
+
else
|
169
175
|
delete_in_db do
|
170
176
|
deletion_query = query
|
171
177
|
if opts_c.use_ctid_over_primary_key && detected_additional_identifier_columns.include?('ctid')
|
172
178
|
# Do nothing, query will be deleted via ctid
|
173
179
|
else
|
174
|
-
deletion_query = deletion_query.where(id:
|
180
|
+
deletion_query = deletion_query.where(id: ids)
|
175
181
|
end
|
176
182
|
|
177
183
|
# Apply any additional query identifiers (i.e. :ctid column)
|
178
184
|
detected_additional_identifier_columns.each do |column|
|
179
|
-
deletion_query = deletion_query.where(column =>
|
185
|
+
deletion_query = deletion_query.where(column => additional_identifiers.values.pluck(column))
|
180
186
|
end
|
181
187
|
|
182
188
|
# Perform Deletion
|
183
189
|
deletion_result = deletion_query.delete_all
|
184
|
-
|
185
190
|
# Returning the following data in the event that the gem-implementer wants to insert their own db_delete_wrapper proc
|
186
191
|
# and have access to these objects in their proc.
|
187
|
-
|
188
|
-
[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
|
189
249
|
end
|
190
250
|
end
|
191
251
|
end
|