Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[Bug] ParsePrepareRecord() parses XLOG_XACT_PREPARE with a stale xl_xact_prepare layout: gid decodes as empty in pg_waldump and two-phase logical decoding

Đang mở
#1,973 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
74/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c, postgresql

Hướng nghiên cứu

Bắt đầu với ParsePrepareRecord() trong src/backend/access/rmgrdesc/xactdesc.c và so sánh src/include/access/xact.h với TwoPhaseFileHeader trong src/include/access/twophase_xlog.h; theo dõi StartPrepare()/EndPrepare() trong twophase.c. Tái hiện vấn đề bằng các lệnh pg_waldump và logical-decoding được cung cấp. Hoàn thành có nghĩa là PREPARE hiển thị và phát ra gid thực, giải mã two-phase chính xác và không có hồi quy trong bố cục bản ghi liên quan.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

type: Bug
Apache Cloudberry version

main (89baa48a4f4, 2026-09-07) and local 3.0.0-devel+dev.8551.gc781604c5ba (PostgreSQL 16.9 base). The mismatch has existed since the GPDB-era commits b0208894eb5 / 68c0ff2d4ec / b74f174b6cd (2019) that extended TwoPhaseFileHeader.

What happened

ParsePrepareRecord() in src/backend/access/rmgrdesc/xactdesc.c walks a XLOG_XACT_PREPARE record using the layout of xl_xact_prepare (src/include/access/xact.h). But the record is actually written by StartPrepare()/EndPrepare() (twophase.c) as a TwoPhaseFileHeader (src/include/access/twophase_xlog.h), and Cloudberry extended that struct with fields that xl_xact_prepare never got:

TwoPhaseFileHeader (what is written, 96 bytes)     xl_xact_prepare (what is parsed, 72 bytes)
  ...                                                ...
  int32  nabortrels;                                 int32  nabortrels;
  int32  ncommitdbs;        <- extra                 int32  ncommitstats;
  int32  nabortdbs;         <- extra                 int32  nabortstats;
  int32  ncommitstats;                               int32  ninvalmsgs;
  int32  nabortstats;                                bool   initfileinval;
  int32  ninvalmsgs;                                 uint16 gidlen;
  bool   initfileinval;                              XLogRecPtr origin_lsn;
  Oid    tablespace_oid_to_delete_on_abort;  <- extra TimestampTz origin_timestamp;
  Oid    tablespace_oid_to_delete_on_commit; <- extra
  uint16 gidlen;
  XLogRecPtr origin_lsn;
  TimestampTz origin_timestamp;

Consequences of reading through the wrong struct:

  • xlrec->gidlen is read from offset 54, which is the upper half of the real nabortstats (normally 0), so parsed->twophase_gid is an empty string.
  • xlrec->ncommitstats / nabortstats / ninvalmsgs actually read ncommitdbs / nabortdbs / ncommitstats; initfileinval and origin_lsn read from unrelated bytes.
  • bufptr starts at offset 72 instead of 96, so parsed->subxacts, xlocators, abortlocators, stats, abortstats, msgs all point at the wrong bytes. The parser also never skips the commitdbs / abortdbs (DbDirNode) arrays that EndPrepare() writes between abortrels and the stats arrays.

Two user-visible symptoms:

  1. pg_waldump prints an empty gid for every PREPARE record (every distributed transaction on a segment):

    rmgr: Transaction ... desc: PREPARE gid : 2026-09-09 05:06:49.215203 CST
    rmgr: Transaction ... desc: COMMIT_PREPARED 5256: 2026-09-09 05:06:49.216803 CST gxid = 34413
    

    (COMMIT_PREPARED is parsed by ParseCommitRecord() and is fine, which shows the gid really is 34413 in the WAL.)

  2. Logical decoding with two_phase emits PREPARE TRANSACTION '' with an empty gid, while the matching COMMIT PREPARED carries the real gid. A pgoutput subscriber would try to PREPARE TRANSACTION ''. Worse, DecodePrepare() passes the shifted parsed->subxacts / nsubxacts into SnapBuildCommitTxn() and ReorderBufferPrepare(), so decoding can consume garbage subxact ids.

Crash recovery is not affected because xact_redo() for PREPARE goes through twophase.c's own TwoPhaseFileHeader-based parsing, not ParsePrepareRecord().

What you think should happen instead

xl_xact_prepare must have the same layout as TwoPhaseFileHeader (the upstream PostgreSQL comment in twophase.c says the two must stay in sync), and ParsePrepareRecord() must skip the commitdbs / abortdbs arrays. Then pg_waldump shows PREPARE gid 34413: ... and two-phase logical decoding emits PREPARE TRANSACTION '34413'.

A static assert comparing sizeof(xl_xact_prepare) and sizeof(TwoPhaseFileHeader) (or simply typedef-ing one to the other) would keep this from regressing.

How to reproduce

On a demo cluster with wal_level = logical (needed only for step 2; step 1 works with replica):

-- via coordinator
create table t2pc(id int primary key, v text) distributed by (id);
insert into t2pc select g, 'x' from generate_series(1,6) g;   -- touches all segments -> 2PC
  1. pg_waldump on any primary segment:

    pg_waldump -r Transaction <segdatadir>/pg_wal/<current wal file> | grep -E 'PREPARE|COMMIT_PREPARED' | tail -2
    

    shows PREPARE gid : <timestamp> (empty gid) followed by COMMIT_PREPARED ... gxid = <n>.

  2. two-phase logical decoding on a segment (utility mode):

    -- PGOPTIONS='-c gp_role=utility' psql -p <segport>
    select * from pg_create_logical_replication_slot('s2pc', 'test_decoding', false, true);
    -- run the INSERT above on the coordinator
    select data from pg_logical_slot_peek_changes('s2pc', NULL, NULL, 'include-xids', '1');
    

    output:

    BEGIN 5256
    table public.t2pc: INSERT: id[integer]:2 v[text]:'x'
    ...
    PREPARE TRANSACTION '', txid 5256
    COMMIT PREPARED '34413', txid 5256
    
Operating System

CentOS 7 (kernel 3.10.0-1160), gcc 12.2.1

Anything else

Found while prototyping an MPP logical-replication receiver, where the segment-side gid (which is the distributed xid) is the key for aligning per-segment streams. The fix is small and self-contained; happy to send a PR.

Are you willing to submit PR?
  • Yes I am willing to submit a PR!
Code of Conduct
Ngôn ngữ chính
C
Star
1.4k
Fork
248
Merge trung bình
4 ngày 10 giờ
Pull request đã merge (30 ngày)
40

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của apache/cloudberry

Tất cả issue của apache/cloudberry

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.