@@ -109,6 +109,7 @@ typedef struct InProgressIO
109109struct 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