waldit 0.0.4 → 0.0.5

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: 81634f3ce5d00503e50882a14495a960dfc8fb1072b53c92e3cd2a08441666c9
4
- data.tar.gz: b235c7ee87fda30c747db3a678a3b9f6c7b5f5b95861f998b037a63418bee29b
3
+ metadata.gz: 8b76edf8a202620aff6e09391009e6c00ae0f26a404d1b81741c5cd3e339db66
4
+ data.tar.gz: f0c7582d6af476ecf536aa4e96846eea620b8c3bd5c215c240d9e68056ade643
5
5
  SHA512:
6
- metadata.gz: ad16ce87f467600879c9358540143cb1ad4eeeb1fe278472647aad21e28db4420a5238b13e6d70c02a65014d72251d54acfdfb0fe6c448191644871a28d4ef45
7
- data.tar.gz: e932e94f85304d7b8f756a5fce3a3ba0e2c07ca302766e0f8ff3563b4acac9e14b7943ae0bcc3030ee68567571315e635fa172c02c945e70d68e45c46fba4d41
6
+ metadata.gz: 5aee8ef8ff8c9f0b6fc636b866a9fe72a08c1ba98e507df1b7d3771339130808c04c9fb32345a07a12416967d386be92bdfe3bfd1f3215dc723f337baf06278b
7
+ data.tar.gz: e5f139662394131f2bfbe1af5ac3df85fa7b6202756ec67c02f0e679d11c78e2a62ecdb3a5bb188fa20a25ddee23699824266831f86b09e3284c0ad2584e9d6b
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Waldit
4
- VERSION = "0.0.4"
4
+ VERSION = "0.0.5"
5
5
  end
@@ -9,85 +9,61 @@ module Waldit
9
9
  def audit_event(event)
10
10
  return unless event.primary_key
11
11
 
12
- audit = {
13
- transaction_id: event.transaction_id,
14
- lsn: event.lsn,
15
- context: event.context,
16
- table_name: event.table,
17
- primary_key: event.primary_key,
18
- }
19
-
20
- unique_by = %i[table_name primary_key transaction_id]
12
+ audit = [event.transaction_id, event.lsn, event.table, event.primary_key, event.context.to_json]
21
13
 
22
14
  case event
23
15
  when InsertEvent
24
- record.upsert(
25
- audit.merge(action: "insert", new: event.new),
26
- unique_by:,
27
- on_duplicate: :update,
28
- )
16
+ @connection.exec_prepared("waldit_insert", audit + [event.new.to_json])
29
17
 
30
18
  when UpdateEvent
31
19
  return if event.diff.without(ignored_columns(event.table)).empty?
32
- record.upsert(
33
- audit.merge(action: "update", old: event.old, new: event.new),
34
- unique_by:,
35
- on_duplicate: :update,
36
- update_only: %w[new],
37
- )
20
+ @connection.exec_prepared("waldit_update", audit + [event.old.to_json, event.new.to_json])
38
21
 
39
22
  when DeleteEvent
40
- case record.where(audit.slice(*unique_by)).pluck(:action, :old).first
41
- in ["insert", _]
42
- # We are deleting a record that was inserted on this transaction, which means we don't need to audit anything,
43
- # as the record was never commited
44
- record.where(audit.slice(*unique_by)).delete_all
45
-
46
- in ["update", old]
47
- # We are deleting a record we updated on this transaction. Here we are making sure we keep the correct previous
48
- # state, and not the state at the moment of the deletion
49
- record.upsert(
50
- audit.merge(action: "delete", old:, new: {}),
51
- unique_by:,
52
- on_duplicate: :update,
53
- )
54
-
55
- in ["delete", _]
56
- # This should never happend, we wouldn't be able to delete a record that was already deleted on this transaction
57
-
23
+ case @connection.exec_prepared("waldit_delete_cleanup", [event.transaction_id, event.table, event.primary_key]).values
24
+ in [["update", previous_old]]
25
+ @connection.exec_prepared("waldit_delete", audit + [previous_old])
26
+ in []
27
+ @connection.exec_prepared("waldit_delete", audit + [event.old.to_json])
58
28
  else
59
- # Finally the most common case: just deleting a record not created or updated on this transaction
60
- record.upsert(
61
- audit.merge(action: "delete", old: event.old),
62
- unique_by:,
63
- on_duplicate: :update,
64
- )
29
+ # Don't need to audit anything on this case
65
30
  end
66
31
  end
67
32
  end
68
33
 
69
34
  def on_transaction_events(events)
70
- counter = 0
71
- catch :finish do
72
- loop do
73
- record.transaction do
74
- events.each do |event|
75
- case event
76
- when CommitTransactionEvent
77
- record
78
- .where(transaction_id: event.transaction_id)
79
- .update_all(commited_at: event.timestamp) if counter > 0
80
- # Using throw to break the outside loop and finish the thread gracefully
81
- throw :finish
82
-
83
- when InsertEvent, UpdateEvent, DeleteEvent
84
- audit_event(event)
85
-
86
- counter += 1
87
- # We break here to force a commit, so we don't keep a single big transaction pending
88
- break if counter % max_transaction_size == 0
89
- end
35
+ record.transaction do
36
+ @connection = record.connection.raw_connection
37
+ insert_prepared = false
38
+ update_prepared = false
39
+ delete_prepared = false
40
+
41
+ events.each do |event|
42
+ case event
43
+ when CommitTransactionEvent
44
+ record.where(transaction_id: event.transaction_id).update_all(commited_at: event.timestamp)
45
+
46
+ when InsertEvent
47
+ unless insert_prepared
48
+ prepare_insert
49
+ insert_prepared = true
50
+ end
51
+ audit_event(event)
52
+
53
+ when UpdateEvent
54
+ unless update_prepared
55
+ prepare_update
56
+ update_prepared = true
90
57
  end
58
+ audit_event(event)
59
+
60
+ when DeleteEvent
61
+ unless delete_prepared
62
+ prepare_delete
63
+ prepare_delete_cleanup
64
+ delete_prepared = true
65
+ end
66
+ audit_event(event)
91
67
  end
92
68
  end
93
69
  end
@@ -112,5 +88,46 @@ module Waldit
112
88
  def record
113
89
  Waldit.model
114
90
  end
91
+
92
+ private
93
+
94
+ def prepare_insert
95
+ @connection.prepare("waldit_insert", <<~SQL)
96
+ INSERT INTO #{record.table_name} (transaction_id, lsn, table_name, primary_key, action, context, new)
97
+ VALUES ($1, $2, $3, $4, 'insert'::waldit_action, $5, $6)
98
+ ON CONFLICT (table_name, primary_key, transaction_id)
99
+ DO UPDATE SET new = #{record.table_name}.new
100
+ SQL
101
+ end
102
+
103
+ def prepare_update
104
+ @connection.prepare("waldit_update", <<~SQL)
105
+ INSERT INTO #{record.table_name} (transaction_id, lsn, table_name, primary_key, action, context, old, new)
106
+ VALUES ($1, $2, $3, $4, 'update'::waldit_action, $5, $6, $7)
107
+ ON CONFLICT (table_name, primary_key, transaction_id)
108
+ DO UPDATE SET new = excluded.new
109
+ SQL
110
+ end
111
+
112
+ def prepare_delete
113
+ @connection.prepare("waldit_delete", <<~SQL)
114
+ INSERT INTO #{record.table_name} (transaction_id, lsn, table_name, primary_key, action, context, old, new)
115
+ VALUES ($1, $2, $3, $4, 'delete'::waldit_action, $5, $6, '{}'::jsonb)
116
+ ON CONFLICT (table_name, primary_key, transaction_id)
117
+ DO UPDATE SET old = #{record.table_name}.old
118
+ SQL
119
+ end
120
+
121
+ def prepare_delete_cleanup
122
+ @connection.prepare("waldit_delete_cleanup", <<~SQL)
123
+ DELETE FROM #{record.table_name}
124
+ WHERE
125
+ transaction_id = $1
126
+ AND table_name = $2
127
+ AND primary_key = $3
128
+ AND action IN ('insert'::waldit_action, 'update'::waldit_action)
129
+ RETURNING action, old
130
+ SQL
131
+ end
115
132
  end
116
133
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waldit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Navarro
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-08-02 00:00:00.000000000 Z
10
+ date: 2025-08-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: wal