diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml index 3a61e2d688932..34da42eacf539 100644 --- a/doc/src/sgml/logical-replication.sgml +++ b/doc/src/sgml/logical-replication.sgml @@ -150,57 +150,13 @@ snapshot once the transaction has committed. - - Replica Identity - - - A published table must have a replica identity - configured in order to be able to replicate UPDATE - and DELETE operations, so that appropriate rows to - update or delete can be identified on the subscriber side. - - - - By default, this is the primary key, if there is one. Another unique index - (with certain additional requirements) can also be set to be the replica - identity. If the table does not have any suitable key, then it can be set - to replica identity FULL, which means the entire row - becomes the key. When replica identity FULL is - specified, indexes can be used on the subscriber side for searching the - rows. Candidate indexes must be btree or hash, non-partial, and the - leftmost index field must be a column (not an expression) that references - the published table column. These restrictions on the non-unique index - properties adhere to some of the restrictions that are enforced for - primary keys. If there are no such suitable indexes, the search on the - subscriber side can be very inefficient, therefore replica identity - FULL should only be used as a fallback if no other - solution is possible. - - - - If a replica identity other than FULL is set on the - publisher side, a replica identity comprising the same or fewer columns - must also be set on the subscriber side. - - - - Tables with a replica identity defined as NOTHING, - DEFAULT without a primary key, or USING - INDEX with a dropped index, cannot support - UPDATE or DELETE operations when - included in a publication replicating these actions. Attempting such - operations will result in an error on the publisher. - - - - INSERT operations can proceed regardless of any replica identity. - - - - See ALTER TABLE...REPLICA IDENTITY - for details on how to set the replica identity. - - + + To be able to replicate UPDATE and + DELETE operations, a published table must have a + replica identity configured, so that the rows to be updated or deleted can + be identified on the subscriber side. See + for details. + @@ -846,6 +802,375 @@ HINT: To initiate replication, you must manually create the replication slot, e + + Replica Identity + + + replica identity + + + + Logical replication transfers changes row by row, so the apply process on + the subscriber has to determine which row of its copy of a published table + corresponds to the row that was updated or deleted on the publisher. The + replica identity of a table provides the information + needed for that: it determines which old column values are written to the + write-ahead log when a row is updated or deleted, so that those values can + be decoded later and sent to the subscriber together with the change. + + + + Every table has a replica identity, which is recorded in + pg_class.relreplident, + but it only has an effect when the table's changes are decoded logically, + that is, when is set to + logical. Only UPDATE and + DELETE depend on it: INSERT + replicates the complete new row anyway, and TRUNCATE + does not refer to individual rows, so those operations can be replicated + regardless of the replica identity. + + + + Replica Identity Modes + + + The replica identity of a table is set using + ALTER TABLE ... REPLICA IDENTITY, + which supports the four modes summarized in + . + + + + Replica Identity Modes + + + + + + + Mode + Old values written to WAL + Notes + + + + + DEFAULT + the columns of the primary key + + This is the default for user tables. If the table has no primary + key, the behavior is the same as NOTHING. + + + + USING INDEX index_name + the columns covered by the named index + + The index must be unique, must not be partial, must not be + deferrable, must not be an expression index, and must contain only + columns marked NOT NULL. If the index is dropped, + the behavior is the same as NOTHING. + + + + FULL + all columns of the old row + + A fallback for tables that have no suitable key; it is more expensive + on both sides, see + . + + + + NOTHING + nothing + + This is the default for system catalogs. UPDATE + and DELETE cannot be replicated. + + + + +
+ + + With DEFAULT and USING INDEX, the + old values of the key columns are written for every + DELETE, but for UPDATE they are + only written if at least one key column was modified; however, if the old + value of a key column is stored externally (see + ), it is always written, whether or not it + changed. With FULL, the complete old row is written + for every UPDATE and DELETE. + + + + Note that a table can be in a state where no old values are available at + all, namely when its replica identity is NOTHING, + DEFAULT without a primary key, or USING + INDEX with an index that has since been dropped. Such a table + cannot replicate UPDATE or DELETE; + see . + +
+ + + Setting and Examining the Replica Identity + + + The following example gives a table a replica identity based on a unique + index, because the table has no primary key: + +CREATE TABLE t (a int NOT NULL, b text); +CREATE UNIQUE INDEX t_a_key ON t (a); +ALTER TABLE t REPLICA IDENTITY USING INDEX t_a_key; + + + + In psql, an index that is used as the replica + identity is marked as such in the table description, while the + FULL and NOTHING modes are reported + by \d+: + +\d t + Table "public.t" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | not null | + b | text | | | +Indexes: + "t_a_key" UNIQUE, btree (a) REPLICA IDENTITY + + The setting can also be queried directly from the system catalogs: + +SELECT relreplident FROM pg_class WHERE relname = 't'; + relreplident +-------------- + i +(1 row) + + + + Changing the replica identity of a table requires an + ACCESS EXCLUSIVE lock on it, but it neither rewrites + the table nor affects data that has already been written to the + write-ahead log; it only changes what is logged for subsequent + operations. It is therefore best to set the replica identity of a table + before adding it to a publication. + + + + + Requirements on the Publisher + + + If a table is part of a publication that replicates + UPDATE or DELETE operations, and no + old values would be available for it, the operation fails on the + publisher: + +UPDATE t SET b = 'x'; +ERROR: cannot update table "t" because it does not have a replica identity and publishes updates +HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. + + The error is raised when the operation is executed, not when the table is + added to the publication, so a table that is only ever inserted into can + be published without any replica identity at all. + + + + In addition, the replica identity restricts which columns may be used by + other publication features, and it must itself be fully replicated: + + + + + + A row filter of a publication that publishes + UPDATE or DELETE may only + reference columns covered by the replica identity. See + . + + + + + + A column list of such a publication must include all columns of the + replica identity. See . + + + + + + Generated columns that are part of the replica identity must be + published, that is, the publication must either set + publish_generated_columns + to stored or list those columns in its column list. + See . + + + + + + Violations of these rules are likewise reported when an + UPDATE or DELETE is executed on the + published table. + + + + + Requirements on the Subscriber + + + The apply process uses the old values received from the publisher to + search for the local row to update or delete. The target table on the + subscriber therefore has to be searchable with exactly those values: + + + + + + If the published table has a replica identity other than + FULL, the target table must have a primary key or a + replica identity index, and its columns must all be covered by the + replica identity of the published table. In other words, a replica + identity comprising the same or fewer columns must be set on the + subscriber side. Otherwise the apply process reports an error such as: + +ERROR: publisher did not send replica identity column expected by the logical replication target relation "public.t" + + + + + + + If the target table has neither a primary key nor a replica identity + index, the published table must have replica identity + FULL, so that the whole old row is available for the + search. Otherwise the apply process reports: + +ERROR: logical replication target relation "public.t" has neither REPLICA IDENTITY index nor PRIMARY KEY and published relation does not have REPLICA IDENTITY FULL + + + + + + + The search is performed with a primary key or replica identity index if + the target table has one. When the published table has replica identity + FULL and the target table has no such index, any other + suitable index may be used instead. Candidate indexes must be btree or + hash, non-partial, and the leftmost index field must be a column (not an + expression) that references the published table column. These + restrictions on the non-unique index properties adhere to some of the + restrictions that are enforced for primary keys. If there is no suitable + index, the search falls back to a sequential scan of the whole target + table for every change, which can be very slow. + + + + + A search that is not based on a unique index can match several rows, for + example when a table with replica identity FULL + contains duplicate rows. In that case one of the matching rows is + updated or deleted, and which one that is, is not determined. + + + + + + Partitioned Tables + + + Rows are physically stored in leaf partitions, and that is also where the + old values are written to the write-ahead log. The replica identity of + each partition therefore determines whether an UPDATE or + DELETE can be replicated, also when the publication + parameter + publish_via_partition_root + is enabled. + + + + Unlike most other ALTER TABLE forms, + ALTER TABLE ... REPLICA IDENTITY does not recurse to + the partitions of a partitioned table, and partitions created later do + not inherit the setting either. The replica identity has to be set on + each partition individually: + +CREATE TABLE p (a int NOT NULL, b text) PARTITION BY RANGE (a); +CREATE TABLE p1 PARTITION OF p FOR VALUES FROM (0) TO (10); +ALTER TABLE p REPLICA IDENTITY FULL; +ALTER TABLE p1 REPLICA IDENTITY FULL; + + Note that a primary key or a unique constraint defined on the partitioned + table does exist on every partition, so replica identity + DEFAULT and a partitioned primary key work without any + per-partition setup. + + + + When publish_via_partition_root is enabled, changes + are reported using the identity and schema of the root table, and the + replica identity of that root table is announced to the subscriber. In + that case the root table and its partitions should be given matching + replica identities. + + + + + Using <literal>REPLICA IDENTITY FULL</literal> + + + Replica identity FULL makes the entire row the key, so + it works for any table. It comes at a price on both sides of the + replication setup, and should only be used as a fallback if no other + solution is possible: + + + + + + On the publisher, every UPDATE and + DELETE writes a copy of the complete old row into + the write-ahead log, which increases the amount of WAL generated, and + with it the work of decoding and of sending the changes. Values that + are stored externally are written inline, so wide rows are especially + expensive. + + + + + + On the subscriber, the search for the row to update or delete compares + all columns. Unless a suitable index exists, this requires a + sequential scan per change, as described in + . + + + + + + UPDATE and DELETE cannot be + applied on the subscriber if the table includes attributes with data + types (such as point or box) that do not have + a default operator class for btree or hash, because the old and the new + row cannot be compared. Defining a primary key or another replica + identity for the table avoids this limitation. + + + + + + So, in order of preference, use a primary key, or a unique index on + NOT NULL columns via USING INDEX, + and resort to FULL only where neither is available. + + + +
+ Row Filters diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index ff7071bef5b4d..7a2bf78e92cfd 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -986,6 +986,8 @@ WITH ( MODULUS numeric_literal, REM from the new value; however, if the old value is stored externally, it is always logged regardless of whether it changed. This option has no effect except when logical replication is in use. + See for more + information about how the replica identity is used. DEFAULT