Class: ExampleRecordDestroyer
- Inherits:
-
Struct
- Object
- Struct
- ExampleRecordDestroyer
- Defined in:
- lib/sq/dbsync/example_record_destroyer.rb
Overview
An example class that can reconstruct deletes from an audit log. We use the audit table as a proxy, though this is not written to in the same transaction as the destroy so it may arrive some time later.
A faux-table is added to the sync times metadata “record_deletes” to make this process resilient to replication failures in either table.
This is an example implementation, you will need to modify it to suit your purposes.
Instance Attribute Summary collapse
-
#audit_table ⇒ Object
Returns the value of attribute audit_table.
-
#db ⇒ Object
Returns the value of attribute db.
-
#other_table ⇒ Object
Returns the value of attribute other_table.
-
#registry ⇒ Object
Returns the value of attribute registry.
Class Method Summary collapse
Instance Method Summary collapse
- #extract_deletes(audit_logs) ⇒ Object
- #last_sync_time(table) ⇒ Object
-
#last_value_set(xs) ⇒ Object
updated_at is not distinct, so use id column as a tie-break.
- #meta_table ⇒ Object
- #run ⇒ Object
- #unprocessed_audit_logs(max) ⇒ Object
Instance Attribute Details
#audit_table ⇒ Object
Returns the value of attribute audit_table
10 11 12 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 10 def audit_table @audit_table end |
#db ⇒ Object
Returns the value of attribute db
10 11 12 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 10 def db @db end |
#other_table ⇒ Object
Returns the value of attribute other_table
10 11 12 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 10 def other_table @other_table end |
#registry ⇒ Object
Returns the value of attribute registry
10 11 12 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 10 def registry @registry end |
Class Method Details
.run(*args) ⇒ Object
14 15 16 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 14 def self.run(*args) new(*args).run end |
Instance Method Details
#extract_deletes(audit_logs) ⇒ Object
41 42 43 44 45 46 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 41 def extract_deletes(audit_logs) audit_logs. group_by {|x| x[:target_id] }. select {|_, xs| last_value_set(xs) == 'false' }. keys end |
#last_sync_time(table) ⇒ Object
63 64 65 66 67 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 63 def last_sync_time(table) record = registry.get(table) (record || {}).fetch(:last_synced_at, nil) end |
#last_value_set(xs) ⇒ Object
updated_at is not distinct, so use id column as a tie-break.
70 71 72 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 70 def last_value_set(xs) xs.sort_by {|y| [y[:updated_at], y[:id]] }.last[:new_value] end |
#meta_table ⇒ Object
74 75 76 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 74 def :"#{other_table}_deletes" end |
#run ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 18 def run max = last_sync_time(audit_table) if max user_ids = extract_deletes(unprocessed_audit_logs(max)) # This conditional should not be required, but MySQL cannot optimize the # impossible where clause correctly and instead scans the table. if user_ids.any? db[other_table].filter( user_id: user_ids ).delete end # last_row_at calculation isn't correct but we don't use it. registry.set!(, last_synced_at: max, last_row_at: max, last_batch_synced_at: nil ) end end |
#unprocessed_audit_logs(max) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/sq/dbsync/example_record_destroyer.rb', line 48 def unprocessed_audit_logs(max) query = db[audit_table]. select(:target_id, :new_value, :updated_at). filter('updated_at <= ?', max). filter(action_name: %w(delete)) min = last_sync_time() if min query = query.filter('updated_at > ?', min) end query.to_a end |