pgoutput-source-adapter

Gem Version CI Ruby Version License: MIT

pgoutput-source-adapter adapts decoded pgoutput events into downstream change-event platform primitives.

The first supported target is the CDC Ecosystem:

Pgoutput::SourceAdapter::Cdc

It normalizes Pgoutput::Decoder::Events into CDC::Core::ChangeEvent and CDC::Core::TransactionEnvelope objects.

Boundary

The pgoutput family remains standalone:

pgoutput-client   -> PostgreSQL logical replication transport
pgoutput-parser   -> pgoutput protocol messages
pgoutput-decoder  -> typed Ruby row-change events

This gem is the adapter layer:

Pgoutput::Decoder::Events
        |
        v
Pgoutput::SourceAdapter::Cdc
        |
        v
CDC::Core::ChangeEvent / TransactionEnvelope

That keeps the lower-level pgoutput gems usable outside the CDC Ecosystem while still providing a clean bridge into cdc-core for users building CDC platforms.

Installation

gem "pgoutput-source-adapter"
require "pgoutput/source_adapter"

The generated bundle gem require path also works:

require "pgoutput/source/adapter"

Usage

Normalize a decoded insert event:

adapter = Pgoutput::SourceAdapter::Cdc.new
change_event = adapter.normalize(decoded_insert)

change_event.operation
# => :insert

change_event.schema
change_event.table
change_event.new_values

Normalize a transaction-shaped batch:

results = adapter.normalize_many([
  decoded_begin,
  decoded_insert,
  decoded_update,
  decoded_commit
])

envelope = results.first
# => CDC::Core::TransactionEnvelope

Normalize an unbounded replication stream incrementally:

inputs = Enumerator.new do |stream|
  runner.start do |payload, |
    decoded = decoder.decode(parser.process(payload))
    stream << adapter.stream_event(decoded, source_position: .wal_end_lsn)
  end
end

adapter.each_normalized(inputs) do |work|
  processor.process(work)
end

Transaction buffering belongs to the source adapter. Changes outside a transaction are yielded immediately; changes between Begin and Commit are yielded as a CDC::Core::TransactionEnvelope. normalize_many uses the same normalization path for finite batches.

Each change inside a transaction receives a stable, zero-based sequence_number. The ordinal follows decoded row-event order, resets at each Begin, and is reproduced when the same transaction input is replayed. Changes normalized outside transaction boundaries retain a nil sequence number. Downstream consumers can include this discriminator in deterministic event IDs so otherwise identical changes in the same transaction remain distinct.

Primary keys

For update and delete events, pgoutput may provide an old-key tuple. When it does, the complete tuple is used as CDC::Core::ChangeEvent#primary_key, including composite and non-id replica keys.

For inserts and events without an old-key tuple, the default resolver uses relation key-column metadata when the decoded event exposes replica_identity_columns, key_columns, or a relation whose column flags mark replica-key columns. The adapter extracts every declared column and raises Pgoutput::SourceAdapter::Error rather than returning a partial key.

When decoded events do not carry relation key metadata, configure the reusable schema-aware resolver. Relations may be keyed by relation id, [schema, table], or a qualified name:

resolver = Pgoutput::SourceAdapter::ReplicaIdentityResolver.new(
  ["public", "memberships"] => ["tenant_id", "member_uuid"],
  "public.accounts" => ["account_uuid"]
)

adapter = Pgoutput::SourceAdapter::Cdc.new(
  primary_key_resolver: resolver
)

The legacy id / "id" inference remains as a compatibility fallback only when neither relation metadata nor an explicit resolver is available. Custom callables remain supported through primary_key_resolver:.

Metadata

Each normalized event includes pgoutput metadata:

{
  "source" => "pgoutput",
  "relation_id" => 123,
  "pgoutput_event" => "Insert"
}

Additional metadata can be injected:

adapter = Pgoutput::SourceAdapter::Cdc.new(
  metadata_builder: ->(_event) { { pipeline: "default" } }
)

Public namespace

Pgoutput::SourceAdapter
Pgoutput::SourceAdapter::Cdc
Pgoutput::SourceAdapter::ReplicaIdentityResolver

A compatibility alias is also provided for the generated gem path:

Pgoutput::Source::Adapter

Non-goals

This gem does not:

  • connect to PostgreSQL
  • parse pgoutput protocol messages
  • decode PostgreSQL values
  • run processors
  • manage replication slots
  • persist sink data

Those responsibilities belong to pgoutput-client, pgoutput-parser, pgoutput-decoder, runtime gems, or application code.

Development

bundle exec rake

The default quality task enforces 100% line and branch coverage and 100% YARD API documentation coverage. Run bundle exec yard stats --list-undoc to inspect the documented public API objects.

License

MIT.