System functions
================

Perform PGD management primarily by using functions you call from SQL.
All functions in PGD are exposed in the ``bdr`` schema. Schema qualify
any calls to these functions instead of putting ``bdr`` in the
``search_path`` .

Version information functions
-----------------------------

bdr.bdr_version
^^^^^^^^^^^^^^^

This function retrieves the textual representation of the version of the
BDR extension currently in use.

bdr.bdr_version_num
^^^^^^^^^^^^^^^^^^^

This function retrieves the version number of the BDR extension that is
currently in use. Version numbers are monotonically increasing, allowing
this value to be used for less-than and greater-than comparisons.

The following formula returns the version number consisting of major
version, minor version, and patch release into a single numerical value:

::

   MAJOR_VERSION * 10000 + MINOR_VERSION * 100 + PATCH_RELEASE

System information functions
----------------------------

bdr.get_relation_stats
^^^^^^^^^^^^^^^^^^^^^^

Returns the relation information.

bdr.get_subscription_stats
^^^^^^^^^^^^^^^^^^^^^^^^^^

Returns the current subscription statistics.

System and progress information parameters
------------------------------------------

PGD exposes some parameters that you can query directly in SQL using,
for example, ``SHOW`` or the ``current_setting()`` function. You can
also use ``PQparameterStatus`` (or equivalent) from a client
application.

bdr.local_node_id
^^^^^^^^^^^^^^^^^

When you initialize a session, this is set to the node id the client is
connected to. This allows an application to figure out the node it’s
connected to, even behind a transparent proxy.

It’s also used with :ref:`Connection pools and proxies <Connection pools and proxies>`  .

bdr.last_committed_lsn
^^^^^^^^^^^^^^^^^^^^^^

After every ``COMMIT`` of an asynchronous transaction, this parameter is
updated to point to the end of the commit record on the origin node.
Combining it with ``bdr.wait_for_apply_queue`` , allows applications to
perform causal reads across multiple nodes, that is, to wait until a
transaction becomes remotely visible.

transaction_id
^^^^^^^^^^^^^^

If a CAMO transaction is in progress, ``transaction_id`` is updated to
show the assigned transaction id. You can query this parameter only by
using using ``PQparameterStatus`` or equivalent, and it isn’t accessible
in SQL. See :ref:`Application use <Application use>` 

for a usage example.

Node status functions
---------------------

bdr.is_node_connected
^^^^^^^^^^^^^^^^^^^^^

Synopsis
^^^^^^^^

.. code:: sql

   bdr.is_node_connected(node_name name)

Returns boolean by checking if the walsender for a given peer is active
on this node.

bdr.is_node_ready
^^^^^^^^^^^^^^^^^

.. _synopsis-1:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.is_node_ready(node_name name, span interval DEFAULT NULL)

Returns boolean by checking if the lag is lower than the given span or
lower than the ``timeout`` for ``TO ASYNC`` otherwise.

Consensus function
------------------

bdr.consensus_disable
^^^^^^^^^^^^^^^^^^^^^

.. _synopsis-2:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.consensus_disable(node_group_name text DEFAULT NULL)

Disables the consensus worker on the local node until server restart or
until it’s reenabled using ``bdr.consensus_enable`` (whichever happens
first).

The ``node_group_name`` parameter is accepted for interface consistency
but is currently a placeholder. The function disables the consensus
worker database-wide, affecting all Raft instances on the node
regardless of the value passed.

..  Warning::
   Disabling consensus disables some features of PGD and affects availability of the EDB Postgres Distributed cluster if left disabled for a long time. Use this function only when working with Technical Support.

bdr.consensus_enable
^^^^^^^^^^^^^^^^^^^^

.. _synopsis-3:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.consensus_enable(node_group_name text DEFAULT NULL)

Re-enables a disabled consensus worker on the local node.

The ``node_group_name`` parameter is accepted for interface consistency
but is currently a placeholder. The function re-enables the consensus
worker database-wide, affecting all Raft instances on the node
regardless of the value passed.

bdr.consensus_proto_version
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Returns currently used consensus protocol version by the local node.

Needed by the PGD group reconfiguration internal mechanisms.

bdr.consensus_snapshot_export
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. _synopsis-4:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.consensus_snapshot_export(version integer DEFAULT NULL, raft_instance_id integer DEFAULT NULL)

Generate a new PGD consensus snapshot from the currently
committed-and-applied state of the local node and return it as bytea.

By default, a snapshot for the highest supported Raft version is
exported. But you can override that by passing an explicit ``version``
number.

The ``raft_instance_id`` parameter identifies which Raft instance to
export the snapshot from. When ``NULL`` (the default), the top-level
Raft instance is used. To target a specific node group’s Raft instance,
pass its instance ID, which you can look up via ``bdr.raft_instances`` .

The exporting node doesn’t have to be the current Raft leader, and it
doesn’t need to be completely up to date with the latest state on the
leader. However, ``bdr.consensus_snapshot_import()`` might not accept
such a snapshot.

The new snapshot isn’t automatically stored to the local node’s
``bdr.local_consensus_snapshot`` table. It’s only returned to the
caller.

The generated snapshot might be passed to
``bdr.consensus_snapshot_import()`` on any other nodes in the same PGD
node group that’s behind the exporting node’s Raft log position.

The local PGD consensus worker must be disabled for this function to
work. Typical usage is:

.. code:: sql

   SELECT bdr.bdr_consensus_disable();
   COPY (SELECT * FROM bdr.consensus_snapshot_export()) TO /var/lib/postgresql/my_node_consensus_snapshot.data;
   SELECT bdr.bdr_consensus_enable();

While the PGD consensus worker is disabled:

- DDL locking attempts on the node fail or time out.

- galloc sequences don’t get new values.

- Eager and CAMO transactions pause or error.

- Other functionality that needs the distributed consensus system is
  disrupted. The required downtime is generally very brief.

Depending on the use case, it might be practical to extract a snapshot
that already exists from the ``snapshot`` field of the
``bdr.local_consensus_snapshot`` table and use that instead. Doing so
doesn’t require you to stop the consensus worker.

bdr.consensus_snapshot_import
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. _synopsis-5:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.consensus_snapshot_import(snapshot bytea, raft_instance_id integer DEFAULT NULL, node_name bdr.regnode DEFAULT NULL)

Import a consensus snapshot that was exported by
``bdr.consensus_snapshot_export()`` , usually from another node in the
same PGD node group.

It’s also possible to use a snapshot extracted directly from the
``snapshot`` field of the ``bdr.local_consensus_snapshot`` table on
another node.

This function is useful for resetting a PGD node’s catalog state to a
known good state in case of corruption or user error.

The ``raft_instance_id`` parameter identifies which Raft instance to
import the snapshot into. When ``NULL`` (the default), the top-level
Raft instance is used. To target a specific node group’s Raft instance,
pass its instance ID, which you can look up via ``bdr.raft_instances`` .

The ``node_name`` parameter attributes the snapshot to a specific source
node, typically the leader node from which the snapshot was exported.
When ``NULL`` (the default), no source node is attributed.

You can import the snapshot if the importing node’s ``apply_index`` is
less than or equal to the snapshot-exporting node’s ``commit_index``
when the snapshot was generated. (See ``bdr.get_raft_status()`` .) A
node that can’t accept the snapshot because its log is already too far
ahead raises an error and makes no changes. The imported snapshot
doesn’t have to be completely up to date, as once the snapshot is
imported the node fetches the remaining changes from the current leader.

The PGD consensus worker must be disabled on the importing node for this
function to work. See notes on ``bdr.consensus_snapshot_export()`` for
details.

It’s possible to use this function to force the local node to generate a
new Raft snapshot by running:

::

   SELECT bdr.consensus_snapshot_import(bdr.consensus_snapshot_export());

This approach might also truncate the Raft logs up to the current
applied log position.

bdr.consensus_snapshot_verify
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. _synopsis-6:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.consensus_snapshot_verify(snapshot bytea)

Verify the given consensus snapshot that was exported by
``bdr.consensus_snapshot_export()`` . The snapshot header contains the
version with which it was generated and the node tries to verify it
against the same version.

The snapshot might have been exported on the same node or any other node
in the cluster. If the node verifying the snapshot doesn’t support the
version of the exported snapshot, then an error is raised.

bdr.get_consensus_status
^^^^^^^^^^^^^^^^^^^^^^^^

Returns status information about the current consensus (Raft) worker.

bdr.get_raft_status
^^^^^^^^^^^^^^^^^^^

Returns status information about the current consensus (Raft) worker.
Alias for ``bdr.get_consensus_status`` .

bdr.raft_leadership_transfer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. _synopsis-7:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.raft_leadership_transfer(node_name text, 
                                wait_for_completion boolean, 
                                node_group_name text DEFAULT NULL)

Request the node identified by ``node_name`` to be the Raft leader. The
request can be initiated from any of the PGD nodes and is internally
forwarded to the current leader to transfer the leadership to the
designated node. The designated node must be an ACTIVE PGD node with
full voting rights.

If ``wait_for_completion`` is false, the request is served on a
best-effort basis. If the node can’t become a leader in the
``bdr.raft_global_lection_timeout`` period, then some other capable node
becomes the leader again. Also, the leadership can change over the
period of time per Raft protocol. A ``true`` return result indicates
only that the request was submitted successfully.

If ``wait_for_completion`` is ``true`` , then the function waits until
the given node becomes the new leader and possibly waits infinitely if
the requested node fails to become Raft leader (for example, due to
network issues). We therefore recommend that you always set a
``statement_timeout`` with ``wait_for_completion`` to prevent an
infinite loop.

The ``node_group_name`` is optional and can be used to specify the name
of the node group where the leadership transfer happens. If not
specified, it defaults to NULL, which is interpreted as the top-level
group in the cluster. If the ``node_group_name`` is specified, the
function transfers leadership only within the specified node group.

Utility functions
-----------------

bdr.transfer_all_leaderships
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Transfers write and Raft leadership away from the local node on all
groups where the local node currently holds write or Raft leadership.
Use this function to drain all leaderships from a node before taking it
offline.

.. _synopsis-8:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.transfer_all_leaderships(writeleader_timeout interval DEFAULT 10s)

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Name,Type,Default,Description
  :widths: 15,12,12,20
  :align: left
  :class: longtable

  `writeleader_timeout`,interval,`'10s'`,Timeout for each write leadership transfer. The write leader is auto-selected and retried until this timeout is reached.

Return value
^^^^^^^^^^^^

Returns ``true`` if all transfers succeeded or if the node held no
leaderships, or ``false`` if any write leader transfer timed out.

If a write leader transfer times out, the function continues processing
remaining write leaders and Raft transfers on a best-effort basis, and
still returns ``false`` even if other transfers succeeded. Raft
transfers are retried indefinitely until leadership changes or the query
is cancelled, respecting the ``statement_timeout`` configuration
parameter.

The function shares logic with ``bdr.routing_leadership_transfer`` and
``bdr.raft_leadership_transfer`` , completing both operations in a
single call for faster results.

bdr.alter_node_kind
^^^^^^^^^^^^^^^^^^^

PGD5 introduced a concept of Task Manager Leader node. The node is
selected by PGD, but for upgraded clusters, it’s important to set the
``node_kind`` properly for all nodes in the cluster. Do this manually
after upgrading to the latest PGD version by calling the
``bdr.alter_node_kind()`` SQL function for each node.

.. _synopsis-9:

Synopsis
^^^^^^^^

::

   bdr.alter_node_kind(node_name text,
                       node_kind text);

.. _parameters-1:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_name`,Name of the node to change kind.
  `node_kind`,Kind of the node.

bdr.alter_subscription_skip_changes_upto
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Because logical replication can replicate across versions, doesn’t
replicate global changes like roles, and can replicate selectively,
sometimes the logical replication apply process can encounter an error
and stop applying changes.

Wherever possible, fix such problems by making changes to the target
side. ``CREATE`` any missing table that’s blocking replication,
``CREATE`` a needed role, ``GRANT`` a necessary permission, and so on.
But occasionally a problem can’t be fixed that way and it might be
necessary to skip entirely over a transaction. Changes are skipped as
entire transactions—all or nothing. To decide where to skip to, use log
output to find the commit LSN, per the example that follows, or peek the
change stream with the logical decoding functions.

Unless a transaction made only one change, you often need to manually
apply the transaction’s effects on the target side, so it’s important to
save the problem transaction whenever possible, as shown in the examples
that follow.

It’s possible to skip over changes without
``bdr.alter_subscription_skip_changes_upto`` by using
``pg_catalog.pg_logical_slot_get_binary_changes`` to skip to the LSN of
interest, so this is a convenience function. It does do a faster skip,
although it might bypass some kinds of errors in logical decoding.

This function works only on disabled subscriptions.

The usual sequence of steps is:

1. Identify the problem subscription and LSN of the problem commit.

2. Disable the subscription.

3. Save a copy of the transaction using
   ``pg_catalog.pg_logical_slot_peek_changes`` on the source node, if
   possible.

4. ``bdr.alter_subscription_skip_changes_upto`` on the target node.

5. Apply repaired or equivalent changes on the target manually, if
   necessary.

6. Reenable the subscription.

..  Warning::
   It's easy to make problems worse when using this function. Don't do anything unless you're certain it's the only option.

.. _synopsis-10:

Synopsis
^^^^^^^^

::

     bdr.alter_subscription_skip_changes_upto(
       subname text,
       skip_upto_and_including pg_lsn
     );

Example
^^^^^^^

Apply of a transaction is failing with an error, and you’ve determined
that lower-impact fixes such as changes on the target side can’t resolve
this issue. You determine that you must skip the transaction.

In the error logs, find the commit record LSN to skip to, as in this
example:

::

   ERROR:  XX000: CONFLICT: target_table_missing; resolver skip_if_recently_dropped returned an error: table does not exist
   CONTEXT:  during apply of INSERT from remote relation public.break_me in xact with commit-end lsn 0/300AC18 xid 131315
   committs 2021-02-02 15:11:03.913792+01 (action #2) (effective sess origin id=2 lsn=0/300AC18)
   while consuming I message from receiver for subscription bdr_regression_bdrgroup_node1_node2 (id=2667578509)
   on node node2 (id=3367056606) from upstream node node1 (id=1148549230, reporiginid=2)

In this portion of log, you have the information you need:
the_target_lsn: **0/300AC18** the_subscription:
**bdr_regression_bdrgroup_node1_node2**

Next, disable the subscription so the apply worker doesn’t try to
connect to the replication slot:

::

     SELECT bdr.alter_subscription_disable(the_subscription);

You can’t skip only parts of the transaction: it’s all or nothing. So we
strongly recommend that you save a record of it by copying it out on the
provider side first, using the subscription’s slot name.

::

     \copy (SELECT * FROM pg_catalog.pg_logical_slot_peek_changes(the_slot_name,
         the_target_lsn, NULL, min_proto_version, 1, max_proto_version, 1,
         startup_params_format, 1, proto_format, json))
      TO transaction_to_drop.csv WITH (FORMAT csv);

This example is broken into multiple lines for readability, but issue it
in a single line. ``\copy`` doesn’t support multi-line commands.

You can skip the change by changing ``peek`` to ``get`` , but
``bdr....skip_changes_upto`` does a faster skip that avoids decoding and
outputting all the data:

::

     SELECT bdr.alter_subscription_skip_changes_upto(subscription_name,
         the_target_lsn);

You can apply the same changes (or repaired versions of them) manually
to the target node, using the dumped transaction contents as a guide.

Finally, reenable the subscription:

::

     SELECT bdr.alter_subscription_enable(the_subscription);

bdr.connection_manager_refresh_pools
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This function tells the PGD connection manager to refresh its connection
pools.

This function is useful for integration with other tools or when an
immediate re-evaluation of connection pools is required. This function
operates only on the local node; to refresh connection managers across
the entire cluster, you must execute the function on each node
individually. Execution requires the ``bdr_superuser`` role. Expand
commentComment on line R861Resolved

.. _synopsis-11:

Synopsis
^^^^^^^^

.. code:: sql

   SELECT bdr.connection_manager_refresh_pools();

..  Note::
   This function may terminate existing client connections if routing changes are detected (e.g., write leader changed, nodes added/removed from routing targets, or consensus was lost).

bdr.get_node_sub_apply_lsn
^^^^^^^^^^^^^^^^^^^^^^^^^^

You can use this function on a subscriber to get the last LSN that was
received and applied from the given origin.

.. _synopsis-12:

Synopsis
^^^^^^^^

::

   bdr.get_node_sub_apply_lsn(node_name name)

.. _parameters-2:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_name`,The name of the node that's the source of the replication stream whose LSN is being retrieved.

bdr.get_node_sub_receive_lsn
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can use this function on a subscriber to get the last LSN that was
received from the given origin. It can be either unfiltered or filtered
to take into account only relevant LSN increments for transactions to be
applied.

The difference between the output of this function and the output of
``bdr.get_node_sub_apply_lsn()`` measures the size of the corresponding
apply queue.

.. _synopsis-13:

Synopsis
^^^^^^^^

::

   bdr.get_node_sub_receive_lsn(node_name name, committed bool default true)

.. _parameters-3:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_name`,The name of the node that's the source of the replication stream whose LSN is being retrieved.
  `committed`,The default (true) makes this function take into account only commits of transactions received rather than the last LSN overall. This includes actions that have no effect on the subscriber node.

bdr.global_lock_table
^^^^^^^^^^^^^^^^^^^^^

This function acquires a global DML locks on a given table. See
:ref:`DDL locking details <DDL locking details>` 

for information about global DML lock.

.. _synopsis-14:

Synopsis
^^^^^^^^

::

   bdr.global_lock_table(relation regclass)

.. _parameters-4:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `relation`,Name or oid of the relation to lock.

Notes
^^^^^

This function acquires the global DML lock independently of the
``ddl_locking`` setting.

The ``bdr.global_lock_table`` function requires ``UPDATE`` , ``DELETE``
, or ``TRUNCATE`` privilege on the locked ``relation`` unless
``bdr.backwards_compatibility`` is set to 30618 or lower.

.. _example-1:

Example
^^^^^^^

::

   pgddb=# SELECT bdr.local_group_slot_name();
    local_group_slot_name
   - ----------------------
    bdr_pgddb_bdrgroup

bdr.group_lease_override
^^^^^^^^^^^^^^^^^^^^^^^^

Manually transfers or cancels node group lease ownership. Under normal
operating conditions, PGD nodes transfer leases automatically. However,
if a lease holder crashes, stops, or becomes disconnected from
consensus, other nodes cannot acquire the lease until the current lease
expires. This function bypasses that waiting period.

..  Danger::
   Use this function only if you are certain the previous lease holder is not accessing the resource anymore. For example, manually transferring an analytics lease while the old leader is disconnected from consensus but still active, can result in both nodes writing to the same store, causing duplicate data.

.. _parameters-5:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_group`,Name of the group where the lease override is applied.
  `node`,Name of the node that will hold the lease.
  `duration`,Duration of the lease in seconds. Providing a value of `0` or less cancels the current lease.
  `kind`,"The lease kind (for example, `analytics`)."
  `local_only`,"When `false` (default), the override is sent over consensus to all nodes. When `true`, the override only affects the local node. Use `true` only if consensus is lost."

bdr.local_group_slot_name
^^^^^^^^^^^^^^^^^^^^^^^^^

Returns the name of the group slot on the local node.

bdr.node_group_type
^^^^^^^^^^^^^^^^^^^

Returns the type of the given node group. Returned value is the same as
what was passed to ``bdr.create_node_group()`` when the node group was
created, except ``global`` is returned if the ``node_group_type`` was
passed as NULL when the group was created.

.. _example-2:

Example
^^^^^^^

::

   pgddb=# SELECT bdr.node_group_type(bdrgroup);
    node_group_type
   - ----------------
    global

bdr.replicate_ddl_command
^^^^^^^^^^^^^^^^^^^^^^^^^

Function to replicate a DDL command to a group of nodes.

.. _synopsis-15:

Synopsis
^^^^^^^^

::

   bdr.replicate_ddl_command(ddl_cmd text,
                             replication_sets text[],
                             ddl_locking text,
                             execute_locally bool)

.. _parameters-6:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `ddl_cmd`,DDL command to execute.
  `replication_sets`,"An array of replication set names to apply the `ddlcommand` to. If NULL (or the function is passed only the `ddlcommand`), this parameter is set to the active PGD groups's default replication set."
  `ddl_locking`,A string that sets the  :ref:`pg_upgrade <Running DDL safely>`  value while replicating. Defaults to the GUC value for `bdr.ddl_locking` on the local system that's running `replicate_ddl_command`.
  `execute_locally`,A Boolean that determines whether the DDL command executes locally. Defaults to true.

.. _notes-1:

Notes
^^^^^

The only required parameter of this function is ``ddl_cmd`` .

``bdr.replicate_ddl_command()`` always replicates the command and is
unaffected by the setting of :ref:`bdr.ddl_replication <bdr.ddl_replication>` 

.

bdr.run_on_all_nodes
^^^^^^^^^^^^^^^^^^^^

Function to run a query on all nodes.

..  Warning::
   This function runs an arbitrary query on a remote node with the privileges of the user used for the internode connections as specified in the node's DSN. Use caution when granting privileges to this function.

.. _synopsis-16:

Synopsis
^^^^^^^^

::

   bdr.run_on_all_nodes(query text)

.. _parameters-7:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `query`,Arbitrary query to execute.

.. _notes-2:

Notes
^^^^^

This function connects to other nodes and executes the query, returning
a result from each of them in JSON format. Multiple rows might be
returned from each node, encoded as a JSON array. Any errors, such as
being unable to connect because a node is down, are shown in the
response field. No explicit statement_timeout or other runtime
parameters are set, so defaults are used.

This function doesn’t go through normal replication. It uses direct
client connection to all known nodes. By default, the connection is
created with ``bdr.ddl_replication = off`` , since the commands are
already being sent to all of the nodes in the cluster.

In PGD 6 and later, this function also sets ``bdr.xact_replication=off``
on the connection to ensure that transaction run locally only when the
command is executed on another node.

Be careful when using this function since you risk breaking replication
and causing inconsistencies between nodes. Use either transparent DDL
replication or ``bdr.replicate_ddl_command()`` to replicate DDL. DDL
might be blocked in a future release.

.. _example-3:

Example
^^^^^^^

It’s useful to use this function in monitoring, for example, as in the
following query:

::

   SELECT bdr.run_on_all_nodes($$
       SELECT local_slot_name, origin_name, target_name, replay_lag_size
         FROM bdr.node_slots
        WHERE origin_name IS NOT NULL
   $$);

This query returns something like this on a two-node cluster:

::

   [
       {
           "dsn": "host=node1 port=5432 dbname=pgddb user=postgres ",
           "node_id": "2232128708",
           "response": {
               "command_status": "SELECT 1",
               "command_tuples": [
                   {
                       "origin_name": "node1",
                       "target_name": "node2",
                       "local_slot_name": "bdr_pgddb_bdrgroup_node2",
                       "replay_lag_size": "0 bytes"
                   }
               ]
           },
           "node_name": "node1"
       },
       {
           "dsn": "host=node2 port=5432 dbname=pgddb user=postgres ",
           "node_id": "2058684375",
           "response": {
               "command_status": "SELECT 1",
               "command_tuples": [
                   {
                       "origin_name": "node2",
                       "target_name": "node1",
                       "local_slot_name": "bdr_pgddb_bdrgroup_node1",
                       "replay_lag_size": "0 bytes"
                   }
               ]
           },
           "node_name": "node2"
       }
   ]

bdr.run_on_group
^^^^^^^^^^^^^^^^

Function to run a query on a group of nodes.

..  Warning::
   This function runs an arbitrary query on remote nodes with the privileges of the user used for the internode connections as specified in the node's DSN. Use caution when granting privileges to this function.

.. _synopsis-17:

Synopsis
^^^^^^^^

::

   bdr.run_on_group(node_group_name text, query text)

.. _parameters-8:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_group_name`,Name of the node group where the query is executed.
  `query`,Arbitrary query to execute.

.. _notes-3:

Notes
^^^^^

This function connects to other nodes and executes the query, returning
a result from each of them in JSON format. Multiple rows can be returned
from each node, encoded as a JSON array. Any errors, such as being
unable to connect because a node is down, are shown in the response
field. No explicit statement_timeout or other runtime parameters are
set, so defaults are used.

This function doesn’t go through normal replication. It uses direct
client connection to all known nodes. By default, the connection is
created with ``bdr.ddl_replication = off`` to avoid replication issues
when the same replicated DDL command is sent to multiple nodes.

In PGD 6 and later, this function also sets ``bdr.xact_replication=off``
on the connection to ensure that transactions run locally only when the
command is executed on another node.

Be careful when using this function since you risk breaking replication
and causing inconsistencies between nodes in the group. For global
schema changes, to replicate DDL, use either transparent DDL replication
or ``bdr.replicate_ddl_command()`` .

bdr.run_on_nodes
^^^^^^^^^^^^^^^^

Function to run a query on a specified list of nodes.

..  Warning::
   This function runs an arbitrary query on remote nodes with the privileges of the user used for the internode connections as specified in the node's DSN. Use caution when granting privileges to this function.

.. _synopsis-18:

Synopsis
^^^^^^^^

::

   bdr.run_on_nodes(node_names text[], query text)

.. _parameters-9:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_names`,Text ARRAY of node names where the query is executed.
  `query`,Arbitrary query to execute.

.. _notes-4:

Notes
^^^^^

This function connects to other nodes and executes the query, returning
a result from each of them in JSON format. Multiple rows can be returned
from each node, encoded as a JSON array. Any errors, such as being
unable to connect because a node is down, are shown in the response
field. No explicit statement_timeout or other runtime parameters are
set, so defaults are used.

This function doesn’t go through normal replication. It uses direct
client connection to all known nodes. By default, the connection is
created with ``bdr.ddl_replication = off`` to avoid replication issues
when the same replicated DDL command is sent to multiple nodes.

In PGD 6 and later, this function also sets ``bdr.xact_replication=off``
on the connection to ensure that transactions run locally only when the
command is executed on another node.

Be careful when using this function since you risk breaking replication
and causing inconsistencies between nodes. For global schema changes, to
replicate DDL, use either transparent DDL replication or
``bdr.replicate_ddl_command()`` .

bdr.stat_reset
^^^^^^^^^^^^^^

Resets statistics for the specified category. Running this function
clears the value of the column ``stats_reset`` in the :ref:`bdr.stat_reconcile_commit_scope <bdr.stat_reconcile_commit_scope>` 
view.

Requires ``bdr_superuser`` privileges.

.. _parameters-10:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  stats_tupe,"The category of statistics to reset. Valid values are `commit_scope` and `reconcile_commit_scope`. If `NULL`, the function defaults to `commit_scope`."

bdr.wait_for_apply_queue
^^^^^^^^^^^^^^^^^^^^^^^^

The function ``bdr.wait_for_apply_queue`` allows a PGD node to wait for
the local application of certain transactions originating from a given
PGD node. It returns only after all transactions from that peer node are
applied locally. An application or a proxy can use this function to
prevent stale reads.

For convenience, PGD provides a variant of this function for CAMO and
the CAMO partner node. See :ref:`bdr.wait\_for\_camo\_partner\_queue <Commit At Most Once>` 

.

In case a specific LSN is given, that’s the point in the recovery stream
from which the peer waits. You can use this with
``bdr.last_committed_lsn`` retrieved from that peer node on a previous
or concurrent connection.

If the given ``target_lsn`` is NULL, this function checks the local
receive buffer and uses the LSN of the last transaction received from
the given peer node, effectively waiting for all transactions already
received to be applied. This is especially useful in case the peer node
has failed and it’s not known which transactions were sent. In this
case, transactions that are still in transit or buffered on the sender
side aren’t waited for.

.. _synopsis-19:

Synopsis
^^^^^^^^

::

   bdr.wait_for_apply_queue(peer_node_name TEXT, target_lsn pg_lsn)

.. _parameters-11:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `peer_node_name`,"The name of the peer node from which incoming transactions are expected to be queued and to wait for. If NULL, waits for all peer node's apply queue to be consumed."
  `target_lsn`,"The LSN in the replication stream from the peer node to wait for, usually learned by way of `bdr.last_committed_lsn` from the peer node."

bdr.wait_for_xid_progress
^^^^^^^^^^^^^^^^^^^^^^^^^

You can use this function to wait for the given transaction (identified
by its XID) originated at the given node (identified by its node id) to
make enough progress on the cluster. The progress is defined as the
transaction being applied on a node and this node having seen all other
replication changes done before the transaction is applied.

.. _synopsis-20:

Synopsis
^^^^^^^^

::

   bdr.wait_for_xid_progress(origin_node_id oid, origin_topxid int4, allnodes boolean DEFAULT true)

.. _parameters-12:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `origin_node_id`,Node id of the node where the transaction originated.
  `origin_topxid`,XID of the transaction.
  `allnodes`,"If `true`, wait for the transaction to progress on all nodes. Otherwise, wait only for the current node."

.. _notes-5:

Notes
^^^^^

You can use the function only for those transactions that replicated a
DDL command because only those transactions are tracked currently. If a
wrong ``origin_node_id`` or ``origin_topxid`` is supplied, the function
might wait forever or until ``statement_timeout`` occurs.

bdr.wait_node_confirm_lsn
^^^^^^^^^^^^^^^^^^^^^^^^^

Wait until a node passes a certain LSN.

This function allows you to wait until the last write on this session
was replayed to one or all nodes.

Upon being called, the function waits for a node to pass a certain LSN.
If no LSN is supplied, the current wal_flush_lsn (using the
``pg_current_wal_flush_lsn()`` function) position is used on the local
node. Supplying a node name parameter tells the function to wait for
that node to pass the LSN. If no node name is supplied (by passing
NULL), the function waits until all the nodes pass the LSN.

We recommend using this function if you are using :ref:`Optimizing subscriber-only groups <Optimizing subscriber-only groups>` 

instead of :ref:`bdr.wait_slot_confirm_lsn <bdr.wait_slot_confirm_lsn>` 

.

This is because in an optimized topology, not all nodes have replication
slots, so the function ``bdr.wait_slot_confirm_lsn`` might not work as
expected. ``bdr.wait_node_confirm_lsn`` is designed to work with nodes
that don’t have replication slots, using alternative strategies to
determine the progress of a node.

If a node is currently down, isn’t updating, or simply can’t be
connected to, the wait will continue indefinitely. To avoid this
condition, set the statement_timeout to the maximum amount of time you
are prepared to wait.

.. _synopsis-21:

Synopsis
^^^^^^^^

::

   bdr.wait_node_confirm_lsn(node_name text DEFAULT NULL, target_lsn pg_lsn DEFAULT NULL)

.. _parameters-13:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_name`,"Name of the node to wait for. If NULL, waits for all nodes."
  `target_lsn`,"LSN to wait for. If NULL, uses the current wal\_flush\_lsn on the local node."

.. _notes-6:

Notes
^^^^^

Requires ``bdr_application`` privileges to use.

bdr.wait_slot_confirm_lsn
^^^^^^^^^^^^^^^^^^^^^^^^^

Allows you to wait until the last write on this session was replayed to
one or all nodes.

Waits until a slot passes a certain LSN. If no position is supplied, the
current write position is used on the local node.

If no slot name is passed, it waits until all PGD slots pass the LSN.

The function polls every 1000 ms for changes from other nodes.

If a slot is dropped concurrently, the wait ends for that slot. If a
node is currently down and isn’t updating its slot, then the wait
continues. You might want to set ``statement_timeout`` to complete
earlier in that case.

If you are using :ref:`Optimizing subscriber-only groups <Optimizing subscriber-only groups>` 

, we recommend using `bdr.wait_node_confirm_lsn <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/functions#bdrwait_node_confirm_lsn>`_ 

instead.

.. _synopsis-22:

Synopsis
^^^^^^^^

::

   bdr.wait_slot_confirm_lsn(slot_name text DEFAULT NULL, target_lsn pg_lsn DEFAULT NULL)

.. _notes-7:

Notes
^^^^^

Requires ``bdr_application`` privileges to use.

.. _parameters-14:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `slot_name`,"Name of the replication slot to wait for. If NULL, waits for all PGD slots."
  `target_lsn`,"LSN to wait for. If NULL, uses the current write LSN on the local node."

Global advisory locks
---------------------

PGD supports global advisory locks. These locks are similar to the
advisory locks available in PostgreSQL except that the advisory locks
supported by PGD are global. They follow semantics similar to DDL locks.
So an advisory lock is obtained by majority consensus and can be used
even if one or more nodes are down or lagging behind, as long as a
majority of all nodes can work together.

Currently only EXCLUSIVE locks are supported. So if another node or
another backend on the same node has already acquired the advisory lock
on the object, then other nodes or backends must wait for the lock to be
released.

Advisory lock is transactional in nature. So the lock is released when
the transaction ends unless you explicitly release it before the end of
the transaction. In this case, it becomes available as soon as it’s
released. Session-level advisory locks aren’t currently supported.

Global advisory locks are reentrant. So if the same resource is locked
three times, you must then unlock it three times to release it for use
in other sessions.

bdr.global_advisory_lock
^^^^^^^^^^^^^^^^^^^^^^^^

This function acquires an EXCLUSIVE lock on the provided object. If the
lock isn’t available, then it waits until the lock becomes available or
the ``bdr.global_lock_timeout`` is reached.

.. _synopsis-23:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.global_advisory_lock(key bigint)

.. _parameters-15:

parameters
^^^^^^^^^^

- ``key`` — The object on which an advisory lock is acquired.

.. _synopsis-24:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.global_advisory_lock(key1 integer, key2 integer)

.. _parameters-16:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `key1`,First part of the composite key.
  `key2`,Second part of the composite key.

bdr.global_advisory_unlock
^^^^^^^^^^^^^^^^^^^^^^^^^^

This function releases a previously acquired lock on the
application-defined source. The lock must have been obtained in the same
transaction by the application. Otherwise, an error is raised.

.. _synopsis-25:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.global_advisory_unlock(key bigint)

.. _parameters-17:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `key`,The object on which an advisory lock is acquired.

.. _synopsis-26:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.global_advisory_unlock(key1 integer, key2 integer)

.. _parameters-18:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `key1`,First part of the composite key.
  `key2`,Second part of the composite key.

Monitoring functions
--------------------

bdr.monitor_group_versions
^^^^^^^^^^^^^^^^^^^^^^^^^^

To provide a cluster-wide version check, this function uses PGD version
information returned from the view ``bdr.group_version_details`` .

.. _synopsis-27:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.monitor_group_versions()

.. _notes-8:

Notes
^^^^^

This function returns a record with fields ``status`` and ``message`` ,
as explained in :ref:`Monitoring Parallel Apply <Monitoring Parallel Apply>`  .

This function calls ``bdr.run_on_all_nodes()`` .

bdr.monitor_group_raft
^^^^^^^^^^^^^^^^^^^^^^

To provide a cluster-wide Raft check, this function uses PGD Raft
information returned from the view ``bdr.group_raft_details`` .

.. _synopsis-28:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.monitor_group_raft()

.. _parameters-19:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_group_name`,The node group name to check.

.. _notes-9:

Notes
^^^^^

This function returns a record with fields ``status`` and ``message`` ,
as explained in :ref:`Monitoring Parallel Apply <Monitoring Parallel Apply>`  .

This function calls ``bdr.run_on_all_nodes()`` .

bdr.monitor_local_replslots
^^^^^^^^^^^^^^^^^^^^^^^^^^^

This function uses replication slot status information returned from the
view ``pg_replication_slots`` (slot active or inactive) to provide a
local check considering all replication slots except the PGD group
slots.

This function also provides status information on subscriber-only nodes
that are operating as subscriber-only group leaders in a PGD cluster
when :ref:`Optimizing subscriber-only groups <Optimizing subscriber-only groups>`  is enabled.

.. _synopsis-29:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.monitor_local_replslots()

.. _notes-10:

Notes
^^^^^

This function returns a record with fields ``status`` and ``message`` .

.. csv-table::
  :header: Status,Message
  :widths: 10,30
  :align: left
  :class: longtable

  UNKNOWN,This node is not part of any BDR group
  OK,All BDR replication slots are working correctly
  OK,This node is part of a subscriber-only group
  CRITICAL,There is at least 1 BDR replication slot which is inactive
  CRITICAL,There is at least 1 BDR replication slot which is missing

Further explaination is available in :ref:`Monitoring replication slots <Monitoring replication slots>`  .

bdr.ts_metrics
^^^^^^^^^^^^^^

This function lists every registered timeseries metric definition. PGD
Monitor uses this registry to give the same metric names and
descriptions across SQL, its REST API, its web UI, and its Prometheus
``/metrics`` endpoint.

.. _synopsis-30:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.ts_metrics()

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `metric_id`,Internal numeric identifier for the metric.
  `metric_name`,"The metric's name. The same name is used in the REST API, the web UI, and the Prometheus `/metrics` endpoint."
  `kind`,"The metric's kind, `gauge` or `histogram`."
  `value_type`,"The type used to store sampled values, `int8` or `float8`."
  `display_kind`,"How the PGD Monitor web UI charts summarize the metric when zoomed out to a coarser time range (mean, mean with a minimum/maximum band, minimum, or maximum)."
  `unit`,"The metric's unit, for example bytes, percent, or seconds."
  `description`,A description of the metric.

.. _notes-11:

Notes
^^^^^

Executable by roles with ``bdr_superuser`` , ``bdr_read_all_stats`` , or
``bdr_monitor`` membership.

See :ref:`Monitoring metrics reference <Monitoring metrics reference>` 

for the full list of registered metrics, and :ref:`Using the PGD Monitor <Using the PGD Monitor>`  for the
interfaces that expose them.

bdr.ts_latest
^^^^^^^^^^^^^

This function reads the most recently sampled value for a named
timeseries metric.

.. _synopsis-31:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.ts_latest(metric_name text)

.. _parameters-20:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `metric_name`,"The metric to read, matching a `metric_name` value from `bdr.ts_metrics()`."

.. _output-columns-1:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `ts`,Timestamp of the sample.
  `value_int`,"The sampled value, when the metric's `value_type` is `int8`."
  `value_float`,"The sampled value, when the metric's `value_type` is `float8`."

.. _notes-12:

Notes
^^^^^

Executable by roles with ``bdr_superuser`` , ``bdr_read_all_stats`` , or
``bdr_monitor`` membership.

bdr.ts_errors
^^^^^^^^^^^^^

This function reads recent errors captured by the timeseries error ring
buffer, which backs the PGD Monitor web UI’s Error Log page.

.. _synopsis-32:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.ts_errors(max_entries int DEFAULT 50)

.. _parameters-21:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `max_entries`,Maximum number of error entries to return. Defaults to 50.

.. _output-columns-2:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `ts`,Timestamp of the error.
  `sqlerrcode`,The error's SQLSTATE code.
  `message`,The error message.
  `detail`,"Additional detail for the error, if any."

.. _notes-13:

Notes
^^^^^

Executable by roles with ``bdr_superuser`` , ``bdr_read_all_stats`` , or
``bdr_monitor`` membership.

bdr.ts_samples
^^^^^^^^^^^^^^

This function reads historical timeseries samples for a metric at a
given downsampling frequency level, retrieved over IPC from the monitor
background worker. It backs the timeseries charts in the PGD Monitor web
UI.

.. _synopsis-33:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.ts_samples(metric_name text, level int DEFAULT 0)

.. _parameters-22:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `metric_name`,"The metric to read, matching a `metric_name` value from `bdr.ts_metrics()`."
  `level`,"The downsampling frequency level to read, from `0` (finest) to `4` (coarsest). Defaults to `0`."

.. _output-columns-3:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `ts`,Timestamp of the sample.
  `value`,"The sampled value, or the mean value for a downsampled window."
  `min`,"The minimum value in the sample window, for a downsampled window."
  `max`,"The maximum value in the sample window, for a downsampled window."

.. _notes-14:

Notes
^^^^^

Executable by roles with ``bdr_superuser`` , ``bdr_read_all_stats`` , or
``bdr_monitor`` membership.

bdr.ts_histogram
^^^^^^^^^^^^^^^^

For a histogram-kind metric, such as ``pgd_commit_latency_milliseconds``
, this function reads percentile summaries (p50, p90, p99) at a given
downsampling frequency level.

.. _synopsis-34:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.ts_histogram(metric_name text, level int DEFAULT 3)

.. _parameters-23:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `metric_name`,"The histogram metric to read, matching a `metric_name` value from `bdr.ts_metrics()`."
  `level`,"The downsampling frequency level to read, from `3` to `4`. Defaults to `3`."

.. _output-columns-4:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `ts`,Timestamp of the sample window.
  `p50`,50th percentile value in the window.
  `p90`,90th percentile value in the window.
  `p99`,99th percentile value in the window.
  `count`,Number of observations in the window.
  `sum`,Sum of observations in the window.
  `overflow_count`,Observations that exceeded the histogram's largest bucket boundary.

.. _notes-15:

Notes
^^^^^

Executable by roles with ``bdr_superuser`` , ``bdr_read_all_stats`` , or
``bdr_monitor`` membership.

bdr.ts_histogram_distribution
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For a histogram-kind metric, this function returns per-bucket
observation counts aggregated across all samples at a given downsampling
frequency level.

.. _synopsis-35:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.ts_histogram_distribution(metric_name text, level int DEFAULT 3)

.. _parameters-24:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `metric_name`,"The histogram metric to read, matching a `metric_name` value from `bdr.ts_metrics()`."
  `level`,"The downsampling frequency level to read, from `3` to `4`. Defaults to `3`."

.. _output-columns-5:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `bucket_upper_ms`,"Upper boundary of the bucket, in milliseconds."
  `count`,Number of observations in the bucket.

.. _notes-16:

Notes
^^^^^

Executable by roles with ``bdr_superuser`` , ``bdr_read_all_stats`` , or
``bdr_monitor`` membership.

bdr.ts_histogram_cumulative
^^^^^^^^^^^^^^^^^^^^^^^^^^^

For a histogram-kind metric, this function returns
cumulative-since-start bucket counters, one row per bucket boundary
ending with ``+Inf`` . This is the SQL equivalent of the ``_bucket`` ,
``_sum`` , and ``_count`` series that Prometheus histograms expose.

.. _synopsis-36:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.ts_histogram_cumulative(metric_name text)

.. _parameters-25:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `metric_name`,"The histogram metric to read, matching a `metric_name` value from `bdr.ts_metrics()`."

.. _output-columns-6:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `bucket_le_ms`,"Bucket boundary, in milliseconds (the bucket covers values less than or equal to this boundary)."
  `cumulative_count`,Cumulative number of observations at or below the boundary.
  `sum_ms`,"Sum of all observations, in milliseconds."
  `total_count`,Total number of observations across all buckets.

.. _notes-17:

Notes
^^^^^

Executable by roles with ``bdr_superuser`` , ``bdr_read_all_stats`` , or
``bdr_monitor`` membership. Buckets are cumulative monotone counters,
matching the convention ``histogram_quantile()`` and ``rate()`` expect
in PromQL.

bdr.wal_sender_stats
^^^^^^^^^^^^^^^^^^^^

If the :ref:`Decoding worker <Decoding worker>`  is enabled, this function shows information about
the decoder slot and current logical change record (LCR) segment file
being read by each WAL sender.

.. _synopsis-37:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.wal_sender_stats()

.. _output-columns-7:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `pid`,PID of the WAL sender. (Corresponds to the `pid` column of `pg_stat_replication`).
  `is_using_lcr`,Whether the WAL sender is sending LCR files.
  `decoder_slot_name`,Name of the decoder replication slot.
  `lcr_file_name`,Name of the current LCR file.

bdr.get_decoding_worker_stat
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If the :ref:`Decoding worker <Decoding worker>`  is enabled, this function shows information about
the state of the decoding worker associated with the current database.
This also provides more granular information about decoding worker
progress than is available via ``pg_replication_slots`` .

.. _synopsis-38:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.get_decoding_worker_stat()

.. _output-columns-8:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `pid`,The PID of the decoding worker. (Corresponds to the column `active_pid` in `pg_replication_slots`.)
  `decoded_upto_lsn`,LSN up to which the decoding worker read transactional logs.
  `waiting`,Whether the decoding worker is waiting for new WAL.
  `waiting_for_lsn`,The LSN of the next expected WAL.

.. _notes-18:

Notes
^^^^^

For details, see :ref:`Monitoring WAL senders using LCR <Monitoring WAL senders using LCR>`  .

bdr.lag_control
^^^^^^^^^^^^^^^

If :ref:`Lag Control <Lag Control>`  is enabled, this function shows information about the
commit delay and number of nodes conforming to their configured lag
measure for the local node and current database.

.. _synopsis-39:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.lag_control()

.. _output-columns-9:

Output columns
^^^^^^^^^^^^^^

.. csv-table::
  :header: Column name,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `commit_scope_id`,OID of the commit scope (see  :ref:``bdr.commit_scopes`   columns<`bdr.commit_scopes`   columns>` ).
  `sessions`,Number of sessions referencing the lag control entry.
  `current_commit_delay`,"Current runtime commit delay, in fractional milliseconds."
  `maximum_commit_delay`,"Configured maximum commit delay, in fractional milliseconds."
  `commit_delay_adjust`,"Change to runtime commit delay possible during a sample interval, in fractional milliseconds."
  `current_conforming_nodes`,Current runtime number of nodes conforming to lag measures.
  `minimum_conforming_nodes`,"Configured minimum number of nodes required to conform to lag measures, below which a commit delay adjustment is applied."
  `lag_bytes_threshold`,"Lag size at which a commit delay is applied, in kilobytes."
  `maximum_lag_bytes`,"Configured maximum lag size, in kilobytes."
  `lag_time_threshold`,"Lag time at which a commit delay is applied, in milliseconds."
  `maximum_lag_time`,"Configured maximum lag time, in milliseconds."
  `sample_interval`,"Configured minimum time between lag samples and possible commit delay adjustments, in milliseconds."

Routing functions
-----------------

bdr.routing_leadership_transfer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Changing the routing leader transfers the leadership of the node group
to another node. If ``leader_name`` is ``NULL`` , the system
automatically selects the most appropriate node based on write leader
progress.

.. _synopsis-40:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.routing_leadership_transfer(node_group_name text,
                 leader_name text DEFAULT NULL,
                 transfer_method text DEFAULT strict,
                 transfer_timeout interval DEFAULT 10s);

.. _parameters-26:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Name,Type,Default,Description
  :widths: 15,12,12,20
  :align: left
  :class: longtable

  `node_group_name`,text,"",Name of group where the leadership transfer is requested.
  `leader_name`,text,`NULL`,"Name of node that will become write leader. If NULL, the system automatically selects the most appropriate candidate based on write leader progress."
  `transfer_method`,text,`'strict'`,"Type of the transfer. It can be `'fast'` or the default, `'strict'`, which checks the maximum lag."
  `transfer_timeout`,interval,`'10s'`,"Timeout of the leadership transfer. Default is 10 seconds. When using auto-selection (`leader_name` is NULL), the system retries selecting the furthest-progressed node until the timeout is reached."

.. _notes-19:

Notes
^^^^^

When ``leader_name`` is ``NULL`` , the function selects the best
candidate node by identifying the node with the furthest write leader
progress and retrying selection until a suitable candidate is available
or the timeout is reached. Automatic selection is useful for automated
failover scenarios where manual node selection isn’t required. When
using Quorum Commit as the commit scope kind, pass ``NULL`` for
``leader_name`` to trigger automatic selection of the most appropriate
node.

Selecting a candidate automatically
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

During automatic leader selection (when ``leader_name`` is ``NULL`` ):

- Route-fenced nodes (nodes with ``route_fence`` enabled via
  :ref:`bdr.alter_node_option <bdr.alter_node_option>`  ) aren’t considered candidates for leadership transfer.

- Nodes with ``route_writes`` set to ``false`` aren’t considered
  candidates for leadership transfer.

- The ``route_priority`` option doesn’t influence automatic selection.
  Selection is based solely on write leader progress among eligible
  nodes.

Running in a mixed-version cluster
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Automatic leader selection (passing ``NULL`` for ``leader_name`` )
requires PGD 6.4 and Raft protocol version 6004 or above.

When calling the function on pre-6.4 nodes, the function exits without
performing any action, as the older function signature requires a
non-``NULL`` ``leader_name`` parameter.

When calling the function on 6.4 nodes, the behavior depends on the Raft
protocol version:

- If Raft protocol version is below 6004 and a PGD 6.4 or above node is
  the Raft leader, the function returns
  ``ERROR: leadership auto transfer is supported from raft protocol version 6004 and above``
  .

- If a pre-6.4 node is the Raft leader, the operation times out and
  returns ``ERROR: switchover timed out`` .

For reliable automatic leader selection, ensure all nodes in the cluster
are running PGD 6.4 or later with Raft protocol version 6004 or above.

CAMO functions
--------------

CAMO requires that a client actively participates in the committing of a
transaction by following the transactions progress. The functions listed
here are used for that purpose and explained in :ref:`Commit At Most Once <Commit At Most Once>`  .

bdr.is_camo_partner_connected
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Allows checking of the connection status of a CAMO partner node
configured in pair mode. There currently is no equivalent for CAMO used
with eager replication.

.. _synopsis-41:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.is_camo_partner_connected()

.. _return-value-1:

Return value
^^^^^^^^^^^^

A Boolean value indicating whether the CAMO partner is currently
connected to a WAL sender process on the local node and therefore can
receive transactional data and send back confirmations.

bdr.is_camo_partner_ready
^^^^^^^^^^^^^^^^^^^^^^^^^

Allows checking of the readiness status of a CAMO partner node
configured in pair mode. Underneath, this triggers the switch to and
from local mode.

.. _synopsis-42:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.is_camo_partner_ready()

.. _return-value-2:

Return value
^^^^^^^^^^^^

A Boolean value indicating whether the CAMO partner can reasonably be
expected to confirm transactions originating from the local node in a
timely manner, that is, before ``timeout`` for ``TO ASYNC`` expires.

..  Note::
   This function queries the past or current state. A positive return value doesn't indicate whether the CAMO partner can confirm future transactions.

bdr.get_configured_camo_partner
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This function shows the local node’s CAMO partner (configured by pair
mode).

.. _synopsis-43:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.get_configured_camo_partner()

bdr.wait_for_camo_partner_queue
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The function is a wrapper around ``bdr.wait_for_apply_queue`` defaulting
to query the CAMO partner node. It returns an error if the local node
isn’t part of a CAMO pair.

.. _synopsis-44:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.wait_for_camo_partner_queue()

bdr.camo_transactions_resolved
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This function begins a wait for CAMO transactions to be fully resolved.

.. _synopsis-45:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.camo_transactions_resolved()

bdr.logical_transaction_status
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To check the status of a transaction that was being committed when the
node failed, the application must use this function, passing as
parameters the node id of the node the transaction originated from and
the transaction id on the origin node.

.. _synopsis-46:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.logical_transaction_status(node_id OID, xid OID,
                                  require_camo_partner boolean DEFAULT true)

.. _parameters-27:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `node_id`,"The node id of the PGD node the transaction originates from, usually retrieved by the client before `COMMIT` from the  :ref:`pg_upgrade <Monitoring through SQL>`  `bdr.local_node_id`."
  `xid`,"The transaction id on the origin node, usually retrieved by the client before `COMMIT` from the  :ref:`pg_upgrade <Monitoring through SQL>`  `transaction_id`."
  `require_camo_partner`,Defaults to true and enables configuration checks. Set to false to disable these checks and query the status of a transaction that wasn't a CAMO transaction.

.. _return-value-3:

Return value
^^^^^^^^^^^^

The function returns one of these results:

- ``'committed'::TEXT`` — The transaction was committed, is visible on
  both nodes of the CAMO pair, and is eventually replicated to all other
  PGD nodes. No need for the client to retry it.

- ``'aborted'::TEXT`` — The transaction was aborted and isn’t replicated
  to any other PGD node. The client needs to either retry it or escalate
  the failure to commit the transaction.

- ``'in progress'::TEXT`` — The transaction is still in progress on this
  local node and wasn’t committed or aborted yet. The transaction might
  be in the COMMIT phase, waiting for the CAMO partner to confirm or
  deny the commit. The recommended client reaction is to disconnect from
  the origin node and reconnect to the CAMO partner to query that
  instead. With a load balancer or proxy in between, where the client
  lacks control over which node gets queried, the client can only poll
  repeatedly until the status switches to either ``'committed'`` or
  ``'aborted'`` .

For eager all-node replication, peer nodes yield this result for
transactions that aren’t yet committed or aborted. Even transactions not
yet replicated (or not even started on the origin node) might yield an
``in progress`` result on a peer PGD node in this case. However, the
client must not query the transaction status prior to attempting to
commit on the origin.

- ``'unknown'::TEXT`` — The transaction specified is unknown because
  it’s either in the future, not replicated to that specific node yet,
  or too far in the past. The status of such a transaction isn’t yet or
  is no longer known. This return value is a sign of improper use by the
  client.

The client must be prepared to retry the function call on error.

Commit Scope functions
----------------------

bdr.add_commit_scope
^^^^^^^^^^^^^^^^^^^^

**Deprecated**. Use `bdr.create_commit_scope <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/functions#bdrcreate_commit_scope>`_  instead. Previously, this function
was used to add a commit scope to a node group. It’s now deprecated and
will emit a warning until it is removed in a future release, at which
point it will raise an error.

bdr.create_commit_scope
^^^^^^^^^^^^^^^^^^^^^^^

``bdr.create_commit_scope`` creates a rule for the given commit scope
name and origin node group. If the rule is the same for all nodes in the
EDB Postgres Distributed cluster, invoking this function once for the
top-level node group is enough to fully define the commit scope.

Alternatively, you can invoke it multiple times with the same
``commit_scope_name`` but different origin node groups and rules for
commit scopes that vary depending on the origin of the transaction.

.. _synopsis-47:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.create_commit_scope(
       commit_scope_name NAME,
       origin_node_group NAME,
       rule TEXT,
       wait_for_ready boolean DEFAULT true)

Note
^^^^

``bdr.create_commit_scope`` replaces the deprecated `bdr.add_commit_scope <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/functions#bdradd_commit_scope>`_ 
function. Unlike ``add_commit_scope`` , it doesn’t silently overwrite
existing commit scopes when the same name is used. Instead, an error is
reported.

bdr.alter_commit_scope
^^^^^^^^^^^^^^^^^^^^^^

``bdr.alter_commit_scope`` allows you to change a specific rule for a
single origin node group in a commit scope.

.. _synopsis-48:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.alter_commit_scope(
       commit_scope_name NAME,
       origin_node_group NAME,
       rule TEXT)

bdr.drop_commit_scope
^^^^^^^^^^^^^^^^^^^^^

Drops a single rule in a commit scope. If you define multiple rules for
the commit scope, you must invoke this function once per rule to fully
remove the entire commit scope.

.. _synopsis-49:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.drop_commit_scope(
       commit_scope_name NAME,
       origin_node_group NAME)

..  Note::
   Dropping a commit scope that's still used as default by a node group isn't allowed.

bdr.remove_commit_scope
^^^^^^^^^^^^^^^^^^^^^^^

**Deprecated**. Use `bdr.drop_commit_scope <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/functions#bdrdrop_commit_scope>`_  instead. Previously, this function
was used to remove a commit scope from a node group. It’s now deprecated
and will emit a warning until it is removed in a future release, at
which point it will raise an error.

Apply error policy functions
----------------------------

These functions let you review, resolve, and analyze replication
failures recorded by the apply error policy. See the
``apply_error_policy`` , ``apply_error_max_retries`` ,
``apply_error_max_skips`` , and ``apply_error_max_record_size`` options
in :ref:`Group configuration using bdr.alter_node_group_option() <Group configuration using bdr.alter_node_group_option()>`  for configuration details. For a step-by-step guide to
configuring a policy and resolving failures, see :ref:`Handling replication apply errors <Handling replication apply errors>`  .

bdr.skip_failed_change
^^^^^^^^^^^^^^^^^^^^^^

Marks a single captured change in ``bdr.pending_failed_changes`` as
skipped. The change status is updated to ``skipped`` but the row isn’t
deleted.

.. _synopsis-50:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.skip_failed_change(change_id bigint)

.. _parameters-28:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `change_id`,Primary key of the row in `bdr.pending_failed_changes` to skip.

bdr.skip_failed_transaction
^^^^^^^^^^^^^^^^^^^^^^^^^^^

Marks all pending changes in a failed transaction as skipped and
optionally re-enables the subscription.

.. _synopsis-51:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.skip_failed_transaction(
       subscription_name name,
       remote_xid xid,
       remote_commit_lsn pg_lsn,
       dry_run boolean DEFAULT false,
       re_enable boolean DEFAULT false
   )

   bdr.skip_failed_transaction(
       failed_txn_id uuid,
       dry_run boolean DEFAULT false,
       re_enable boolean DEFAULT false
   )

.. _parameters-29:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `subscription_name`,Name of the subscription.
  `remote_xid`,Transaction ID on the origin node.
  `remote_commit_lsn`,Commit LSN of the failed transaction on the origin node.
  `failed_txn_id`,"UUID of the failed transaction from `bdr.pending_failed_changes`. Use as a convenient alternative to specifying `subscription_name`, `remote_xid`, and `remote_commit_lsn`."
  `dry_run`,"When `true`, returns the count of changes that would be skipped without modifying anything. Default is `false`."
  `re_enable`,"When `true`, re-enables the subscription after skipping. Default is `false`."

.. _return-value-4:

Return value
^^^^^^^^^^^^

Returns the number of changes skipped. Returns ``0`` if the transaction
was already resolved (no error is raised).

bdr.apply_failed_change
^^^^^^^^^^^^^^^^^^^^^^^

Retries applying a single captured change.

.. _synopsis-52:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.apply_failed_change(change_id bigint)

.. _parameters-30:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `change_id`,Primary key of the row in `bdr.pending_failed_changes` to apply.

bdr.apply_failed_transaction
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Retries all pending changes for a failed transaction. Defaults to
dry-run mode so you can preview the outcome before committing.

.. _synopsis-53:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.apply_failed_transaction(
       subscription_name name,
       remote_xid xid,
       remote_commit_lsn pg_lsn,
       dry_run boolean DEFAULT true,
       re_enable boolean DEFAULT false
   )

   bdr.apply_failed_transaction(
       failed_txn_id uuid,
       dry_run boolean DEFAULT true,
       re_enable boolean DEFAULT false
   )

.. _parameters-31:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `subscription_name`,Name of the subscription.
  `remote_xid`,Transaction ID on the origin node.
  `remote_commit_lsn`,Commit LSN of the failed transaction on the origin node.
  `failed_txn_id`,"UUID of the failed transaction from `bdr.pending_failed_changes`. Use as a convenient alternative to specifying `subscription_name`, `remote_xid`, and `remote_commit_lsn`."
  `dry_run`,"When `true` (the default), shows what would happen without actually applying changes (rolls back at the end). Pass `false` to execute."
  `re_enable`,"When `true` and `dry_run` is `false`, re-enables the subscription after applying. Default is `false`."

.. _return-value-5:

Return value
^^^^^^^^^^^^

Returns one row per change with the following columns.

.. csv-table::
  :header: Column,Type,Description
  :widths: 12,15,25
  :align: left
  :class: longtable

  `change_num`,`integer`,Order of the change within the transaction.
  `change_id`,`bigint`,Primary key of the row in `bdr.pending_failed_changes`.
  `operation`,`text`,"Operation type: `INSERT`, `UPDATE`, `DELETE`, `SQL`, `DDL`, or `TRUNCATE`."
  `schema_name`,`text`,Schema of the target table.
  `table_name`,`text`,Name of the target table.
  `status`,`text`,"Result of the apply attempt. In dry-run mode, values are `would_apply`, `would_skip`, or `would_error`. When executed, values are `apply`, `skip`, or `error`."
  `message`,`text`,"Error message if the change failed, or `NULL` on success."

If an error occurs during apply, the function returns results for all
changes up to and including the failed one. If the transaction was
already resolved, the function returns 0 rows without raising an error.

bdr.modify_failed_change
^^^^^^^^^^^^^^^^^^^^^^^^

Edits the column values of a single captured change before retrying the
change. Updates the ``modified_values`` column in
``bdr.pending_failed_changes`` and sets the change status to
``modified`` .

.. _synopsis-54:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.modify_failed_change(change_id bigint, new_values jsonb)

.. _parameters-32:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `change_id`,Primary key of the row in `bdr.pending_failed_changes` to modify.
  `new_values`,JSONB object containing the updated column values to use when the change is retried.

bdr.reconstruct_change_sql
^^^^^^^^^^^^^^^^^^^^^^^^^^

Generates executable SQL for a single captured change. The
``bdr.pending_failed_changes_sql`` view uses this function internally.

.. _synopsis-55:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.reconstruct_change_sql(
       operation text,
       schema_name name,
       table_name name,
       key_columns jsonb,
       old_values jsonb,
       new_values jsonb
   )

.. _parameters-33:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `operation`,"Operation type. Valid values are `INSERT`, `UPDATE`, `DELETE`, `SQL`, `DDL`, and `TRUNCATE`."
  `schema_name`,Schema of the target table.
  `table_name`,Name of the target table.
  `key_columns`,JSONB object containing the primary key column values.
  `old_values`,"JSONB object containing the before-image column values, used for `UPDATE` and `DELETE` operations."
  `new_values`,"JSONB object containing the after-image column values for `INSERT` and `UPDATE` operations, or the SQL text for `DDL` and `SQL` operations."

.. _return-value-6:

Return value
^^^^^^^^^^^^

Returns a ``text`` string containing the reconstructed SQL statement.
DDL operations include a ``SET search_path`` prefix when a search path
was in effect during the original execution.

bdr.reconstruct_failed_transaction
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Generates a complete SQL script for all captured changes in a failed
transaction, wrapped in ``BEGIN`` and ``COMMIT`` .

.. _synopsis-56:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.reconstruct_failed_transaction(
       subscription_name name,
       remote_xid xid,
       remote_commit_lsn pg_lsn
   )

   bdr.reconstruct_failed_transaction(failed_txn_id uuid)

.. _parameters-34:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `subscription_name`,Name of the subscription.
  `remote_xid`,Transaction ID on the origin node.
  `remote_commit_lsn`,Commit LSN of the failed transaction on the origin node.
  `failed_txn_id`,"UUID of the failed transaction from `bdr.pending_failed_changes`. Use as a convenient alternative to specifying `subscription_name`, `remote_xid`, and `remote_commit_lsn`."

.. _return-value-7:

Return value
^^^^^^^^^^^^

Returns a ``text`` SQL script containing all changes in the transaction.
Use the script to review or manually replay the complete transaction.

bdr.resolve_failed_transaction
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Marks all pending changes in a failed transaction as resolved, advances
the subscription’s position past the failed transaction, and optionally
re-enables the subscription. Defaults to dry-run mode so you can preview
the outcome before committing.

.. _synopsis-57:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.resolve_failed_transaction(
       subscription_name name,
       remote_xid xid,
       remote_commit_lsn pg_lsn,
       resolution text DEFAULT skipped,
       re_enable boolean DEFAULT false,
       dry_run boolean DEFAULT true
   )

   bdr.resolve_failed_transaction(
       failed_txn_id uuid,
       resolution text DEFAULT skipped,
       re_enable boolean DEFAULT false,
       dry_run boolean DEFAULT true
   )

.. _parameters-35:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `subscription_name`,Name of the subscription.
  `remote_xid`,Transaction ID on the origin node.
  `remote_commit_lsn`,Commit LSN of the failed transaction on the origin node.
  `failed_txn_id`,"UUID of the failed transaction from `bdr.pending_failed_changes`. Use as a convenient alternative to specifying `subscription_name`, `remote_xid`, and `remote_commit_lsn`."
  `resolution`,"Resolution action to apply. Valid values are `skipped`, `applied`, and `discarded`. Default is `skipped`."
  `re_enable`,"When `true`, re-enables the subscription after resolving. Default is `false`."
  `dry_run`,"When `true` (the default), returns a detailed preview of what the operation would do without making any changes. Pass `false` to execute."

.. _return-value-8:

Return value
^^^^^^^^^^^^

In dry-run mode, returns a ``text`` report describing the planned
changes. When ``dry_run`` is ``false`` , returns a ``text`` confirmation
of the resolution applied.

.. _notes-20:

Notes
^^^^^

Calling this function also inserts a summary record into
``bdr.resolved_transactions`` . Once resolved, you can truncate
``bdr.pending_failed_changes`` to reclaim space while retaining a
history of resolutions.

If the transaction was already resolved, the function raises an error
with details about the prior resolution.

bdr.analyze_skip_impact
^^^^^^^^^^^^^^^^^^^^^^^

Analyzes the safety and potential data impact of skipping a single
captured change. Returns a risk assessment based on the error type,
operation, and optionally the current local database state.

.. _synopsis-58:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.analyze_skip_impact(
       change_id bigint,
       verify_state boolean DEFAULT true,
       include_transaction_analysis boolean DEFAULT true
   )

.. _parameters-36:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `change_id`,Primary key of the row in `bdr.pending_failed_changes` to analyze.
  `verify_state`,"When `true` (the default), queries the local database to verify current state before assessing safety. Pass `false` for a faster static analysis based on error code and operation type only."
  `include_transaction_analysis`,"When `true` (the default), examines all other changes in the same transaction to detect dependencies and generate warnings."

.. _return-value-9:

Return value
^^^^^^^^^^^^

Returns a row with the following columns.

.. csv-table::
  :header: Column,Type,Description
  :widths: 12,15,25
  :align: left
  :class: longtable

  `skip_safety`,`text`,"Safety classification: `safe`, `caution`, or `risky`."
  `recommendation`,`text`,Human-readable recommendation.
  `explanation`,`text`,Detailed explanation including error context and any state verification results.
  `data_impact`,`text`,"Data impact level: `none`, `possible_divergence`, or `certain_divergence`."
  `suggested_action`,`text`,"Suggested action: `skip`, `apply_modified`, or `manual_review`."
  `dependency_warnings`,`text[]`,"Array of warnings about related changes in the transaction that will also be affected, or `NULL` if none."

Safety classifications
^^^^^^^^^^^^^^^^^^^^^^

.. csv-table::
  :header: Safety,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `safe`,Skipping the change is unlikely to cause data divergence between nodes.
  `caution`,Minor potential issues exist. Review the change before skipping.
  `risky`,Skipping the change will likely cause data divergence between nodes.

bdr.analyze_transaction_skip_impact
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Analyzes the skip safety for all pending changes in a failed
transaction, returning one row per change.

.. _synopsis-59:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.analyze_transaction_skip_impact(
       subscription_name name,
       remote_xid xid,
       remote_commit_lsn pg_lsn
   )

.. _parameters-37:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `subscription_name`,Name of the subscription.
  `remote_xid`,Transaction ID on the origin node.
  `remote_commit_lsn`,Commit LSN of the failed transaction on the origin node.

.. _return-value-10:

Return value
^^^^^^^^^^^^

Returns one row per change in the transaction, with the following
columns.

.. csv-table::
  :header: Column,Type,Description
  :widths: 12,15,25
  :align: left
  :class: longtable

  `change_seq`,`integer`,Order of the change within the transaction.
  `operation`,`text`,"Operation type: `INSERT`, `UPDATE`, `DELETE`, `SQL`, `DDL`, or `TRUNCATE`."
  `qualified_table`,`text`,Schema-qualified table name.
  `skip_safety`,`text`,"Safety classification: `safe`, `caution`, or `risky`."
  `recommendation`,`text`,Human-readable recommendation for the change.
  `has_dependents`,`boolean`,Whether other changes in the transaction may depend on the outcome of this change.

bdr.format_skip_analysis
^^^^^^^^^^^^^^^^^^^^^^^^

Returns a formatted, human-readable analysis of the skip safety for a
single captured change.

.. _synopsis-60:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.format_skip_analysis(change_id bigint)

.. _parameters-38:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `change_id`,Primary key of the row in `bdr.pending_failed_changes` to analyze.

.. _return-value-11:

Return value
^^^^^^^^^^^^

Returns a ``text`` report including the safety classification,
recommendation, explanation, and any state verification results.

bdr.alter_subscription_reset_error_counters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Resets the error retry and skip counters for a subscription to their
initial values. Use the reset after resolving replication failures to
allow the subscription’s error policy thresholds to apply fresh.

.. _synopsis-61:

Synopsis
^^^^^^^^

.. code:: sql

   bdr.alter_subscription_reset_error_counters(subscription_name name)

.. _parameters-39:

Parameters
^^^^^^^^^^

.. csv-table::
  :header: Parameter,Description
  :widths: 10,30
  :align: left
  :class: longtable

  `subscription_name`,Name of the subscription.
