Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: postgresql-cfbot/postgresql
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: cf/6078~1
Choose a base ref
...
head repository: postgresql-cfbot/postgresql
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: cf/6078
Choose a head ref
  • 3 commits
  • 6 files changed
  • 2 contributors

Commits on Dec 12, 2025

  1. Improve LISTEN/NOTIFY test coverage

    This adds isolation tests to cover previously untested code paths:
    
    * Check simple NOTIFY reparenting when parent has no action
    * Check LISTEN reparenting in subtransaction
    * Check LISTEN merge path when both outer and inner transactions have actions
    * Check LISTEN abort path (ROLLBACK TO SAVEPOINT discards pending actions)
    * Check notification_match function (triggered by hash table duplicate detection)
    * Check that notifications sent from a backend that has not done LISTEN
      are properly delivered to a listener in another backend
    
    This also adds a test to prepare for the next patch:
    
    * Check ChannelHashAddListener array growth
    joelonsql authored and Commitfest Bot committed Dec 12, 2025
    Configuration menu
    Copy the full SHA
    8e33a91 View commit details
    Browse the repository at this point in the history
  2. Optimize LISTEN/NOTIFY with shared channel map and direct advancement

    This patch reworks the LISTEN/NOTIFY signaling path to avoid the
    long-standing inefficiency where every commit wakes all listening
    backends in the same database, even those that are listening on
    completely different channels.
    
    Problem
    -------
    
    At present, SignalBackends has no central knowledge of which backend
    listens on which channel. When a backend commits a transaction that
    issued NOTIFY, it simply iterates over all registered listeners in the
    same database and sends each one a PROCSIG_NOTIFY_INTERRUPT signal.
    
    That behavior is fine when all listeners are on the same channel, but
    when many backends are listening on different channels, each NOTIFY
    triggers a storm of unnecessary wakeups and context switches. As the
    number of idle listeners grows, this often becomes the bottleneck and
    throughput drops sharply.
    
    Overview of the solution
    ------------------------
    
    This patch introduces a lazily-created dynamic shared hash (dshash)
    backed by dynamic shared memory (DSA) that maps (dboid, channel) to
    arrays of ListenerEntry structures (containing ProcNumber and active
    flag) representing the backends listening on each channel. This allows
    the sender to target only those backends actually listening on the
    channels for which it has queued notifications.
    
    Critical section safety
    -----------------------
    
    To avoid ERROR→PANIC after transaction commit, all risky operations
    (DSA allocations, dshash operations, memory allocations) are moved to
    PreCommit_Notify where failures can still safely abort.
    
    Each listener entry includes an 'active' flag. During PreCommit, LISTEN
    operations insert entries with active=false to pre-allocate all required
    space. After commit to clog, AtCommit_Notify simply flips active flags
    to true—a boolean assignment that has acceptably low failure risk.
    
    Similarly, signal arrays (notifySignalPids/notifySignalProcs) use a
    hybrid allocation strategy: allocated once in TopMemoryContext during
    the first NOTIFY (in PreCommit where OOM can still abort), then reused
    permanently across all transactions.
    
    At commit time:
    
    * Exec_ListenPreCommit performs all risky allocations with active=false
    * Exec_ListenCommit flips active flags to true (post-commit)
    * AtCommit_Notify updates the shared channelHash to reflect any LISTEN
      or UNLISTEN actions performed in the transaction.
    * SignalBackends consults this hash to find the backends that are
      listening on the channels being notified in the current database, and
      signals only those with active=true.
    
    Each backend's entry in AsyncQueueControl now includes a wakeupPending
    flag to prevent duplicate signals while a previous wakeup is still being
    processed.
    
    Direct advancement
    ------------------
    
    A further optimization avoids signaling idle backends that are not
    listening on any of the channels notified within the transaction.
    
    While queuing notifications, PreCommit_Notify records the queue head
    position both before and after writing its notifications. Because all
    writers are serialized by the existing cluster-wide heavyweight lock on
    "database 0", no backend (from any database) can insert entries between
    those two points. This guarantees that the region [oldHead, newHead)
    contains only the entries written by our commit.
    
    SignalBackends uses this fact to directly advance any backend still
    positioned at oldHead up to newHead, avoiding a needless wakeup for
    listeners that would otherwise not find any notifies of interest.
    
    To handle advancing backends correctly, each backend's entry tracks both
    whether it is currently advancing (isAdvancing) and the target position
    it is advancing to (advancingPos). This allows SignalBackends to signal
    advancing backends only when their target position would leave them
    behind the new queue head, while safely direct-advancing idle backends
    that would not be interested in the newly written notifications.
    Idle backends that are stationary at a position before the old queue
    head are signaled, since they might be interested in the notifications
    in between their current position and the old queue head.
    
    Other notes
    -----------
    
    * Maintains dual data structures: a shared channelHash for determining
      which backends to signal, and a local per-backend listenChannels list
      for fast lock-free lookups during notification processing. This avoids
      contention on the shared hash during the high-frequency IsListeningOn
      checks that occur for every notification read from the queue.
    * Backends remain registered in the global listener list as long as
      listenChannels is non-empty.
    * AtAbort_Notify cleanly handles both local and shared hash cleanup,
      removing inactive (uncommitted) entries while preserving active
      (committed) entries from previous transactions.
    * Adds LWLock tranche NOTIFY_CHANNEL_HASH and wait event
      NotifyChannelHash for visibility.
    * No user-visible behavioral changes; this is an internal optimization
      only.
    joelonsql authored and Commitfest Bot committed Dec 12, 2025
    Configuration menu
    Copy the full SHA
    5e72ab3 View commit details
    Browse the repository at this point in the history
  3. [CF 6078] Optimize LISTEN/NOTIFY

    This branch was automatically generated by a robot using patches from an
    email thread registered at:
    
    https://commitfest.postgresql.org/patch/6078
    
    The branch will be overwritten each time a new patch version is posted to
    the thread, and also periodically to check for bitrot caused by changes
    on the master branch.
    
    Patch(es): https://www.postgresql.org/message-id/82c084d7-ff48-49f0-8fc9-b68867dbd713@app.fastmail.com
    Author(s): Joel Jacobson
    Commitfest Bot committed Dec 12, 2025
    Configuration menu
    Copy the full SHA
    8b5163f View commit details
    Browse the repository at this point in the history
Loading