pgoutput-client 0.2.3 → 0.2.4
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/CHANGELOG.md +29 -0
- data/README.md +51 -0
- data/lib/pgoutput/client/connection.rb +18 -4
- data/lib/pgoutput/client/feedback.rb +3 -0
- data/lib/pgoutput/client/keepalive.rb +3 -0
- data/lib/pgoutput/client/runner.rb +10 -0
- data/lib/pgoutput/client/state.rb +3 -0
- data/lib/pgoutput/client/version.rb +1 -1
- data/lib/pgoutput/client/xlog_data.rb +3 -0
- data/lib/pgoutput/client.rb +7 -0
- data/sig/pgoutput/client/connection.rbs +7 -3
- 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: e4f6889c573693726868891513ab942b96ea305cef6c70407216a0fdf9046a32
|
|
4
|
+
data.tar.gz: 6ae8423d421ba51472c3f777451bc970f986c2b2b52c9cf6a234f97491c0ea70
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 57d9bdda60c19cfc8c09e403d998af9f26bfcdf9ddb6dd81874f22142c00b8fd1e20cea7120fcbc7484886490870f1c3efe4fcab20a0e0a52283a2d456332977
|
|
7
|
+
data.tar.gz: 2840604c443d755c6850154284dfe412d60821adac80428b76fa3569f8db986109ee0457a695938b6e051f224e1b5bb69923cea13934a95184836595bfad6b4f
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.4 - 2026-06-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added socket-aware replication stream polling.
|
|
10
|
+
- Added E2E coverage for PostgreSQL restart and replication stream recovery.
|
|
11
|
+
- Added connection readiness checks to E2E infrastructure.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Improved replication stream handling during idle periods.
|
|
16
|
+
- Improved reconnect behavior after PostgreSQL restarts.
|
|
17
|
+
- Improved standby feedback reliability during long-running streams.
|
|
18
|
+
- Improved E2E test stability across PostgreSQL startup and restart scenarios.
|
|
19
|
+
- Normalized `PG#get_copy_data` idle responses to simplify stream processing.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Fixed replication stream recovery after PostgreSQL restart.
|
|
24
|
+
- Fixed handling of idle COPY stream reads.
|
|
25
|
+
- Fixed reconnect loops triggered by PostgreSQL replication timeouts.
|
|
26
|
+
- Fixed E2E race conditions during PostgreSQL initialization.
|
|
27
|
+
- Fixed replication slot creation behavior when a slot already exists.
|
|
28
|
+
- Fixed several edge cases uncovered by Mammoth integration testing.
|
|
29
|
+
|
|
30
|
+
### Documentation
|
|
31
|
+
|
|
32
|
+
- Expanded YARD documentation coverage to 98.95%.
|
|
33
|
+
|
|
5
34
|
## 0.2.3 - 2026-06-17
|
|
6
35
|
|
|
7
36
|
### Fixed
|
data/README.md
CHANGED
|
@@ -363,6 +363,57 @@ Equivalent Rake task:
|
|
|
363
363
|
bundle exec rake e2e:run
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
+
## Transport lifecycle behavior
|
|
367
|
+
|
|
368
|
+
`pgoutput-client` owns PostgreSQL logical replication transport and lifecycle
|
|
369
|
+
management. It opens the replication connection, optionally creates the logical
|
|
370
|
+
replication slot, starts streaming, sends standby status feedback, and retries
|
|
371
|
+
reconnectable failures.
|
|
372
|
+
|
|
373
|
+
### Idle standby feedback
|
|
374
|
+
|
|
375
|
+
Long-running replication streams can be quiet for long periods when no WAL
|
|
376
|
+
changes are produced. During those idle periods the client wakes periodically
|
|
377
|
+
and sends standby status feedback so PostgreSQL does not terminate the walsender
|
|
378
|
+
for replication timeout.
|
|
379
|
+
|
|
380
|
+
Control the feedback cadence with `feedback_interval`:
|
|
381
|
+
|
|
382
|
+
```ruby
|
|
383
|
+
runner = Pgoutput::Client::Runner.new(
|
|
384
|
+
database_url: ENV.fetch("DATABASE_URL"),
|
|
385
|
+
slot_name: "mammoth_live",
|
|
386
|
+
publication_names: ["mammoth_publication"],
|
|
387
|
+
feedback_interval: 10.0
|
|
388
|
+
)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Idempotent automatic slot creation
|
|
392
|
+
|
|
393
|
+
When `auto_create_slot` is enabled, the client treats slot creation as
|
|
394
|
+
"ensure this slot exists". Missing slots are created before streaming; existing
|
|
395
|
+
slots are reused and do not cause startup failure.
|
|
396
|
+
|
|
397
|
+
```ruby
|
|
398
|
+
runner = Pgoutput::Client::Runner.new(
|
|
399
|
+
database_url: ENV.fetch("DATABASE_URL"),
|
|
400
|
+
slot_name: "mammoth_live",
|
|
401
|
+
publication_names: ["mammoth_publication"],
|
|
402
|
+
auto_create_slot: true,
|
|
403
|
+
temporary_slot: false
|
|
404
|
+
)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Publication creation remains outside this gem. Create publications through
|
|
408
|
+
application migrations, database bootstrap SQL, or infrastructure tooling.
|
|
409
|
+
|
|
410
|
+
### Restart recovery
|
|
411
|
+
|
|
412
|
+
After a stream has connected successfully, transient PostgreSQL outages are
|
|
413
|
+
retried through the reconnect lifecycle. This includes ordinary container or
|
|
414
|
+
process restart windows where PostgreSQL temporarily refuses connections or
|
|
415
|
+
reports that the database system is starting up.
|
|
416
|
+
|
|
366
417
|
---
|
|
367
418
|
|
|
368
419
|
## License
|
|
@@ -76,14 +76,19 @@ module Pgoutput
|
|
|
76
76
|
|
|
77
77
|
# Receive one CopyData payload from the server.
|
|
78
78
|
#
|
|
79
|
-
# The
|
|
80
|
-
#
|
|
81
|
-
#
|
|
79
|
+
# The stream must not block forever while PostgreSQL is idle, because the
|
|
80
|
+
# caller needs opportunities to send periodic standby feedback. Wait
|
|
81
|
+
# briefly for socket readability, then use the pg driver's blocking
|
|
82
|
+
# CopyData read only when data is available. `nil` means the stream is
|
|
83
|
+
# currently idle.
|
|
82
84
|
#
|
|
83
85
|
# @return [String, nil] raw CopyData payload or `nil`
|
|
84
86
|
# @raise [ConnectionError] if receiving fails
|
|
85
87
|
def get_copy_data # rubocop:disable Naming/AccessorMethodName
|
|
86
|
-
|
|
88
|
+
return nil unless copy_data_readable?
|
|
89
|
+
|
|
90
|
+
copy_data = @pg_connection.get_copy_data(false)
|
|
91
|
+
copy_data == false ? nil : copy_data
|
|
87
92
|
rescue PG::Error => e
|
|
88
93
|
raise ConnectionError, e.message
|
|
89
94
|
end
|
|
@@ -110,6 +115,15 @@ module Pgoutput
|
|
|
110
115
|
|
|
111
116
|
private
|
|
112
117
|
|
|
118
|
+
def copy_data_readable?
|
|
119
|
+
return true unless @pg_connection.respond_to?(:socket_io)
|
|
120
|
+
|
|
121
|
+
socket = @pg_connection.socket_io
|
|
122
|
+
return true unless socket
|
|
123
|
+
|
|
124
|
+
!!IO.select([socket], nil, nil, 0.1)
|
|
125
|
+
end
|
|
126
|
+
|
|
113
127
|
def exec(sql)
|
|
114
128
|
@pg_connection.exec(sql)
|
|
115
129
|
rescue PG::Error => e
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Pgoutput
|
|
4
4
|
module Client
|
|
5
|
+
# Internal immutable base class generated by `Data.define` for {Feedback}.
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
5
8
|
FeedbackData = Data.define(:received_lsn, :flushed_lsn, :applied_lsn, :client_clock, :reply_requested)
|
|
6
9
|
|
|
7
10
|
# Standby status feedback message builder.
|
|
@@ -37,7 +37,17 @@ module Pgoutput
|
|
|
37
37
|
# @see Stream
|
|
38
38
|
# @api public
|
|
39
39
|
class Runner
|
|
40
|
+
# Default number of reconnect attempts after a previously healthy stream
|
|
41
|
+
# fails. The default is intentionally large enough to survive ordinary
|
|
42
|
+
# PostgreSQL restart windows.
|
|
43
|
+
#
|
|
44
|
+
# @return [Integer]
|
|
40
45
|
DEFAULT_RECONNECT_ATTEMPTS = 30
|
|
46
|
+
|
|
47
|
+
# Base reconnect backoff, in seconds. Attempt `n` sleeps for
|
|
48
|
+
# `n * DEFAULT_RECONNECT_BACKOFF`.
|
|
49
|
+
#
|
|
50
|
+
# @return [Float]
|
|
41
51
|
DEFAULT_RECONNECT_BACKOFF = 0.5
|
|
42
52
|
|
|
43
53
|
# Configuration used by this runner.
|
data/lib/pgoutput/client.rb
CHANGED
|
@@ -13,6 +13,13 @@ require_relative "client/connection"
|
|
|
13
13
|
require_relative "client/stream"
|
|
14
14
|
require_relative "client/runner"
|
|
15
15
|
|
|
16
|
+
# Namespace for PostgreSQL pgoutput logical replication components.
|
|
17
|
+
#
|
|
18
|
+
# The top-level namespace is shared by pgoutput ecosystem gems. This gem
|
|
19
|
+
# defines only the `Pgoutput::Client` transport namespace and leaves protocol
|
|
20
|
+
# parsing, value decoding, and CDC normalization to sibling libraries.
|
|
21
|
+
#
|
|
22
|
+
# @api public
|
|
16
23
|
module Pgoutput
|
|
17
24
|
# Namespace for PostgreSQL logical replication transport support.
|
|
18
25
|
#
|
|
@@ -61,9 +61,11 @@ module Pgoutput
|
|
|
61
61
|
|
|
62
62
|
# Receive one CopyData payload from the server.
|
|
63
63
|
#
|
|
64
|
-
# The
|
|
65
|
-
#
|
|
66
|
-
#
|
|
64
|
+
# The stream must not block forever while PostgreSQL is idle, because the
|
|
65
|
+
# caller needs opportunities to send periodic standby feedback. Wait
|
|
66
|
+
# briefly for socket readability, then use the pg driver's blocking
|
|
67
|
+
# CopyData read only when data is available. `nil` means the stream is
|
|
68
|
+
# currently idle.
|
|
67
69
|
#
|
|
68
70
|
# @return [String, nil] raw CopyData payload or `nil`
|
|
69
71
|
# @raise [ConnectionError] if receiving fails
|
|
@@ -85,6 +87,8 @@ module Pgoutput
|
|
|
85
87
|
|
|
86
88
|
private
|
|
87
89
|
|
|
90
|
+
def copy_data_readable?: () -> bool
|
|
91
|
+
|
|
88
92
|
def exec: (untyped sql) -> untyped
|
|
89
93
|
end
|
|
90
94
|
end
|