wal 0.0.25 → 0.0.26
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/wal/replicator.rb +187 -20
- data/lib/wal/version.rb +1 -1
- data/rbi/wal.rbi +4 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f4e3079bb9acf7aca8959aca8f0e9aca650e4d615112e472bcc903a90517ad7c
|
|
4
|
+
data.tar.gz: 90db3db17387e674920dd72e31079b5345bc8e2f049458f1c0afce8ad68811ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 80a7497a438b5ec3f8fce77ac15d09789f1e3adec6c121bf901bf0a01041de9b20819ba24cd4b9c9f4dbd9344314a02eb210518270751e60cc1bcc41ce8949f1
|
|
7
|
+
data.tar.gz: 91bf03a93fe7afb0b9320ec73e9cbaeb726494504813a77b166f43fe1d6f0118871cd147568372d7d8b277c69fcb74f1a49032ffaf4ea72e6e12fae7a7a88495
|
data/lib/wal/replicator.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require "ostruct"
|
|
2
|
-
|
|
3
1
|
module Wal
|
|
4
2
|
# Responsible to hook into a Postgres logical replication slot and stream the changes to a specific `Watcher`.
|
|
5
3
|
# Also it supports inject "contexts" into the replication events.
|
|
@@ -23,8 +21,14 @@ module Wal
|
|
|
23
21
|
nil
|
|
24
22
|
end
|
|
25
23
|
|
|
24
|
+
def close
|
|
25
|
+
@watch_conn&.stop_replication
|
|
26
|
+
@watch_conn&.close
|
|
27
|
+
@watch_conn = nil
|
|
28
|
+
end
|
|
29
|
+
|
|
26
30
|
def replicate(watcher, publications:)
|
|
27
|
-
watch_conn = PG.connect(
|
|
31
|
+
@watch_conn = PG.connect(
|
|
28
32
|
dbname: @db_config[:database],
|
|
29
33
|
host: @db_config[:host],
|
|
30
34
|
user: @db_config[:username],
|
|
@@ -34,7 +38,7 @@ module Wal
|
|
|
34
38
|
)
|
|
35
39
|
|
|
36
40
|
begin
|
|
37
|
-
watch_conn.query(<<~SQL)
|
|
41
|
+
@watch_conn.query(<<~SQL)
|
|
38
42
|
CREATE_REPLICATION_SLOT #{@replication_slot} #{@use_temporary_slot ? "TEMPORARY" : ""} LOGICAL "pgoutput"
|
|
39
43
|
SQL
|
|
40
44
|
rescue PG::DuplicateObject
|
|
@@ -45,7 +49,7 @@ module Wal
|
|
|
45
49
|
context = {}
|
|
46
50
|
transaction_id = nil
|
|
47
51
|
|
|
48
|
-
watch_conn.start_pgoutput_replication_slot(@replication_slot, publications, messages: true).filter_map do |msg|
|
|
52
|
+
@watch_conn.start_pgoutput_replication_slot(@replication_slot, publications, messages: true).filter_map do |msg|
|
|
49
53
|
case msg
|
|
50
54
|
in XLogData(data: PG::Replication::PGOutput::Relation(oid:, name:, columns:, namespace:))
|
|
51
55
|
tables[oid] = Table.new(
|
|
@@ -53,16 +57,7 @@ module Wal
|
|
|
53
57
|
primary_key_colums: columns.any? { |col| col.name == "id" } ? ["id"] : [],
|
|
54
58
|
schema: namespace,
|
|
55
59
|
name:,
|
|
56
|
-
columns: columns.map
|
|
57
|
-
Column.new(
|
|
58
|
-
name: col.name,
|
|
59
|
-
decoder: ActiveRecord::Base.connection.lookup_cast_type_from_column(
|
|
60
|
-
# We have to create this OpenStruct because weird AR API reasons...
|
|
61
|
-
# And the `sql_type` param luckly doesn't really matter for our use case
|
|
62
|
-
::OpenStruct.new(oid: col.oid, fmod: col.modifier, sql_type: "")
|
|
63
|
-
),
|
|
64
|
-
)
|
|
65
|
-
end
|
|
60
|
+
columns: columns.map { |col| Column.new(oid: col.oid, name: col.name) },
|
|
66
61
|
)
|
|
67
62
|
next
|
|
68
63
|
|
|
@@ -84,11 +79,11 @@ module Wal
|
|
|
84
79
|
timestamp:,
|
|
85
80
|
).tap do |event|
|
|
86
81
|
watcher.on_event(event)
|
|
87
|
-
watch_conn.standby_status_update(write_lsn: lsn)
|
|
82
|
+
@watch_conn.standby_status_update(write_lsn: lsn)
|
|
88
83
|
end
|
|
89
84
|
|
|
90
85
|
in XLogData(lsn:, data: PG::Replication::PGOutput::Message(prefix: "wal_ping"))
|
|
91
|
-
watch_conn.standby_status_update(write_lsn: [watch_conn.last_confirmed_lsn, lsn].compact.max)
|
|
86
|
+
@watch_conn.standby_status_update(write_lsn: [@watch_conn.last_confirmed_lsn, lsn].compact.max)
|
|
92
87
|
next
|
|
93
88
|
|
|
94
89
|
in XLogData(data: PG::Replication::PGOutput::Message(prefix:, content:)) if watcher.valid_context_prefix? prefix
|
|
@@ -156,14 +151,186 @@ module Wal
|
|
|
156
151
|
next
|
|
157
152
|
end
|
|
158
153
|
rescue
|
|
159
|
-
|
|
154
|
+
close
|
|
160
155
|
raise
|
|
161
156
|
end
|
|
162
157
|
end
|
|
163
158
|
|
|
164
|
-
class Column < Data.define(:name, :
|
|
159
|
+
class Column < Data.define(:name, :oid)
|
|
160
|
+
BOOLEAN_DECODER = PG::TextDecoder::Boolean.new
|
|
161
|
+
BYTEA_DECODER = PG::TextDecoder::Bytea.new
|
|
162
|
+
INTEGER_DECODER = PG::TextDecoder::Integer.new
|
|
163
|
+
FLOAT_DECODER = PG::TextDecoder::Float.new
|
|
164
|
+
NUMERIC_DECODER = PG::TextDecoder::Numeric.new
|
|
165
|
+
DATE_DECODER = PG::TextDecoder::Date.new
|
|
166
|
+
TIMESTAMP_DECODER = PG::TextDecoder::TimestampWithoutTimeZone.new
|
|
167
|
+
TIMESTAMPTZ_DECODER = PG::TextDecoder::TimestampWithTimeZone.new
|
|
168
|
+
JSON_DECODER = PG::TextDecoder::JSON.new
|
|
169
|
+
INET_DECODER = PG::TextDecoder::Inet.new
|
|
170
|
+
STRING_DECODER = PG::TextDecoder::String.new
|
|
171
|
+
|
|
172
|
+
BOOLEAN_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: BOOLEAN_DECODER)
|
|
173
|
+
BYTEA_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: BYTEA_DECODER)
|
|
174
|
+
INTEGER_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: INTEGER_DECODER)
|
|
175
|
+
FLOAT_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: FLOAT_DECODER)
|
|
176
|
+
NUMERIC_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: NUMERIC_DECODER)
|
|
177
|
+
DATE_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: DATE_DECODER)
|
|
178
|
+
TIMESTAMP_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: TIMESTAMP_DECODER)
|
|
179
|
+
TIMESTAMPTZ_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: TIMESTAMPTZ_DECODER)
|
|
180
|
+
JSON_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: JSON_DECODER)
|
|
181
|
+
INET_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: INET_DECODER)
|
|
182
|
+
STRING_ARRAY_DECODER = PG::TextDecoder::Array.new(elements_type: STRING_DECODER)
|
|
183
|
+
|
|
184
|
+
# Reference: https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_type.dat
|
|
185
|
+
DECODERS = {
|
|
186
|
+
16 => BOOLEAN_DECODER, # bool
|
|
187
|
+
1000 => BOOLEAN_ARRAY_DECODER, # _bool
|
|
188
|
+
|
|
189
|
+
17 => BYTEA_DECODER, # bytea
|
|
190
|
+
1001 => BYTEA_ARRAY_DECODER, # _bytea
|
|
191
|
+
|
|
192
|
+
20 => INTEGER_DECODER, # int8
|
|
193
|
+
21 => INTEGER_DECODER, # int2
|
|
194
|
+
23 => INTEGER_DECODER, # int4
|
|
195
|
+
26 => INTEGER_DECODER, # oid
|
|
196
|
+
28 => INTEGER_DECODER, # xid
|
|
197
|
+
29 => INTEGER_DECODER, # cid
|
|
198
|
+
5069 => INTEGER_DECODER, # xid8
|
|
199
|
+
1005 => INTEGER_ARRAY_DECODER, # _int2
|
|
200
|
+
1007 => INTEGER_ARRAY_DECODER, # _int4
|
|
201
|
+
1016 => INTEGER_ARRAY_DECODER, # _int8
|
|
202
|
+
1028 => INTEGER_ARRAY_DECODER, # _oid
|
|
203
|
+
1011 => INTEGER_ARRAY_DECODER, # _xid
|
|
204
|
+
1012 => INTEGER_ARRAY_DECODER, # _cid
|
|
205
|
+
271 => INTEGER_ARRAY_DECODER, # _xid8
|
|
206
|
+
|
|
207
|
+
700 => FLOAT_DECODER, # float4
|
|
208
|
+
701 => FLOAT_DECODER, # float8
|
|
209
|
+
1021 => FLOAT_ARRAY_DECODER, # _float4
|
|
210
|
+
1022 => FLOAT_ARRAY_DECODER, # _float8
|
|
211
|
+
|
|
212
|
+
1700 => NUMERIC_DECODER, # numeric
|
|
213
|
+
790 => NUMERIC_DECODER, # money
|
|
214
|
+
1231 => NUMERIC_ARRAY_DECODER, # _numeric
|
|
215
|
+
791 => NUMERIC_ARRAY_DECODER, # _money
|
|
216
|
+
|
|
217
|
+
1082 => DATE_DECODER, # date
|
|
218
|
+
1114 => TIMESTAMP_DECODER, # timestamp
|
|
219
|
+
1184 => TIMESTAMPTZ_DECODER, # timestamptz
|
|
220
|
+
1083 => STRING_DECODER, # time (no dedicated decoder, use string)
|
|
221
|
+
1266 => STRING_DECODER, # timetz
|
|
222
|
+
1186 => STRING_DECODER, # interval
|
|
223
|
+
1182 => DATE_ARRAY_DECODER, # _date
|
|
224
|
+
1115 => TIMESTAMP_ARRAY_DECODER, # _timestamp
|
|
225
|
+
1185 => TIMESTAMPTZ_ARRAY_DECODER, # _timestamptz
|
|
226
|
+
1183 => STRING_ARRAY_DECODER, # _time
|
|
227
|
+
1270 => STRING_ARRAY_DECODER, # _timetz
|
|
228
|
+
1187 => STRING_ARRAY_DECODER, # _interval
|
|
229
|
+
|
|
230
|
+
114 => JSON_DECODER, # json
|
|
231
|
+
3802 => JSON_DECODER, # jsonb
|
|
232
|
+
199 => JSON_ARRAY_DECODER, # _json
|
|
233
|
+
3807 => JSON_ARRAY_DECODER, # _jsonb
|
|
234
|
+
4072 => STRING_DECODER, # jsonpath
|
|
235
|
+
4073 => STRING_ARRAY_DECODER, # _jsonpath
|
|
236
|
+
|
|
237
|
+
869 => INET_DECODER, # inet
|
|
238
|
+
650 => INET_DECODER, # cidr
|
|
239
|
+
829 => STRING_DECODER, # macaddr
|
|
240
|
+
774 => STRING_DECODER, # macaddr8
|
|
241
|
+
1041 => INET_ARRAY_DECODER, # _inet
|
|
242
|
+
651 => INET_ARRAY_DECODER, # _cidr
|
|
243
|
+
1040 => STRING_ARRAY_DECODER, # _macaddr
|
|
244
|
+
775 => STRING_ARRAY_DECODER, # _macaddr8
|
|
245
|
+
|
|
246
|
+
18 => STRING_DECODER, # char
|
|
247
|
+
19 => STRING_DECODER, # name
|
|
248
|
+
25 => STRING_DECODER, # text
|
|
249
|
+
1042 => STRING_DECODER, # bpchar
|
|
250
|
+
1043 => STRING_DECODER, # varchar
|
|
251
|
+
2950 => STRING_DECODER, # uuid
|
|
252
|
+
142 => STRING_DECODER, # xml
|
|
253
|
+
1002 => STRING_ARRAY_DECODER, # _char
|
|
254
|
+
1003 => STRING_ARRAY_DECODER, # _name
|
|
255
|
+
1009 => STRING_ARRAY_DECODER, # _text
|
|
256
|
+
1014 => STRING_ARRAY_DECODER, # _bpchar
|
|
257
|
+
1015 => STRING_ARRAY_DECODER, # _varchar
|
|
258
|
+
2951 => STRING_ARRAY_DECODER, # _uuid
|
|
259
|
+
143 => STRING_ARRAY_DECODER, # _xml
|
|
260
|
+
|
|
261
|
+
1560 => STRING_DECODER, # bit
|
|
262
|
+
1562 => STRING_DECODER, # varbit
|
|
263
|
+
1561 => STRING_ARRAY_DECODER, # _bit
|
|
264
|
+
1563 => STRING_ARRAY_DECODER, # _varbit
|
|
265
|
+
|
|
266
|
+
600 => STRING_DECODER, # point
|
|
267
|
+
601 => STRING_DECODER, # lseg
|
|
268
|
+
602 => STRING_DECODER, # path
|
|
269
|
+
603 => STRING_DECODER, # box
|
|
270
|
+
604 => STRING_DECODER, # polygon
|
|
271
|
+
628 => STRING_DECODER, # line
|
|
272
|
+
718 => STRING_DECODER, # circle
|
|
273
|
+
1017 => STRING_ARRAY_DECODER, # _point
|
|
274
|
+
1018 => STRING_ARRAY_DECODER, # _lseg
|
|
275
|
+
1019 => STRING_ARRAY_DECODER, # _path
|
|
276
|
+
1020 => STRING_ARRAY_DECODER, # _box
|
|
277
|
+
1027 => STRING_ARRAY_DECODER, # _polygon
|
|
278
|
+
629 => STRING_ARRAY_DECODER, # _line
|
|
279
|
+
719 => STRING_ARRAY_DECODER, # _circle
|
|
280
|
+
|
|
281
|
+
3904 => STRING_DECODER, # int4range
|
|
282
|
+
3906 => STRING_DECODER, # numrange
|
|
283
|
+
3908 => STRING_DECODER, # tsrange
|
|
284
|
+
3910 => STRING_DECODER, # tstzrange
|
|
285
|
+
3912 => STRING_DECODER, # daterange
|
|
286
|
+
3926 => STRING_DECODER, # int8range
|
|
287
|
+
4451 => STRING_DECODER, # int4multirange
|
|
288
|
+
4532 => STRING_DECODER, # nummultirange
|
|
289
|
+
4533 => STRING_DECODER, # tsmultirange
|
|
290
|
+
4534 => STRING_DECODER, # tstzmultirange
|
|
291
|
+
4535 => STRING_DECODER, # datemultirange
|
|
292
|
+
4536 => STRING_DECODER, # int8multirange
|
|
293
|
+
3905 => STRING_ARRAY_DECODER, # _int4range
|
|
294
|
+
3907 => STRING_ARRAY_DECODER, # _numrange
|
|
295
|
+
3909 => STRING_ARRAY_DECODER, # _tsrange
|
|
296
|
+
3911 => STRING_ARRAY_DECODER, # _tstzrange
|
|
297
|
+
3913 => STRING_ARRAY_DECODER, # _daterange
|
|
298
|
+
3927 => STRING_ARRAY_DECODER, # _int8range
|
|
299
|
+
|
|
300
|
+
3614 => STRING_DECODER, # tsvector
|
|
301
|
+
3615 => STRING_DECODER, # tsquery
|
|
302
|
+
3643 => STRING_ARRAY_DECODER, # _tsvector
|
|
303
|
+
3645 => STRING_ARRAY_DECODER, # _tsquery
|
|
304
|
+
|
|
305
|
+
3220 => STRING_DECODER, # pg_lsn
|
|
306
|
+
3221 => STRING_ARRAY_DECODER, # _pg_lsn
|
|
307
|
+
|
|
308
|
+
24 => INTEGER_DECODER, # regproc
|
|
309
|
+
2202 => INTEGER_DECODER, # regprocedure
|
|
310
|
+
2203 => INTEGER_DECODER, # regoper
|
|
311
|
+
2204 => INTEGER_DECODER, # regoperator
|
|
312
|
+
2205 => INTEGER_DECODER, # regclass
|
|
313
|
+
2206 => INTEGER_DECODER, # regtype
|
|
314
|
+
4096 => INTEGER_DECODER, # regrole
|
|
315
|
+
4089 => INTEGER_DECODER, # regnamespace
|
|
316
|
+
3734 => INTEGER_DECODER, # regconfig
|
|
317
|
+
3769 => INTEGER_DECODER, # regdictionary
|
|
318
|
+
4191 => INTEGER_DECODER, # regcollation
|
|
319
|
+
1008 => INTEGER_ARRAY_DECODER, # _regproc
|
|
320
|
+
2207 => INTEGER_ARRAY_DECODER, # _regprocedure
|
|
321
|
+
2208 => INTEGER_ARRAY_DECODER, # _regoper
|
|
322
|
+
2209 => INTEGER_ARRAY_DECODER, # _regoperator
|
|
323
|
+
2210 => INTEGER_ARRAY_DECODER, # _regclass
|
|
324
|
+
2211 => INTEGER_ARRAY_DECODER, # _regtype
|
|
325
|
+
4097 => INTEGER_ARRAY_DECODER, # _regrole
|
|
326
|
+
4090 => INTEGER_ARRAY_DECODER, # _regnamespace
|
|
327
|
+
3735 => INTEGER_ARRAY_DECODER, # _regconfig
|
|
328
|
+
3770 => INTEGER_ARRAY_DECODER, # _regdictionary
|
|
329
|
+
4192 => INTEGER_ARRAY_DECODER, # _regcollation
|
|
330
|
+
}.freeze
|
|
331
|
+
|
|
165
332
|
def decode(value)
|
|
166
|
-
|
|
333
|
+
value&.then { (DECODERS[oid] || STRING_DECODER).decode(_1) }
|
|
167
334
|
end
|
|
168
335
|
end
|
|
169
336
|
|
data/lib/wal/version.rb
CHANGED
data/rbi/wal.rbi
CHANGED
|
@@ -7,7 +7,7 @@ module Wal
|
|
|
7
7
|
UpdateEvent,
|
|
8
8
|
DeleteEvent,
|
|
9
9
|
) }
|
|
10
|
-
VERSION = "0.0.
|
|
10
|
+
VERSION = "0.0.26"
|
|
11
11
|
|
|
12
12
|
class << self
|
|
13
13
|
sig { returns(T.class_of(Logger)) }
|
|
@@ -176,6 +176,9 @@ module Wal
|
|
|
176
176
|
|
|
177
177
|
sig { params(watcher: Wal::Watcher, publications: T::Array[String]).returns(T::Enumerator::Lazy[Wal::Event]) }
|
|
178
178
|
def replicate(watcher, publications:); end
|
|
179
|
+
|
|
180
|
+
sig { void }
|
|
181
|
+
def close; end
|
|
179
182
|
end
|
|
180
183
|
|
|
181
184
|
class StreamingWatcher
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.26
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rodrigo Navarro
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-01-
|
|
10
|
+
date: 2026-01-14 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: pg
|