wal 0.0.2 → 0.0.3

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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -7
  3. data/lib/wal/noop_watcher.rb +0 -4
  4. data/lib/wal/record_watcher.rb +4 -58
  5. data/lib/wal/replicator.rb +2 -21
  6. data/lib/wal/streaming_watcher.rb +0 -9
  7. data/lib/wal/version.rb +1 -2
  8. data/lib/wal/watcher.rb +0 -25
  9. data/lib/wal.rb +5 -64
  10. data/rbi/wal.rbi +1 -81
  11. metadata +2 -161
  12. data/sorbet/config +0 -7
  13. data/sorbet/rbi/annotations/.gitattributes +0 -1
  14. data/sorbet/rbi/annotations/activemodel.rbi +0 -89
  15. data/sorbet/rbi/annotations/activerecord.rbi +0 -98
  16. data/sorbet/rbi/annotations/activesupport.rbi +0 -463
  17. data/sorbet/rbi/annotations/minitest.rbi +0 -119
  18. data/sorbet/rbi/annotations/rainbow.rbi +0 -269
  19. data/sorbet/rbi/gems/.gitattributes +0 -1
  20. data/sorbet/rbi/gems/actioncable@8.0.2.rbi +0 -9
  21. data/sorbet/rbi/gems/actionmailbox@8.0.2.rbi +0 -9
  22. data/sorbet/rbi/gems/actionmailer@8.0.2.rbi +0 -9
  23. data/sorbet/rbi/gems/actionpack@8.0.2.rbi +0 -21122
  24. data/sorbet/rbi/gems/actiontext@8.0.2.rbi +0 -9
  25. data/sorbet/rbi/gems/actionview@8.0.2.rbi +0 -16423
  26. data/sorbet/rbi/gems/activejob@8.0.2.rbi +0 -9
  27. data/sorbet/rbi/gems/activemodel@8.0.2.rbi +0 -6866
  28. data/sorbet/rbi/gems/activerecord@8.0.2.rbi +0 -43227
  29. data/sorbet/rbi/gems/activestorage@8.0.2.rbi +0 -9
  30. data/sorbet/rbi/gems/activesupport@8.0.2.rbi +0 -21110
  31. data/sorbet/rbi/gems/ast@2.4.3.rbi +0 -585
  32. data/sorbet/rbi/gems/base64@0.3.0.rbi +0 -545
  33. data/sorbet/rbi/gems/benchmark@0.4.1.rbi +0 -619
  34. data/sorbet/rbi/gems/bigdecimal@3.2.2.rbi +0 -78
  35. data/sorbet/rbi/gems/builder@3.3.0.rbi +0 -9
  36. data/sorbet/rbi/gems/commander@5.0.0.rbi +0 -9
  37. data/sorbet/rbi/gems/concurrent-ruby@1.3.5.rbi +0 -11657
  38. data/sorbet/rbi/gems/connection_pool@2.5.3.rbi +0 -9
  39. data/sorbet/rbi/gems/crass@1.0.6.rbi +0 -623
  40. data/sorbet/rbi/gems/date@3.4.1.rbi +0 -75
  41. data/sorbet/rbi/gems/diff-lcs@1.6.2.rbi +0 -1134
  42. data/sorbet/rbi/gems/docker-api@2.4.0.rbi +0 -1719
  43. data/sorbet/rbi/gems/docopt@0.6.1.rbi +0 -9
  44. data/sorbet/rbi/gems/drb@2.2.3.rbi +0 -1661
  45. data/sorbet/rbi/gems/erubi@1.13.1.rbi +0 -155
  46. data/sorbet/rbi/gems/excon@1.2.7.rbi +0 -1514
  47. data/sorbet/rbi/gems/globalid@1.2.1.rbi +0 -9
  48. data/sorbet/rbi/gems/highline@3.0.1.rbi +0 -9
  49. data/sorbet/rbi/gems/i18n@1.14.7.rbi +0 -2359
  50. data/sorbet/rbi/gems/io-console@0.8.0.rbi +0 -9
  51. data/sorbet/rbi/gems/logger@1.7.0.rbi +0 -963
  52. data/sorbet/rbi/gems/loofah@2.24.1.rbi +0 -1105
  53. data/sorbet/rbi/gems/mail@2.8.1.rbi +0 -9
  54. data/sorbet/rbi/gems/marcel@1.0.4.rbi +0 -9
  55. data/sorbet/rbi/gems/mini_mime@1.1.5.rbi +0 -9
  56. data/sorbet/rbi/gems/minitest@5.25.5.rbi +0 -1704
  57. data/sorbet/rbi/gems/multi_json@1.15.0.rbi +0 -268
  58. data/sorbet/rbi/gems/net-imap@0.5.9.rbi +0 -9
  59. data/sorbet/rbi/gems/net-pop@0.1.2.rbi +0 -9
  60. data/sorbet/rbi/gems/net-protocol@0.2.2.rbi +0 -292
  61. data/sorbet/rbi/gems/net-smtp@0.5.1.rbi +0 -9
  62. data/sorbet/rbi/gems/netrc@0.11.0.rbi +0 -159
  63. data/sorbet/rbi/gems/nio4r@2.7.4.rbi +0 -9
  64. data/sorbet/rbi/gems/nokogiri@1.18.8.rbi +0 -8206
  65. data/sorbet/rbi/gems/ostruct@0.6.2.rbi +0 -354
  66. data/sorbet/rbi/gems/parallel@1.27.0.rbi +0 -291
  67. data/sorbet/rbi/gems/parlour@9.1.1.rbi +0 -3071
  68. data/sorbet/rbi/gems/parser@3.3.8.0.rbi +0 -7338
  69. data/sorbet/rbi/gems/pg-replication-protocol@0.0.7.rbi +0 -633
  70. data/sorbet/rbi/gems/pg@1.5.9.rbi +0 -2806
  71. data/sorbet/rbi/gems/pp@0.6.2.rbi +0 -368
  72. data/sorbet/rbi/gems/prettyprint@0.2.0.rbi +0 -477
  73. data/sorbet/rbi/gems/prism@1.4.0.rbi +0 -41732
  74. data/sorbet/rbi/gems/psych@5.2.3.rbi +0 -2435
  75. data/sorbet/rbi/gems/racc@1.8.1.rbi +0 -160
  76. data/sorbet/rbi/gems/rack-session@2.1.1.rbi +0 -727
  77. data/sorbet/rbi/gems/rack-test@2.2.0.rbi +0 -734
  78. data/sorbet/rbi/gems/rack@3.1.16.rbi +0 -4940
  79. data/sorbet/rbi/gems/rackup@2.2.1.rbi +0 -230
  80. data/sorbet/rbi/gems/rails-dom-testing@2.3.0.rbi +0 -858
  81. data/sorbet/rbi/gems/rails-html-sanitizer@1.6.2.rbi +0 -785
  82. data/sorbet/rbi/gems/rails@8.0.2.rbi +0 -9
  83. data/sorbet/rbi/gems/railties@8.0.2.rbi +0 -3865
  84. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +0 -403
  85. data/sorbet/rbi/gems/rake@13.2.1.rbi +0 -3120
  86. data/sorbet/rbi/gems/rbi@0.3.6.rbi +0 -6893
  87. data/sorbet/rbi/gems/rbs@3.9.4.rbi +0 -6978
  88. data/sorbet/rbi/gems/rdoc@6.12.0.rbi +0 -12760
  89. data/sorbet/rbi/gems/reline@0.6.0.rbi +0 -2451
  90. data/sorbet/rbi/gems/rexml@3.4.1.rbi +0 -5240
  91. data/sorbet/rbi/gems/rspec-core@3.13.4.rbi +0 -11348
  92. data/sorbet/rbi/gems/rspec-expectations@3.13.5.rbi +0 -8189
  93. data/sorbet/rbi/gems/rspec-mocks@3.13.5.rbi +0 -5350
  94. data/sorbet/rbi/gems/rspec-sorbet@1.9.2.rbi +0 -164
  95. data/sorbet/rbi/gems/rspec-support@3.13.4.rbi +0 -1630
  96. data/sorbet/rbi/gems/rspec@3.13.1.rbi +0 -83
  97. data/sorbet/rbi/gems/securerandom@0.4.1.rbi +0 -75
  98. data/sorbet/rbi/gems/spoom@1.6.3.rbi +0 -6985
  99. data/sorbet/rbi/gems/stringio@3.1.5.rbi +0 -9
  100. data/sorbet/rbi/gems/tapioca@0.16.11.rbi +0 -3628
  101. data/sorbet/rbi/gems/testcontainers-core@0.2.0.rbi +0 -1005
  102. data/sorbet/rbi/gems/testcontainers-postgres@0.2.0.rbi +0 -145
  103. data/sorbet/rbi/gems/thor@1.3.2.rbi +0 -4378
  104. data/sorbet/rbi/gems/timeout@0.4.3.rbi +0 -157
  105. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +0 -5918
  106. data/sorbet/rbi/gems/uri@1.0.3.rbi +0 -2349
  107. data/sorbet/rbi/gems/useragent@0.16.11.rbi +0 -9
  108. data/sorbet/rbi/gems/websocket-driver@0.8.0.rbi +0 -9
  109. data/sorbet/rbi/gems/websocket-extensions@0.1.5.rbi +0 -9
  110. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
  111. data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18379
  112. data/sorbet/rbi/gems/zeitwerk@2.7.3.rbi +0 -9
  113. data/sorbet/tapioca/config.yml +0 -5
  114. data/sorbet/tapioca/require.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc5df8a8a67077b8c9a4da905a102e72bf4b1e6e41ad2569a4662038d81d123a
4
- data.tar.gz: dc5dd65b5b87f43b0b1b4bc7d7d66b2b97fa92fd04963bbbdb32f2e781f4bcaf
3
+ metadata.gz: 3df0dd993bf343f90d54d4b60f52504ca2a84d4fd839c5c952e6f866b40a46cd
4
+ data.tar.gz: 8e8b568f3a26ed687e9010bd9c69ea4a656e6af59be20449a9142b61c61fe4ad
5
5
  SHA512:
6
- metadata.gz: 0e1b415b21cef101a2493ed922b95e8e70e972575bb6bfc190200aee567a51a4582a26b29f07eca316621ae48ec4b63e995e718cec3a963ffd42fb6774c40ee1
7
- data.tar.gz: 940d945bafe3f36dd303a427bbe25524f50a98ac88a76f2ef02e842cfe30b190ba87c047a15256c5b7f80408ee211bda08beb7b6bb37bb93325540fd5ad6e4e7
6
+ metadata.gz: '09591c7506a7b29d9261574f9a512f120862b58d08ae71d31501147076bbae7f9ab65c9c0ded4fe3c15ddfaa9ec1881ebebf934069c08847d8b486d4fcdfc6f7'
7
+ data.tar.gz: c09c1417b8021fde2890f9dd38d32cec9e64e9fec6d1341613b2a8c4522edd60c0ae34e78fdee3c45e8257c6be66d480fb8ad5f3d52fcec5fb0955d79c054a63
data/Rakefile CHANGED
@@ -3,10 +3,3 @@
3
3
  require "bundler/gem_tasks"
4
4
 
5
5
  task(:test) { sh "bundle exec rspec" }
6
-
7
- task default: %i[build]
8
-
9
- task("sig/wal.rbi") { sh "bundle exec parlour" }
10
- task("rbi/wal.rbs") { sh "rbs prototype rbi rbi/wal.rbi > sig/wal.rbs" }
11
-
12
- Rake::Task["build"].enhance(["sig/wal.rbi", "rbi/wal.rbs"])
@@ -1,12 +1,8 @@
1
- # typed: strict
2
-
3
1
  module Wal
4
2
  # A watcher that does nothing. Just for performance testing in general. Useful in testing aswell.
5
3
  class NoopWatcher
6
- extend T::Sig
7
4
  include Wal::Watcher
8
5
 
9
- sig { override.params(event: Event).void }
10
6
  def on_event(event); end
11
7
  end
12
8
  end
@@ -1,5 +1,3 @@
1
- # typed: true
2
-
3
1
  module Wal
4
2
  # Watcher that process records at the end of a transaction, keeping only its final state.
5
3
  #
@@ -39,12 +37,7 @@ module Wal
39
37
  # end
40
38
  # ```
41
39
  class RecordWatcher
42
- extend T::Sig
43
- extend T::Helpers
44
40
  include Wal::Watcher
45
- abstract!
46
-
47
- RecordEvent = T.type_alias { T.any(InsertEvent, UpdateEvent, DeleteEvent) }
48
41
 
49
42
  def self.inherited(subclass)
50
43
  super
@@ -52,53 +45,26 @@ module Wal
52
45
  @@delete_callbacks = Hash.new { |hash, key| hash[key] = [] }
53
46
  end
54
47
 
55
- sig do
56
- params(
57
- table: T.any(String, T.class_of(::ActiveRecord::Base)),
58
- block: T.proc.bind(T.attached_class).params(event: InsertEvent).void,
59
- ).void
60
- end
61
48
  def self.on_insert(table, &block)
62
49
  table = table.is_a?(String) ? table : table.table_name
63
50
  @@change_callbacks[table].push(only: [:create], block: block)
64
51
  end
65
52
 
66
- sig do
67
- params(
68
- table: T.any(String, T.class_of(::ActiveRecord::Base)),
69
- changed: T.nilable(T::Array[T.any(String, Symbol)]),
70
- block: T.proc.bind(T.attached_class).params(event: UpdateEvent).void,
71
- ).void
72
- end
73
53
  def self.on_update(table, changed: nil, &block)
74
54
  table = table.is_a?(String) ? table : table.table_name
75
55
  @@change_callbacks[table].push(only: [:update], changed: changed&.map(&:to_s), block: block)
76
56
  end
77
57
 
78
- sig do
79
- params(
80
- table: T.any(String, T.class_of(::ActiveRecord::Base)),
81
- changed: T.nilable(T::Array[T.any(String, Symbol)]),
82
- block: T.proc.bind(T.attached_class).params(event: T.any(InsertEvent, UpdateEvent)).void,
83
- ).void
84
- end
85
58
  def self.on_save(table, changed: nil, &block)
86
59
  table = table.is_a?(String) ? table : table.table_name
87
60
  @@change_callbacks[table].push(only: [:create, :update], changed: changed&.map(&:to_s), block: block)
88
61
  end
89
62
 
90
- sig do
91
- params(
92
- table: T.any(String, T.class_of(::ActiveRecord::Base)),
93
- block: T.proc.bind(T.attached_class).params(event: DeleteEvent).void,
94
- ).void
95
- end
96
63
  def self.on_destroy(table, &block)
97
64
  table = table.is_a?(String) ? table : table.table_name
98
65
  @@delete_callbacks[table].push(block: block)
99
66
  end
100
67
 
101
- sig { params(event: RecordEvent).void }
102
68
  def on_record_changed(event)
103
69
  case event
104
70
  when InsertEvent
@@ -124,7 +90,6 @@ module Wal
124
90
  end
125
91
  end
126
92
 
127
- sig { params(table: String).returns(T::Boolean) }
128
93
  def should_watch_table?(table)
129
94
  (@@change_callbacks.keys | @@delete_callbacks.keys).include? table
130
95
  end
@@ -139,7 +104,6 @@ module Wal
139
104
  #
140
105
  # These strategies can be defined per transaction, and by default it will uses the memory one, and only fallback
141
106
  # to the temporary table if the transaction size is roughly 2 gigabytes or more.
142
- sig { params(event: BeginTransactionEvent).returns(Symbol) }
143
107
  def aggregation_strategy(event)
144
108
  if event.estimated_size > 1024.pow(3) * 2
145
109
  :temporary_table
@@ -148,7 +112,6 @@ module Wal
148
112
  end
149
113
  end
150
114
 
151
- sig { override.params(event: Event).void }
152
115
  def on_event(event)
153
116
  if event.is_a? BeginTransactionEvent
154
117
  @current_record_watcher = case (strategy = aggregation_strategy(event))
@@ -164,21 +127,15 @@ module Wal
164
127
  end
165
128
 
166
129
  class MemoryRecordWatcher
167
- extend T::Sig
168
- extend T::Helpers
169
130
  include Wal::Watcher
170
131
  include Wal::Watcher::SeparatedEvents
171
132
 
172
- # Records indexed by table and primary key
173
- RecordsStorage = T.type_alias { T::Hash[[String, Integer], T.nilable(RecordEvent)] }
174
-
175
133
  def initialize(watcher)
176
134
  @watcher = watcher
177
135
  end
178
136
 
179
- sig { params(event: BeginTransactionEvent).void }
180
137
  def on_begin(event)
181
- @records = T.let({}, T.nilable(RecordsStorage))
138
+ @records = {}
182
139
  end
183
140
 
184
141
  def on_commit(_event)
@@ -188,7 +145,6 @@ module Wal
188
145
  &.each { |event| @watcher.on_record_changed(event) if event }
189
146
  end
190
147
 
191
- sig { params(event: InsertEvent).void }
192
148
  def on_insert(event)
193
149
  if (id = event.primary_key)
194
150
  @records ||= {}
@@ -196,7 +152,6 @@ module Wal
196
152
  end
197
153
  end
198
154
 
199
- sig { params(event: UpdateEvent).void }
200
155
  def on_update(event)
201
156
  if (id = event.primary_key)
202
157
  @records ||= {}
@@ -217,7 +172,6 @@ module Wal
217
172
  end
218
173
  end
219
174
 
220
- sig { params(event: DeleteEvent).void }
221
175
  def on_delete(event)
222
176
  if (id = event.primary_key)
223
177
  @records ||= {}
@@ -240,8 +194,6 @@ module Wal
240
194
  end
241
195
 
242
196
  class TemporaryTableRecordWatcher
243
- extend T::Sig
244
- extend T::Helpers
245
197
  include Wal::Watcher
246
198
  include Wal::Watcher::SeparatedEvents
247
199
 
@@ -255,7 +207,6 @@ module Wal
255
207
  @batch_size = 5_000
256
208
  end
257
209
 
258
- sig { params(event: BeginTransactionEvent).void }
259
210
  def on_begin(event)
260
211
  @table = begin
261
212
  table_name = "temp_record_watcher_#{SecureRandom.alphanumeric(10).downcase}"
@@ -276,8 +227,7 @@ module Wal
276
227
  base_class.connection.add_index table_name, unique_index, unique: true
277
228
 
278
229
  Class.new(base_class) do
279
- # Using cast here since Sorbet bugs when we don't pass a explicit class to `Class.new`
280
- T.cast(self, T.class_of(::ActiveRecord::Base)).table_name = table_name
230
+ self.table_name = table_name
281
231
 
282
232
  # All this sh$#1t was necessary because AR schema cache doesn't work with temporary tables...
283
233
  insert_all_class = Class.new(::ActiveRecord::InsertAll) do
@@ -290,8 +240,8 @@ module Wal
290
240
  define_singleton_method(:upsert) do |attributes, update_only: nil|
291
241
  insert_all_class
292
242
  .new(
293
- T.cast(self, T.class_of(::ActiveRecord::Base)).none,
294
- T.cast(self, T.class_of(::ActiveRecord::Base)).connection,
243
+ none,
244
+ connection,
295
245
  [attributes],
296
246
  on_duplicate: :update,
297
247
  unique_by: unique_index,
@@ -316,17 +266,14 @@ module Wal
316
266
  base_class.connection.drop_table @table.table_name
317
267
  end
318
268
 
319
- sig { params(event: InsertEvent).void }
320
269
  def on_insert(event)
321
270
  @table.upsert(serialize(event))
322
271
  end
323
272
 
324
- sig { params(event: UpdateEvent).void }
325
273
  def on_update(event)
326
274
  @table.upsert(serialize(event), update_only: %w[new])
327
275
  end
328
276
 
329
- sig { params(event: DeleteEvent).void }
330
277
  def on_delete(event)
331
278
  case @table.where(table_name: event.table, primary_key: event.primary_key).pluck(:action, :old).first
332
279
  in ["insert", _]
@@ -342,7 +289,6 @@ module Wal
342
289
 
343
290
  private
344
291
 
345
- sig { returns(T.class_of(::ActiveRecord::Base)) }
346
292
  def base_class
347
293
  self.class.base_active_record_class || ::ActiveRecord::Base
348
294
  end
@@ -1,21 +1,11 @@
1
- # typed: false
2
-
3
1
  require "ostruct"
4
2
 
5
3
  module Wal
6
4
  # Responsible to hook into a Postgres logical replication slot and stream the changes to a specific `Watcher`.
7
5
  # Also it supports inject "contexts" into the replication events.
8
6
  class Replicator
9
- extend T::Sig
10
7
  include PG::Replication::Protocol
11
8
 
12
- sig do
13
- params(
14
- replication_slot: String,
15
- use_temporary_slot: T::Boolean,
16
- db_config: T::Hash[Symbol, T.untyped],
17
- ).void
18
- end
19
9
  def initialize(
20
10
  replication_slot:,
21
11
  use_temporary_slot: false,
@@ -26,7 +16,6 @@ module Wal
26
16
  @use_temporary_slot = use_temporary_slot
27
17
  end
28
18
 
29
- sig { params(watcher: Watcher, publications: T::Array[String]).void }
30
19
  def replicate_forever(watcher, publications:)
31
20
  replication = replicate(watcher, publications:)
32
21
  loop { replication.next }
@@ -34,7 +23,6 @@ module Wal
34
23
  nil
35
24
  end
36
25
 
37
- sig { params(watcher: Watcher, publications: T::Array[String]).returns(T::Enumerator::Lazy[Event]) }
38
26
  def replicate(watcher, publications:)
39
27
  watch_conn = PG.connect(
40
28
  dbname: @db_config[:database],
@@ -162,20 +150,13 @@ module Wal
162
150
  end
163
151
  end
164
152
 
165
- class Column < T::Struct
166
- const :name, String
167
- const :decoder, T.untyped
168
-
153
+ class Column < Data.define(:name, :decoder)
169
154
  def decode(value)
170
155
  decoder.deserialize(value)
171
156
  end
172
157
  end
173
158
 
174
- class Table < T::Struct
175
- const :name, String
176
- const :primary_key_colums, T::Array[String]
177
- const :columns, T::Array[Column]
178
-
159
+ class Table < Data.define(:name, :primary_key_colums, :columns)
179
160
  def primary_key(decoded_row)
180
161
  case primary_key_colums
181
162
  in [key]
@@ -1,5 +1,3 @@
1
- # typed: true
2
-
3
1
  module Wal
4
2
  # A watcher that streams all the events of each WAL transaction on a separate thread.
5
3
  #
@@ -12,7 +10,6 @@ module Wal
12
10
  #
13
11
  # ```ruby
14
12
  # class RegisterDeletesWalWatcher < Wal::StreamingWalWatcher
15
- # sig { override.params(events: T::Enumerator[Event]).void }
16
13
  # def on_transaction_events(events)
17
14
  # DeletedApplicationRecord.transaction do
18
15
  # events
@@ -24,20 +21,14 @@ module Wal
24
21
  # end
25
22
  # ```
26
23
  class StreamingWatcher
27
- extend T::Sig
28
- extend T::Helpers
29
24
  include Wal::Watcher
30
- abstract!
31
25
 
32
- sig { abstract.params(events: T::Enumerator[Event]).void }
33
26
  def on_transaction_events(events); end
34
27
 
35
- sig { params(event: BeginTransactionEvent).returns(Integer) }
36
28
  def queue_size(event)
37
29
  5_000
38
30
  end
39
31
 
40
- sig { override.params(event: Event).void }
41
32
  def on_event(event)
42
33
  case event
43
34
  when BeginTransactionEvent
data/lib/wal/version.rb CHANGED
@@ -1,6 +1,5 @@
1
- # typed: strict
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Wal
5
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
6
5
  end
data/lib/wal/watcher.rb CHANGED
@@ -1,5 +1,3 @@
1
- # typed: strict
2
-
3
1
  module Wal
4
2
  # Watcher is the core API used to hook into Postgres WAL log.
5
3
  # The only required method on the API is the `on_event`, which will receive a WAL entry of the following events:
@@ -13,22 +11,14 @@ module Wal
13
11
  # desired. In practice, it is rarelly useful to implement this module directly for application level business logic,
14
12
  # and instead it is more recomended using more specific ones, such as the `RecordWatcher` and `StreamingWalWatcher`.
15
13
  module Watcher
16
- extend T::Sig
17
- extend T::Helpers
18
- include Wal
19
- abstract!
20
-
21
- sig { abstract.params(event: Event).void }
22
14
  def on_event(event); end
23
15
 
24
16
  # Allows dropping the processing of any table
25
- sig { params(table: String).returns(T::Boolean) }
26
17
  def should_watch_table?(table)
27
18
  true
28
19
  end
29
20
 
30
21
  # Check if the given context prefix should be allowed for this watcher
31
- sig { params(prefix: String).returns(T::Boolean) }
32
22
  def valid_context_prefix?(prefix)
33
23
  true
34
24
  end
@@ -42,25 +32,19 @@ module Wal
42
32
  #
43
33
  # ```ruby
44
34
  # class MeasureTransactionTimeWatcher
45
- # extend T::Sig
46
35
  # include Wal::Watcher
47
36
  # include Wal::Watcher::SeparatedEvents
48
37
  #
49
- # sig { params(event: BeginTransactionEvent).void }
50
38
  # def on_begin(event)
51
39
  # @start_time = Time.current
52
40
  # end
53
41
  #
54
- # sig { params(event: CommitTransactionEvent).void }
55
42
  # def on_commit(event)
56
43
  # puts "Transaction processing time: #{Time.current - @start_time}"
57
44
  # end
58
45
  # end
59
46
  # ```
60
47
  module SeparatedEvents
61
- extend T::Sig
62
-
63
- sig { params(event: Event).void }
64
48
  def on_event(event)
65
49
  case event
66
50
  when BeginTransactionEvent
@@ -76,19 +60,10 @@ module Wal
76
60
  end
77
61
  end
78
62
 
79
- sig { params(event: BeginTransactionEvent).void }
80
63
  def on_begin(event); end
81
-
82
- sig { params(event: InsertEvent).void }
83
64
  def on_insert(event); end
84
-
85
- sig { params(event: UpdateEvent).void }
86
65
  def on_update(event); end
87
-
88
- sig { params(event: DeleteEvent).void }
89
66
  def on_delete(event); end
90
-
91
- sig { params(event: CommitTransactionEvent).void }
92
67
  def on_commit(event); end
93
68
  end
94
69
  end
data/lib/wal.rb CHANGED
@@ -1,5 +1,3 @@
1
- # typed: strict
2
-
3
1
  require "pg"
4
2
  require "pg/replication"
5
3
  require "active_support"
@@ -13,63 +11,34 @@ require_relative "wal/active_record_context_extension"
13
11
  require_relative "wal/version"
14
12
 
15
13
  module Wal
16
- Event = T.type_alias do
17
- T.any(
18
- BeginTransactionEvent,
19
- CommitTransactionEvent,
20
- InsertEvent,
21
- UpdateEvent,
22
- DeleteEvent,
23
- )
24
- end
25
-
26
- class BeginTransactionEvent < T::Struct
27
- extend T::Sig
28
-
29
- const :transaction_id, Integer
30
- const :lsn, Integer
31
- const :final_lsn, Integer
32
- const :timestamp, Time
33
-
34
- sig { returns(Integer) }
14
+ class BeginTransactionEvent < Data.define(:transaction_id, :lsn, :final_lsn, :timestamp)
35
15
  def estimated_size
36
16
  final_lsn - lsn
37
17
  end
38
18
  end
39
19
 
40
- class CommitTransactionEvent < T::Struct
41
- const :transaction_id, Integer
42
- const :lsn, Integer
43
- const :context, T::Hash[String, T.untyped]
44
- const :timestamp, Time
20
+ class CommitTransactionEvent < Data.define(:transaction_id, :lsn, :context, :timestamp)
45
21
  end
46
22
 
47
23
  module ChangeEvent
48
- extend T::Sig
49
-
50
- sig { returns(T::Hash[String, [T.untyped, T.untyped]]) }
51
24
  def diff
52
25
  {}
53
26
  end
54
27
 
55
- sig { params(attribute: T.any(Symbol, String)).returns(T::Boolean) }
56
28
  def changed_attribute?(attribute)
57
29
  diff.key? attribute.to_s
58
30
  end
59
31
 
60
- sig { params(attribute: T.any(Symbol, String)).returns(T.untyped) }
61
32
  def attribute(attribute)
62
33
  if (changes = diff[attribute.to_s])
63
34
  changes[1]
64
35
  end
65
36
  end
66
37
 
67
- sig { params(attribute: T.any(Symbol, String)).returns(T.nilable([T.untyped, T.untyped])) }
68
38
  def attribute_changes(attribute)
69
39
  diff[attribute.to_s]
70
40
  end
71
41
 
72
- sig { params(attribute: T.any(Symbol, String)).returns(T.untyped) }
73
42
  def attribute_was(attribute)
74
43
  if (changes = diff[attribute.to_s])
75
44
  changes[0]
@@ -77,36 +46,17 @@ module Wal
77
46
  end
78
47
  end
79
48
 
80
- class InsertEvent < T::Struct
81
- extend T::Sig
49
+ class InsertEvent < Data.define(:transaction_id, :lsn, :context, :table, :primary_key, :new)
82
50
  include ::Wal::ChangeEvent
83
51
 
84
- const :transaction_id, Integer
85
- const :lsn, Integer
86
- const :context, T::Hash[String, T.untyped]
87
- const :table, String
88
- const :primary_key, T.untyped
89
- const :new, T::Hash[String, T.untyped]
90
-
91
- sig { returns(T::Hash[String, [T.untyped, T.untyped]]) }
92
52
  def diff
93
53
  new.transform_values { |val| [nil, val] }
94
54
  end
95
55
  end
96
56
 
97
- class UpdateEvent < T::Struct
98
- extend T::Sig
57
+ class UpdateEvent < Data.define(:transaction_id, :lsn, :context, :table, :primary_key, :old, :new)
99
58
  include ::Wal::ChangeEvent
100
59
 
101
- const :transaction_id, Integer
102
- const :lsn, Integer
103
- const :context, T::Hash[String, T.untyped]
104
- const :table, String
105
- const :primary_key, T.untyped
106
- const :old, T::Hash[String, T.untyped]
107
- const :new, T::Hash[String, T.untyped]
108
-
109
- sig { returns(T::Hash[String, [T.untyped, T.untyped]]) }
110
60
  def diff
111
61
  (old.keys | new.keys).reduce({}) do |diff, key|
112
62
  old[key] != new[key] ? diff.merge(key => [old[key], new[key]]) : diff
@@ -114,18 +64,9 @@ module Wal
114
64
  end
115
65
  end
116
66
 
117
- class DeleteEvent < T::Struct
118
- extend T::Sig
67
+ class DeleteEvent < Data.define(:transaction_id, :lsn, :context, :table, :primary_key, :old)
119
68
  include ::Wal::ChangeEvent
120
69
 
121
- const :transaction_id, Integer
122
- const :lsn, Integer
123
- const :context, T::Hash[String, T.untyped]
124
- const :table, String
125
- const :primary_key, T.untyped
126
- const :old, T::Hash[String, T.untyped]
127
-
128
- sig { returns(T::Hash[String, [T.untyped, T.untyped]]) }
129
70
  def diff
130
71
  old.transform_values { |val| [val, nil] }
131
72
  end
data/rbi/wal.rbi CHANGED
@@ -7,7 +7,7 @@ module Wal
7
7
  UpdateEvent,
8
8
  DeleteEvent,
9
9
  ) }
10
- VERSION = "0.0.2"
10
+ VERSION = "0.0.3"
11
11
 
12
12
  class BeginTransactionEvent < T::Struct
13
13
  prop :transaction_id, Integer, immutable: true
@@ -141,66 +141,6 @@ module Wal
141
141
 
142
142
  sig { override.params(event: Event).void }
143
143
  def on_event(event); end
144
-
145
- class MemoryRecordWatcher
146
- include Wal::Watcher
147
- include Wal::Watcher::SeparatedEvents
148
- extend T::Sig
149
- extend T::Helpers
150
- RecordsStorage = T.type_alias { T::Hash[[String, Integer], T.nilable(RecordEvent)] }
151
-
152
- sig { params(watcher: T.untyped).void }
153
- def initialize(watcher); end
154
-
155
- sig { params(event: BeginTransactionEvent).void }
156
- def on_begin(event); end
157
-
158
- sig { params(_event: T.untyped).returns(T.untyped) }
159
- def on_commit(_event); end
160
-
161
- sig { params(event: InsertEvent).void }
162
- def on_insert(event); end
163
-
164
- sig { params(event: UpdateEvent).void }
165
- def on_update(event); end
166
-
167
- sig { params(event: DeleteEvent).void }
168
- def on_delete(event); end
169
- end
170
-
171
- class TemporaryTableRecordWatcher
172
- include Wal::Watcher
173
- include Wal::Watcher::SeparatedEvents
174
- extend T::Sig
175
- extend T::Helpers
176
-
177
- sig { params(watcher: T.untyped, batch_size: T.untyped).void }
178
- def initialize(watcher, batch_size: 5_000); end
179
-
180
- sig { params(event: BeginTransactionEvent).void }
181
- def on_begin(event); end
182
-
183
- sig { params(_event: T.untyped).returns(T.untyped) }
184
- def on_commit(_event); end
185
-
186
- sig { params(event: InsertEvent).void }
187
- def on_insert(event); end
188
-
189
- sig { params(event: UpdateEvent).void }
190
- def on_update(event); end
191
-
192
- sig { params(event: DeleteEvent).void }
193
- def on_delete(event); end
194
-
195
- sig { returns(T.class_of(::ActiveRecord::Base)) }
196
- def base_class; end
197
-
198
- sig { params(event: T.untyped).returns(T.untyped) }
199
- def serialize(event); end
200
-
201
- sig { params(persisted_event: T.untyped).returns(T.untyped) }
202
- def deserialize(persisted_event); end
203
- end
204
144
  end
205
145
 
206
146
  class Replicator
@@ -215,26 +155,6 @@ module Wal
215
155
 
216
156
  sig { params(watcher: Watcher, publications: T::Array[String]).returns(T::Enumerator::Lazy[Event]) }
217
157
  def replicate(watcher, publications:); end
218
-
219
- class Column < T::Struct
220
- prop :name, String, immutable: true
221
- prop :decoder, T.untyped, immutable: true
222
-
223
- sig { params(value: T.untyped).returns(T.untyped) }
224
- def decode(value); end
225
- end
226
-
227
- class Table < T::Struct
228
- prop :name, String, immutable: true
229
- prop :primary_key_colums, T::Array[String], immutable: true
230
- prop :columns, T::Array[Column], immutable: true
231
-
232
- sig { params(decoded_row: T.untyped).returns(T.untyped) }
233
- def primary_key(decoded_row); end
234
-
235
- sig { params(values: T.untyped).returns(T.untyped) }
236
- def decode_row(values); end
237
- end
238
158
  end
239
159
 
240
160
  class StreamingWatcher