| From: | Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com> |
|---|---|
| To: | Nathan Bossart <nathandbossart(at)gmail(dot)com> |
| Cc: | pgsql-hackers(at)postgresql(dot)org, alvherre(at)kurilemu(dot)de |
| Subject: | Re: REPACK (CONCURRENTLY) fails when replica identity index is dropped |
| Date: | 2026-08-27 19:31:27 |
| Message-ID: | CAEze2WhT2=bm8s45MjQf+YJ5Md7TTXy=8MKKOYsWspHGXxNWKA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Thu, 27 Aug 2026 at 20:26, Nathan Bossart <nathandbossart(at)gmail(dot)com> wrote:
>
> I don't fully understand the mechanics of this one, but here is a
> reproducer:
>
> CREATE TABLE t (a INT PRIMARY KEY, b INT, c TEXT);
> INSERT INTO t SELECT g, g, repeat('x', 1000) FROM generate_series(1, 1000000) g;
> CREATE UNIQUE INDEX i ON t (a);
> ALTER TABLE t REPLICA IDENTITY USING INDEX i;
> DROP INDEX i;
> REPACK (CONCURRENTLY) t;
>
> -- in a separate session, while REPACK is still running
> DELETE FROM t WHERE a = 1;
>
> This produces the following ERROR from the REPACK command:
>
> ERROR: incomplete delete info
> CONTEXT: slot "pg_repack_34213", output plugin "pgrepack", in the change callback, associated LSN 0/A6CC1ED0
> REPACK decoding worker
>
> I think this can be addressed by verifying the index exists in
> check_concurrent_repack_requirements() and erroring out if it doesn't.
I think the issue is caused by the following: REPACK's check has the
incorrect assumption that REPLICA IDENTITY USING INDEX either reverts
to DEFAULT or falls back to the behaviour of DEFAULT if the identity
index gets dropped, and thus uses GetRelationIdentityOrPK(), which
hides a lack of replica identity index. The issue shows up due to the
following garden path of data flows:
1. A table with REPLICA IDENTITY USING INDEX doesn't fall back to
REPLICA IDENTITY DEFAULT once the identity index is dropped.
2. In the catcache, the table won't fall back to rd_replidindex =
pkeyIndex when replident='i', but instead will set
rd_replidindex=InvalidOid.
See the tail end of RelationGetIndexList.
3. Then, in heap_delete, it calls ExtractReplicaIdentity() to find the
key of the deleted tuple.
3a. ExtractR_I_() checks the identity key attributes from
RelationGetIndexAttrBitmap(..., INDEX_ATTR_BITMAP_IDENTITY_KEY), which
also only uses rd_replidindex, and doesn't fall back to the primary
key index's attributes.
3b. If ExtractR_I_() doesn't have identity key attributes, it returns NULL
3c. heap_delete thus doesn't have any logical identity attributes to
log, and treats the delete operation as any non-logical deletion when
logging the data.
4. Finally, the DELETE record gets decoded, and the logical plugin
finds out that no logical key data was included, and promptly ERRORs
out.
The attached patch is a blind shot that I suspect will fix the issue.
Kind regards,
Matthias van de Meent
Databricks (https://www.databricks.com)
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0001-Repack-Fix-replica-identity-index-check.patch | application/octet-stream | 2.8 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Nathan Bossart | 2026-08-27 19:39:35 | Re: REPACK (ANALYZE) within transaction block segfaults |
| Previous Message | surya poondla | 2026-08-27 19:23:12 | Re: [PATCH] Clarify that ssl_groups is for any key exchange groups |