Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths - Mailing list pgsql-hackers

From Andres Freund
Subject Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths
Date
Msg-id 20220314171445.wyh73j4sfmodtfsy@alap3.anarazel.de
Whole thread Raw
In response to Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths  (Matthias van de Meent <boekewurm+postgres@gmail.com>)
Responses Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths
Re: Non-replayable WAL records through overflows and >MaxAllocSize lengths
List pgsql-hackers
Hi

A random thought I had while thinking about the size limits: We could use the
low bits of the length and xl_prev to store XLR_SPECIAL_REL_UPDATE |
XLR_CHECK_CONSISTENCY and give rmgrs the full 8 bit of xl_info. Which would
allow us to e.g. get away from needing Heap2. Which would aestethically be
pleasing.



On 2022-03-14 17:57:23 +0100, Matthias van de Meent wrote:
> I'm not sure whether or not to include this in the test suite, though,
> as this would require a machine with at least 1GB of memory available
> for this test alone, and I don't know the current requirements for
> running the test suite.

We definitely shouldn't require this much RAM for the tests.

It might be worth adding tests exercising edge cases around segment boundaries
(and perhaps page boundaries) though. E.g. record headers split across pages
and segments.



> --- a/src/backend/access/transam/xloginsert.c
> +++ b/src/backend/access/transam/xloginsert.c
> @@ -338,10 +338,16 @@ XLogRegisterData(char *data, int len)
>  {
>      XLogRecData *rdata;
>  
> -    Assert(begininsert_called);
> +    Assert(begininsert_called && len >= 0 && AllocSizeIsValid(len));

Shouldn't we just make the length argument unsigned?


> -    if (num_rdatas >= max_rdatas)
> +    /*
> +     * Check against max_rdatas; and ensure we don't fill a record with
> +     * more data than can be replayed
> +     */
> +    if (unlikely(num_rdatas >= max_rdatas ||
> +                 !AllocSizeIsValid((uint64) mainrdata_len + (uint64) len)))
>          elog(ERROR, "too much WAL data");
> +
>      rdata = &rdatas[num_rdatas++];

Personally I'd write it as unlikely(num_rdatas >= max_rdatas) || unlikely(...)
but I doubt if it makes an actual difference to the compiler.


>      rdata->data = data;
> @@ -377,7 +383,7 @@ XLogRegisterBufData(uint8 block_id, char *data, int len)
>      registered_buffer *regbuf;
>      XLogRecData *rdata;
>  
> -    Assert(begininsert_called);
> +    Assert(begininsert_called && len >= 0 && len <= UINT16_MAX);
>  
>      /* find the registered buffer struct */
>      regbuf = ®istered_buffers[block_id];
> @@ -385,8 +391,14 @@ XLogRegisterBufData(uint8 block_id, char *data, int len)
>          elog(ERROR, "no block with id %d registered with WAL insertion",
>               block_id);
>  
> -    if (num_rdatas >= max_rdatas)
> +    /*
> +     * Check against max_rdatas; and ensure we don't register more data per
> +     * buffer than can be handled by the physical record format.
> +     */
> +    if (unlikely(num_rdatas >= max_rdatas ||
> +                 regbuf->rdata_len + len > UINT16_MAX))
>          elog(ERROR, "too much WAL data");
> +
>      rdata = &rdatas[num_rdatas++];

Given the repeated check it might be worth to just put it in a static inline
used from the relevant places (which'd generate less code because the same
line number would be used for all the checks).

Greetings,

Andres Freund



pgsql-hackers by date:

Previous
From: Justin Pryzby
Date:
Subject: Re: refactoring basebackup.c (zstd workers)
Next
From: Robert Haas
Date:
Subject: Re: refactoring basebackup.c (zstd workers)