Add LISTEN/NOTIFY: schema-change notifications and pub/sub#111
Merged
Conversation
This was referenced Jul 21, 2026
Introduce psycodict/notifications.py with a pull-based NotificationListener that opens its own autocommit connection and collects notifications via poll()/listen(), plus PostgresDatabase.notify()/listener() and an internal _notify_schema_change() hook. Schema-changing operations (create_table, drop_table, rename_table, add_column, drop_column, and the reload_final_swap) now emit pg_notify on the "psycodict_schema" channel with the affected table name as payload. Emission goes through _execute on the main connection so it is transactional: delivered on commit, dropped on rollback. rename_table announces both the old and new names. Reconnection, JSON payloads, background threads/callbacks, and auto-refresh of table metadata are intentionally out of scope for v1 and documented as such. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PostgresDatabase(config=..., **kwargs) lets a caller override host, dbname, user, etc. for every connection it opens, but listener() built the NotificationListener from self.config alone, dropping those overrides -- so a listener could subscribe on a different server or database than the sender writes to. Remember the kwargs and forward them (NotificationListener already accepts **connect_kwargs and applies them over the config, exactly like _new_connection). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two operational notes for NotificationListener, prompted by wiring the LMFDB website up to the schema channel: - Forking: a listener must be created in the process that polls it; an inherited one must be abandoned without close() (psycopg skips the protocol shutdown when collecting a connection in a different process, so dropping the reference is safe, while an explicit close would kill the parent's copy through the shared socket). - Hot standbys: NOTIFY is not WAL-logged and a server in recovery refuses LISTEN outright (SQLSTATE 25006, verified against devmirror, a physical replica) -- consumers should treat that as permanent for the server and fall back to scheduled or on-error refreshes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds LISTEN/NOTIFY support: schema-change notifications plus a small pub/sub primitive, built on psycopg3.
create_table,drop_table,rename_table,add_column,drop_column, the reload swap) emitpg_notifyon channelpsycodict_schemawith the table name as payload — transactionally on the main connection, so notifications are delivered on commit and dropped on rollback (tested both ways).rename_tableannounces both the old and new names.psycodict/notifications.py: a pull-basedNotificationListeneron a dedicated autocommit connection (poll(timeout)/listen()iterator/context manager, using psycopg 3.2'snotifies(timeout, stop_after)so polls return the instant a notification arrives), plusdb.notify(channel, payload)anddb.listener(). No threads, no callbacks, no silent auto-reconnect — deliberately minimal for v1, each documented.psycodict_schema— is sketched in the docstrings without depending on the separate refresh-tables PR (Add db.refresh_tables() so schema changes don't require restarts #99).notifications.pygained Forking (create listeners per-worker after fork; abandon an inherited one withoutclose()— psycopg's pid-guarded GC makes dropping the reference safe, while an explicit close would kill the parent's copy) and Hot standbys (a server in recovery refusesLISTENoutright, SQLSTATE 25006, and NOTIFY is not WAL-logged — treat as permanent and fall back to scheduled/on-error refresh; verified live against devmirror, which is a physical replica).🤖 Generated with Claude Code