Re: Shutdown indefinitely stuck due to unflushed FPI_FOR_HINT record - Mailing list pgsql-hackers
| From | Fujii Masao |
|---|---|
| Subject | Re: Shutdown indefinitely stuck due to unflushed FPI_FOR_HINT record |
| Date | |
| Msg-id | CAHGQGwGpfEsBJBLQDou7xccnb0tj1T_NQv2gZfuQfgzn_b=RsQ@mail.gmail.com Whole thread |
| In response to | Re: Shutdown indefinitely stuck due to unflushed FPI_FOR_HINT record (Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com>) |
| Responses |
Re: Shutdown indefinitely stuck due to unflushed FPI_FOR_HINT record
|
| List | pgsql-hackers |
On Thu, Mar 12, 2026 at 11:08 PM Anthonin Bonnefoy <anthonin.bonnefoy@datadoghq.com> wrote: > > On Tue, Mar 10, 2026 at 6:11 PM Andres Freund <andres@anarazel.de> wrote: > > I'm pretty sure this is not correct as-is, it suffers from the same issue as > > https://postgr.es/m/vf4hbwrotvhbgcnknrqmfbqlu75oyjkmausvy66ic7x7vuhafx%40e4rvwavtjswo > > I.e. it is not safe to use GetXLogInsertRecPtr() to determine up to where to > > flush to, due to page boundaries. Thanks for the report! > I've managed to reproduce this issue by ensuring the FPI_FOR_HINT > record finishes at the end of a page with the following script (might > need some adjustment if the record sizes are different): > > DROP TABLE IF EXISTS test_insert_rec_ptr; > CREATE TABLE test_insert_rec_ptr(aid int, data text) WITH > (autovacuum_enabled = false); > INSERT INTO test_insert_rec_ptr SELECT *, repeat('a', 100) FROM > generate_series(0, 57); > -- This should tag the page as full > BEGIN; UPDATE test_insert_rec_ptr SET aid=2 where aid=1; ROLLBACK; > CHECKPOINT; > -- Start with a fresh file > SELECT pg_switch_wal(); > -- Our FPI_FOR_HINT writes 8193 bytes > -- With the long header, the first page has 8152 bytes available > -- With the short header, the second page has 8168 bytes available > -- We want our FPI_FOR_HINT to finish at the end of the second page > (+/- 8 bytes of alignment) > -- We need to write the first 25 bytes (or 32 with alignment) in the first page > -- For that, we need to write 8120 bytes of WAL records > BEGIN; > -- 264 bytes of FPW > INSERT INTO test_insert_rec_ptr VALUES(1); > -- 74 * 104 bytes > INSERT INTO test_insert_rec_ptr SELECT *, repeat('a', 44) FROM > generate_series(1, 74); > -- 108 bytes > INSERT INTO test_insert_rec_ptr VALUES(1, repeat('a', 48)); > -- 46 bytes > COMMIT; > -- 264 + 74 * 104 + 46 + 108 = 8114 bytes, which will round up to 8120 > with alignment > -- FPI_FOR_HINT record should be at 0x1FE0 > BEGIN; SELECT * FROM test_insert_rec_ptr WHERE aid=2; ROLLBACK; > > As far as I can tell, the only impact it has is to complain about the > write request being too far: > LOG: request to flush past end of generated WAL; request 0/01604018, > current position 0/01604000 > ERROR: xlog flush request 0/01604018 is not satisfied --- flushed > only to 0/01604000 > > To avoid this issue, it sounds like we need something to use > XLogBytePosToEndRecPtr instead of XLogBytePosToRecPtr to convert the > byte position? With XLogBytePosToRecPtr(), the flush request would > stop at 01604000 instead of going to the next page with 01604018. > > In the attached patch, I've added a GetXLogInsertEndRecPtr() function > which is similar to GetXLogInsertRecPtr(), except it uses > XLogBytePosToEndRecPtr() to stop at the page boundary. > There was also another XLogFlush(GetXLogWriteRecPtr()) call in > syncutils.c, so I replaced both calls with > XLogFlush(GetXLogInsertEndRecPtr()). Thanks for investigating the issue and making the patch! It looks good to me. Andres, Do you have any comments on the proposed patch? Regards, -- Fujii Masao
pgsql-hackers by date: