2727 * - length of page region (OffsetNumber)
2828 * - data - the data to place into the region ('length' number of bytes)
2929 *
30- * Unchanged regions of a page are not represented in its delta. As a
31- * result, a delta can be more compact than the full page image. But having
32- * an unchanged region in the middle of two fragments that is smaller than
33- * the fragment header (offset and length) does not pay off in terms of the
34- * overall size of the delta. For this reason, we break fragments only if
35- * the unchanged region is bigger than MATCH_THRESHOLD.
30+ * Unchanged regions of a page are not represented in its delta. As a result,
31+ * a delta can be more compact than the full page image. But having an
32+ * unchanged region between two fragments that is smaller than the fragment
33+ * header (offset+ length) does not pay off in terms of the overall size of
34+ * the delta. For this reason, we merge adjacent fragments if the unchanged
35+ * region between them is <= MATCH_THRESHOLD bytes .
3636 *
3737 * The worst case for delta sizes occurs when we did not find any unchanged
3838 * region in the page. The size of the delta will be the size of the page plus
4141 */
4242#define FRAGMENT_HEADER_SIZE (2 * sizeof(OffsetNumber))
4343#define MATCH_THRESHOLD FRAGMENT_HEADER_SIZE
44- #define MAX_DELTA_SIZE BLCKSZ + FRAGMENT_HEADER_SIZE
44+ #define MAX_DELTA_SIZE ( BLCKSZ + FRAGMENT_HEADER_SIZE)
4545
4646/* Struct of generic xlog data for single page */
4747typedef struct
4848{
4949 Buffer buffer ; /* registered buffer */
50- char image [BLCKSZ ]; /* copy of page image for modification */
51- char data [MAX_DELTA_SIZE ]; /* delta between page images */
52- int dataLen ; /* space consumed in data field */
5350 bool fullImage ; /* are we taking a full image of this page? */
51+ int deltaLen ; /* space consumed in delta field */
52+ char image [BLCKSZ ]; /* copy of page image for modification */
53+ char delta [MAX_DELTA_SIZE ]; /* delta between page images */
5454} PageData ;
5555
5656/* State of generic xlog record construction */
@@ -61,22 +61,26 @@ struct GenericXLogState
6161};
6262
6363static void writeFragment (PageData * pageData , OffsetNumber offset ,
64- OffsetNumber len , Pointer data );
65- static void writeDelta (PageData * pageData );
66- static void applyPageRedo (Page page , Pointer data , Size dataSize );
64+ OffsetNumber len , const char * data );
65+ static void computeDelta (PageData * pageData );
66+ static void applyPageRedo (Page page , const char * delta , Size deltaSize );
67+
6768
6869/*
69- * Write next fragment into delta.
70+ * Write next fragment into pageData's delta.
71+ *
72+ * The fragment has the given offset and length, and data points to the
73+ * actual data (of length length).
7074 */
7175static void
7276writeFragment (PageData * pageData , OffsetNumber offset , OffsetNumber length ,
73- Pointer data )
77+ const char * data )
7478{
75- Pointer ptr = pageData -> data + pageData -> dataLen ;
79+ char * ptr = pageData -> delta + pageData -> deltaLen ;
7680
77- /* Check if we have enough space */
78- Assert (pageData -> dataLen + sizeof (offset ) +
79- sizeof (length ) + length <= sizeof (pageData -> data ));
81+ /* Verify we have enough space */
82+ Assert (pageData -> deltaLen + sizeof (offset ) +
83+ sizeof (length ) + length <= sizeof (pageData -> delta ));
8084
8185 /* Write fragment data */
8286 memcpy (ptr , & offset , sizeof (offset ));
@@ -86,14 +90,14 @@ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
8690 memcpy (ptr , data , length );
8791 ptr += length ;
8892
89- pageData -> dataLen = ptr - pageData -> data ;
93+ pageData -> deltaLen = ptr - pageData -> delta ;
9094}
9195
9296/*
93- * Make delta for given page.
97+ * Compute the delta record for given page.
9498 */
9599static void
96- writeDelta (PageData * pageData )
100+ computeDelta (PageData * pageData )
97101{
98102 Page page = BufferGetPage (pageData -> buffer , NULL , NULL ,
99103 BGP_NO_SNAPSHOT_TEST ),
@@ -106,6 +110,8 @@ writeDelta(PageData *pageData)
106110 imageLower = ((PageHeader ) image )-> pd_lower ,
107111 imageUpper = ((PageHeader ) image )-> pd_upper ;
108112
113+ pageData -> deltaLen = 0 ;
114+
109115 for (i = 0 ; i < BLCKSZ ; i ++ )
110116 {
111117 bool match ;
@@ -181,22 +187,22 @@ writeDelta(PageData *pageData)
181187 char tmp [BLCKSZ ];
182188
183189 memcpy (tmp , image , BLCKSZ );
184- applyPageRedo (tmp , pageData -> data , pageData -> dataLen );
185- if (memcmp (tmp , page , pageLower )
186- || memcmp (tmp + pageUpper , page + pageUpper , BLCKSZ - pageUpper ))
190+ applyPageRedo (tmp , pageData -> delta , pageData -> deltaLen );
191+ if (memcmp (tmp , page , pageLower ) != 0 ||
192+ memcmp (tmp + pageUpper , page + pageUpper , BLCKSZ - pageUpper ) != 0 )
187193 elog (ERROR , "result of generic xlog apply does not match" );
188194 }
189195#endif
190196}
191197
192198/*
193- * Start new generic xlog record.
199+ * Start new generic xlog record for modifications to specified relation .
194200 */
195201GenericXLogState *
196202GenericXLogStart (Relation relation )
197203{
198- int i ;
199204 GenericXLogState * state ;
205+ int i ;
200206
201207 state = (GenericXLogState * ) palloc (sizeof (GenericXLogState ));
202208
@@ -209,24 +215,30 @@ GenericXLogStart(Relation relation)
209215
210216/*
211217 * Register new buffer for generic xlog record.
218+ *
219+ * Returns pointer to the page's image in the GenericXLogState, which
220+ * is what the caller should modify.
221+ *
222+ * If the buffer is already registered, just return its existing entry.
212223 */
213224Page
214225GenericXLogRegister (GenericXLogState * state , Buffer buffer , bool isNew )
215226{
216227 int block_id ;
217228
218- /* Place new buffer to unused slot in array */
229+ /* Search array for existing entry or first unused slot */
219230 for (block_id = 0 ; block_id < MAX_GENERIC_XLOG_PAGES ; block_id ++ )
220231 {
221232 PageData * page = & state -> pages [block_id ];
222233
223234 if (BufferIsInvalid (page -> buffer ))
224235 {
236+ /* Empty slot, so use it (there cannot be a match later) */
225237 page -> buffer = buffer ;
226- memcpy (page -> image , BufferGetPage (buffer , NULL , NULL ,
227- BGP_NO_SNAPSHOT_TEST ), BLCKSZ );
228- page -> dataLen = 0 ;
229238 page -> fullImage = isNew ;
239+ memcpy (page -> image ,
240+ BufferGetPage (buffer , NULL , NULL , BGP_NO_SNAPSHOT_TEST ),
241+ BLCKSZ );
230242 return (Page ) page -> image ;
231243 }
232244 else if (page -> buffer == buffer )
@@ -239,15 +251,16 @@ GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew)
239251 }
240252 }
241253
242- elog (ERROR , "maximum number of %d generic xlog buffers is exceeded" ,
254+ elog (ERROR , "maximum number %d of generic xlog buffers is exceeded" ,
243255 MAX_GENERIC_XLOG_PAGES );
244-
245256 /* keep compiler quiet */
246257 return NULL ;
247258}
248259
249260/*
250261 * Unregister particular buffer for generic xlog record.
262+ *
263+ * XXX this is dangerous and should go away.
251264 */
252265void
253266GenericXLogUnregister (GenericXLogState * state , Buffer buffer )
@@ -274,7 +287,8 @@ GenericXLogUnregister(GenericXLogState *state, Buffer buffer)
274287}
275288
276289/*
277- * Put all changes in registered buffers to generic xlog record.
290+ * Apply changes represented by GenericXLogState to the actual buffers,
291+ * and emit a generic xlog record.
278292 */
279293XLogRecPtr
280294GenericXLogFinish (GenericXLogState * state )
@@ -291,33 +305,35 @@ GenericXLogFinish(GenericXLogState *state)
291305
292306 for (i = 0 ; i < MAX_GENERIC_XLOG_PAGES ; i ++ )
293307 {
308+ PageData * pageData = & state -> pages [i ];
309+ Page page ;
294310 char tmp [BLCKSZ ];
295- PageData * page = & state -> pages [i ];
296311
297- if (BufferIsInvalid (page -> buffer ))
312+ if (BufferIsInvalid (pageData -> buffer ))
298313 continue ;
299314
315+ page = BufferGetPage (pageData -> buffer , NULL , NULL ,
316+ BGP_NO_SNAPSHOT_TEST );
317+
300318 /* Swap current and saved page image. */
301- memcpy (tmp , page -> image , BLCKSZ );
302- memcpy (page -> image , BufferGetPage (page -> buffer , NULL , NULL ,
303- BGP_NO_SNAPSHOT_TEST ), BLCKSZ );
304- memcpy (BufferGetPage (page -> buffer , NULL , NULL ,
305- BGP_NO_SNAPSHOT_TEST ), tmp , BLCKSZ );
319+ memcpy (tmp , pageData -> image , BLCKSZ );
320+ memcpy (pageData -> image , page , BLCKSZ );
321+ memcpy (page , tmp , BLCKSZ );
306322
307- if (page -> fullImage )
323+ if (pageData -> fullImage )
308324 {
309325 /* A full page image does not require anything special */
310- XLogRegisterBuffer (i , page -> buffer , REGBUF_FORCE_IMAGE );
326+ XLogRegisterBuffer (i , pageData -> buffer , REGBUF_FORCE_IMAGE );
311327 }
312328 else
313329 {
314330 /*
315- * In normal mode, calculate delta and write it as data
331+ * In normal mode, calculate delta and write it as xlog data
316332 * associated with this page.
317333 */
318- XLogRegisterBuffer (i , page -> buffer , REGBUF_STANDARD );
319- writeDelta ( page );
320- XLogRegisterBufData (i , page -> data , page -> dataLen );
334+ XLogRegisterBuffer (i , pageData -> buffer , REGBUF_STANDARD );
335+ computeDelta ( pageData );
336+ XLogRegisterBufData (i , pageData -> delta , pageData -> deltaLen );
321337 }
322338 }
323339
@@ -327,13 +343,13 @@ GenericXLogFinish(GenericXLogState *state)
327343 /* Set LSN and mark buffers dirty */
328344 for (i = 0 ; i < MAX_GENERIC_XLOG_PAGES ; i ++ )
329345 {
330- PageData * page = & state -> pages [i ];
346+ PageData * pageData = & state -> pages [i ];
331347
332- if (BufferIsInvalid (page -> buffer ))
348+ if (BufferIsInvalid (pageData -> buffer ))
333349 continue ;
334- PageSetLSN (BufferGetPage (page -> buffer , NULL , NULL ,
350+ PageSetLSN (BufferGetPage (pageData -> buffer , NULL , NULL ,
335351 BGP_NO_SNAPSHOT_TEST ), lsn );
336- MarkBufferDirty (page -> buffer );
352+ MarkBufferDirty (pageData -> buffer );
337353 }
338354 END_CRIT_SECTION ();
339355 }
@@ -343,13 +359,15 @@ GenericXLogFinish(GenericXLogState *state)
343359 START_CRIT_SECTION ();
344360 for (i = 0 ; i < MAX_GENERIC_XLOG_PAGES ; i ++ )
345361 {
346- PageData * page = & state -> pages [i ];
362+ PageData * pageData = & state -> pages [i ];
347363
348- if (BufferIsInvalid (page -> buffer ))
364+ if (BufferIsInvalid (pageData -> buffer ))
349365 continue ;
350- memcpy (BufferGetPage (page -> buffer , NULL , NULL ,
351- BGP_NO_SNAPSHOT_TEST ), page -> image , BLCKSZ );
352- MarkBufferDirty (page -> buffer );
366+ memcpy (BufferGetPage (pageData -> buffer , NULL , NULL ,
367+ BGP_NO_SNAPSHOT_TEST ),
368+ pageData -> image ,
369+ BLCKSZ );
370+ MarkBufferDirty (pageData -> buffer );
353371 }
354372 END_CRIT_SECTION ();
355373 }
@@ -360,7 +378,9 @@ GenericXLogFinish(GenericXLogState *state)
360378}
361379
362380/*
363- * Abort generic xlog record.
381+ * Abort generic xlog record construction. No changes are applied to buffers.
382+ *
383+ * Note: caller is responsible for releasing locks/pins on buffers, if needed.
364384 */
365385void
366386GenericXLogAbort (GenericXLogState * state )
@@ -372,10 +392,10 @@ GenericXLogAbort(GenericXLogState *state)
372392 * Apply delta to given page image.
373393 */
374394static void
375- applyPageRedo (Page page , Pointer data , Size dataSize )
395+ applyPageRedo (Page page , const char * delta , Size deltaSize )
376396{
377- Pointer ptr = data ,
378- end = data + dataSize ;
397+ const char * ptr = delta ;
398+ const char * end = delta + deltaSize ;
379399
380400 while (ptr < end )
381401 {
@@ -399,10 +419,11 @@ applyPageRedo(Page page, Pointer data, Size dataSize)
399419void
400420generic_redo (XLogReaderState * record )
401421{
402- uint8 block_id ;
403- Buffer buffers [MAX_GENERIC_XLOG_PAGES ] = {InvalidBuffer };
404422 XLogRecPtr lsn = record -> EndRecPtr ;
423+ Buffer buffers [MAX_GENERIC_XLOG_PAGES ];
424+ uint8 block_id ;
405425
426+ /* Protect limited size of buffers[] array */
406427 Assert (record -> max_block_id < MAX_GENERIC_XLOG_PAGES );
407428
408429 /* Iterate over blocks */
@@ -411,20 +432,24 @@ generic_redo(XLogReaderState *record)
411432 XLogRedoAction action ;
412433
413434 if (!XLogRecHasBlockRef (record , block_id ))
435+ {
436+ buffers [block_id ] = InvalidBuffer ;
414437 continue ;
438+ }
415439
416440 action = XLogReadBufferForRedo (record , block_id , & buffers [block_id ]);
417441
418442 /* Apply redo to given block if needed */
419443 if (action == BLK_NEEDS_REDO )
420444 {
421- Pointer blockData ;
422- Size blockDataSize ;
423445 Page page ;
446+ char * blockDelta ;
447+ Size blockDeltaSize ;
424448
425- page = BufferGetPage (buffers [block_id ], NULL , NULL , BGP_NO_SNAPSHOT_TEST );
426- blockData = XLogRecGetBlockData (record , block_id , & blockDataSize );
427- applyPageRedo (page , blockData , blockDataSize );
449+ page = BufferGetPage (buffers [block_id ], NULL , NULL ,
450+ BGP_NO_SNAPSHOT_TEST );
451+ blockDelta = XLogRecGetBlockData (record , block_id , & blockDeltaSize );
452+ applyPageRedo (page , blockDelta , blockDeltaSize );
428453
429454 PageSetLSN (page , lsn );
430455 MarkBufferDirty (buffers [block_id ]);
0 commit comments