Skip to content

Commit e273468

Browse files
committed
Fix read_stream.c for changing io_combine_limit.
In a couple of places, read_stream.c assumed that io_combine_limit would be stable during the lifetime of a stream. That is not true in at least one unusual case: streams held by CURSORs where you could change the GUC between FETCH commands, with unpredictable results. Fix, by storing stream->io_combine_limit and referring only to that after construction. This mirrors the treatment of the other important setting {effective,maintenance}_io_concurrency, which is stored in stream->max_ios. One of the cases was the queue overflow space, which was sized for io_combine_limit and could be overrun if the GUC was increased. Since that coding was a little hard to follow, also introduce a variable for better readability instead of open-coding the arithmetic. Doing so revealed an off-by-one thinko while clamping max_pinned_buffers to INT16_MAX, though that wasn't a live bug due to the current limits on GUC values. Back-patch to 17. Discussion: https://postgr.es/m/CA%2BhUKG%2B2T9p-%2BzM6Eeou-RAJjTML6eit1qn26f9twznX59qtCA%40mail.gmail.com
1 parent 8b2392a commit e273468

File tree

1 file changed

+27
-11
lines changed

1 file changed

+27
-11
lines changed

src/backend/storage/aio/read_stream.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ typedef struct InProgressIO
109109
struct ReadStream
110110
{
111111
int16 max_ios;
112+
int16 io_combine_limit;
112113
int16 ios_in_progress;
113114
int16 queue_size;
114115
int16 max_pinned_buffers;
@@ -219,7 +220,7 @@ read_stream_start_pending_read(ReadStream *stream, bool suppress_advice)
219220

220221
/* This should only be called with a pending read. */
221222
Assert(stream->pending_read_nblocks > 0);
222-
Assert(stream->pending_read_nblocks <= io_combine_limit);
223+
Assert(stream->pending_read_nblocks <= stream->io_combine_limit);
223224

224225
/* We had better not exceed the pin limit by starting this read. */
225226
Assert(stream->pinned_buffers + stream->pending_read_nblocks <=
@@ -307,7 +308,7 @@ read_stream_look_ahead(ReadStream *stream, bool suppress_advice)
307308
int16 buffer_index;
308309
void *per_buffer_data;
309310

310-
if (stream->pending_read_nblocks == io_combine_limit)
311+
if (stream->pending_read_nblocks == stream->io_combine_limit)
311312
{
312313
read_stream_start_pending_read(stream, suppress_advice);
313314
suppress_advice = false;
@@ -367,7 +368,7 @@ read_stream_look_ahead(ReadStream *stream, bool suppress_advice)
367368
* signaled end-of-stream, we start the read immediately.
368369
*/
369370
if (stream->pending_read_nblocks > 0 &&
370-
(stream->pending_read_nblocks == io_combine_limit ||
371+
(stream->pending_read_nblocks == stream->io_combine_limit ||
371372
(stream->pending_read_nblocks == stream->distance &&
372373
stream->pinned_buffers == 0) ||
373374
stream->distance == 0) &&
@@ -396,6 +397,7 @@ read_stream_begin_relation(int flags,
396397
ReadStream *stream;
397398
size_t size;
398399
int16 queue_size;
400+
int16 queue_overflow;
399401
int max_ios;
400402
int strategy_pin_limit;
401403
uint32 max_pinned_buffers;
@@ -429,6 +431,14 @@ read_stream_begin_relation(int flags,
429431
/* Cap to INT16_MAX to avoid overflowing below */
430432
max_ios = Min(max_ios, PG_INT16_MAX);
431433

434+
/*
435+
* If starting a multi-block I/O near the end of the queue, we might
436+
* temporarily need extra space for overflowing buffers before they are
437+
* moved to regular circular position. This is the maximum extra space we
438+
* could need.
439+
*/
440+
queue_overflow = io_combine_limit - 1;
441+
432442
/*
433443
* Choose the maximum number of buffers we're prepared to pin. We try to
434444
* pin fewer if we can, though. We clamp it to at least io_combine_limit
@@ -439,7 +449,7 @@ read_stream_begin_relation(int flags,
439449
*/
440450
max_pinned_buffers = Max(max_ios * 4, io_combine_limit);
441451
max_pinned_buffers = Min(max_pinned_buffers,
442-
PG_INT16_MAX - io_combine_limit - 1);
452+
PG_INT16_MAX - queue_overflow - 1);
443453

444454
/* Give the strategy a chance to limit the number of buffers we pin. */
445455
strategy_pin_limit = GetAccessStrategyPinLimit(strategy);
@@ -465,18 +475,17 @@ read_stream_begin_relation(int flags,
465475
* one big chunk. Though we have queue_size buffers, we want to be able
466476
* to assume that all the buffers for a single read are contiguous (i.e.
467477
* don't wrap around halfway through), so we allow temporary overflows of
468-
* up to the maximum possible read size by allocating an extra
469-
* io_combine_limit - 1 elements.
478+
* up to the maximum possible overflow size.
470479
*/
471480
size = offsetof(ReadStream, buffers);
472-
size += sizeof(Buffer) * (queue_size + io_combine_limit - 1);
481+
size += sizeof(Buffer) * (queue_size + queue_overflow);
473482
size += sizeof(InProgressIO) * Max(1, max_ios);
474483
size += per_buffer_data_size * queue_size;
475484
size += MAXIMUM_ALIGNOF * 2;
476485
stream = (ReadStream *) palloc(size);
477486
memset(stream, 0, offsetof(ReadStream, buffers));
478487
stream->ios = (InProgressIO *)
479-
MAXALIGN(&stream->buffers[queue_size + io_combine_limit - 1]);
488+
MAXALIGN(&stream->buffers[queue_size + queue_overflow]);
480489
if (per_buffer_data_size > 0)
481490
stream->per_buffer_data = (void *)
482491
MAXALIGN(&stream->ios[Max(1, max_ios)]);
@@ -503,7 +512,14 @@ read_stream_begin_relation(int flags,
503512
if (max_ios == 0)
504513
max_ios = 1;
505514

515+
/*
516+
* Capture stable values for these two GUC-derived numbers for the
517+
* lifetime of this stream, so we don't have to worry about the GUCs
518+
* changing underneath us beyond this point.
519+
*/
506520
stream->max_ios = max_ios;
521+
stream->io_combine_limit = io_combine_limit;
522+
507523
stream->per_buffer_data_size = per_buffer_data_size;
508524
stream->max_pinned_buffers = max_pinned_buffers;
509525
stream->queue_size = queue_size;
@@ -517,7 +533,7 @@ read_stream_begin_relation(int flags,
517533
* doing full io_combine_limit sized reads (behavior B).
518534
*/
519535
if (flags & READ_STREAM_FULL)
520-
stream->distance = Min(max_pinned_buffers, io_combine_limit);
536+
stream->distance = Min(max_pinned_buffers, stream->io_combine_limit);
521537
else
522538
stream->distance = 1;
523539

@@ -683,14 +699,14 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
683699
else
684700
{
685701
/* No advice; move towards io_combine_limit (behavior B). */
686-
if (stream->distance > io_combine_limit)
702+
if (stream->distance > stream->io_combine_limit)
687703
{
688704
stream->distance--;
689705
}
690706
else
691707
{
692708
distance = stream->distance * 2;
693-
distance = Min(distance, io_combine_limit);
709+
distance = Min(distance, stream->io_combine_limit);
694710
distance = Min(distance, stream->max_pinned_buffers);
695711
stream->distance = distance;
696712
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy