markerName() (src/source/postgres/replication_protocol.zig) appends _snapshotting (13 chars) to the slot name. slot_name is validated up to MAX_IDENTIFIER_LEN = 63 (src/config/config.zig), so any slot name over 50 chars produces a marker name over Postgres' 63-byte identifier limit.
This is not broken today. Verified on the dev container (PG17):
CREATE PUBLICATION <64 chars> -> NOTICE: identifier ... will be truncated, CREATE PUBLICATION
SELECT ... WHERE pubname = '<64 chars>' -> 1 row
DROP PUBLICATION IF EXISTS <64 chars> -> DROP PUBLICATION
It works because pg_publication.pubname has type name: the literal in objectExists's comparison is coerced to name and truncated to 63 bytes exactly like CREATE PUBLICATION truncates it, so both sides match. Create, probe, and drop stay consistent.
Two things are still worth closing:
- Two slot names that share their first 50 characters collapse onto the same truncated marker. Each pipeline would then read the other's marker as its own, which is the state the marker is meant to make unambiguous.
- The correctness above rests on implicit
text -> name coercion. Nothing in the code says so, and a future rewrite of the probe (a LIKE, a ::text cast, an ORM) would silently break interrupted-snapshot recovery with no test to catch it.
Ask: validate the derived marker name against 63 bytes at config time (rejecting the over-long slot name with a clear message), or truncate it in markerName the way Postgres does. Either way, add a test that pins the behavior.
markerName()(src/source/postgres/replication_protocol.zig) appends_snapshotting(13 chars) to the slot name.slot_nameis validated up toMAX_IDENTIFIER_LEN = 63(src/config/config.zig), so any slot name over 50 chars produces a marker name over Postgres' 63-byte identifier limit.This is not broken today. Verified on the dev container (PG17):
It works because
pg_publication.pubnamehas typename: the literal inobjectExists's comparison is coerced tonameand truncated to 63 bytes exactly likeCREATE PUBLICATIONtruncates it, so both sides match. Create, probe, and drop stay consistent.Two things are still worth closing:
text->namecoercion. Nothing in the code says so, and a future rewrite of the probe (aLIKE, a::textcast, an ORM) would silently break interrupted-snapshot recovery with no test to catch it.Ask: validate the derived marker name against 63 bytes at config time (rejecting the over-long slot name with a clear message), or truncate it in
markerNamethe way Postgres does. Either way, add a test that pins the behavior.