Skip to content

Export pgoutput.so imports from the main wasm module to enable logical replication slots#79

Open
riderx wants to merge 1 commit into
electric-sql:REL_18_3-pglitefrom
riderx:fix/pgoutput-side-module-exports
Open

Export pgoutput.so imports from the main wasm module to enable logical replication slots#79
riderx wants to merge 1 commit into
electric-sql:REL_18_3-pglitefrom
riderx:fix/pgoutput-side-module-exports

Conversation

@riderx

@riderx riderx commented Jul 7, 2026

Copy link
Copy Markdown

Problem

Logical replication slots don't work in PGlite (electric-sql/pglite#880). Even with wal_level=logical, consuming a slot crashes the session:

SELECT pg_create_logical_replication_slot('slot1', 'pgoutput'); -- works
SELECT * FROM pg_logical_slot_peek_binary_changes('slot1', NULL, NULL,
  'proto_version', '1', 'publication_names', 'pub1');
-- returns 0 rows, and every query afterwards silently returns nothing,
-- until the backend PANICs with "ERRORDATA_STACK_SIZE exceeded"

Root cause

pgoutput.so is loaded at runtime as an emscripten side module, but unlike pgxs-built extensions (which generate a .imports file at install time that feeds -sEXPORTED_FUNCTIONS via the pglite-exported-functions target), it is linked from src/backend/replication/pgoutput via Makefile.shlib, so its imports were never collected.

With -sMAIN_MODULE=2, every symbol used only by pgoutput gets dead-code-eliminated from the main module — 46 functions in the current build, including defGetStreamingMode, all logicalrep_write_*, GetRelationPublications, CacheRegisterRelSyncCallback, etc. (verified by diffing WebAssembly.Module.imports(pgoutput.so) against WebAssembly.Module.exports(pglite.wasm); GOT.mem resolves fully, only function imports are missing).

At runtime the first missing call happens inside pgoutput's startup callback, throws TypeError: r is not a function from an invoke_* trampoline, and the JS side swallows it, leaving the backend permanently corrupted.

Fix

Generate pgoutput.imports at install time, using the same llvm-nm recipe as pgxs.mk, so the existing pglite-exported-functions target picks it up and the symbols survive DCE.

After rebuilding, all 46 previously-missing imports are exported from the main module and logical decoding with pgoutput works end-to-end (create/list/drop slots, peek/get binary changes, slot persistence across dump/load). Tests exercising this live in the companion PR to electric-sql/pglite, together with defaulting wal_level=logical and no longer swallowing unexpected WASM exceptions.

Fixes electric-sql/pglite#880

🤖 Generated with Claude Code

pgoutput.so is loaded at runtime as an emscripten side module, but unlike
pgxs-built extensions its undefined symbols were never collected into the
exported-functions list. With -sMAIN_MODULE=2, symbols used only by
pgoutput (defGetStreamingMode, logicalrep_write_*, publication lookup
functions, etc. - 46 in total) were dead-code-eliminated from the main
module, so any logical decoding session using pgoutput crashed at the
output plugin startup callback with a call to an undefined table entry.

Generate a pgoutput.imports file at install time, mirroring the pgxs.mk
recipe, so the pglite-exported-functions target keeps those symbols alive.

This makes logical replication slots usable in PGlite:

  SELECT pg_create_logical_replication_slot('slot', 'pgoutput');
  SELECT * FROM pg_logical_slot_get_binary_changes('slot', NULL, NULL,
    'proto_version', '1', 'publication_names', 'pub');

Fixes electric-sql/pglite#880

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
riderx pushed a commit to riderx/pglite that referenced this pull request Jul 7, 2026
Points at the head of electric-sql/postgres-pglite#79 (served by the
upstream repo via refs/pull/79/head, so submodule checkout by SHA works).
Should be updated to the merge commit once that PR lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PGlite replication slot

1 participant