27
27
* - length of page region (OffsetNumber)
28
28
* - data - the data to place into the region ('length' number of bytes)
29
29
*
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 .
36
36
*
37
37
* The worst case for delta sizes occurs when we did not find any unchanged
38
38
* region in the page. The size of the delta will be the size of the page plus
41
41
*/
42
42
#define FRAGMENT_HEADER_SIZE (2 * sizeof(OffsetNumber))
43
43
#define MATCH_THRESHOLD FRAGMENT_HEADER_SIZE
44
- #define MAX_DELTA_SIZE BLCKSZ + FRAGMENT_HEADER_SIZE
44
+ #define MAX_DELTA_SIZE ( BLCKSZ + FRAGMENT_HEADER_SIZE)
45
45
46
46
/* Struct of generic xlog data for single page */
47
47
typedef struct
48
48
{
49
49
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 */
53
50
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 */
54
54
} PageData ;
55
55
56
56
/* State of generic xlog record construction */
@@ -61,22 +61,26 @@ struct GenericXLogState
61
61
};
62
62
63
63
static 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
+
67
68
68
69
/*
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).
70
74
*/
71
75
static void
72
76
writeFragment (PageData * pageData , OffsetNumber offset , OffsetNumber length ,
73
- Pointer data )
77
+ const char * data )
74
78
{
75
- Pointer ptr = pageData -> data + pageData -> dataLen ;
79
+ char * ptr = pageData -> delta + pageData -> deltaLen ;
76
80
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 ));
80
84
81
85
/* Write fragment data */
82
86
memcpy (ptr , & offset , sizeof (offset ));
@@ -86,14 +90,14 @@ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
86
90
memcpy (ptr , data , length );
87
91
ptr += length ;
88
92
89
- pageData -> dataLen = ptr - pageData -> data ;
93
+ pageData -> deltaLen = ptr - pageData -> delta ;
90
94
}
91
95
92
96
/*
93
- * Make delta for given page.
97
+ * Compute the delta record for given page.
94
98
*/
95
99
static void
96
- writeDelta (PageData * pageData )
100
+ computeDelta (PageData * pageData )
97
101
{
98
102
Page page = BufferGetPage (pageData -> buffer , NULL , NULL ,
99
103
BGP_NO_SNAPSHOT_TEST ),
@@ -106,6 +110,8 @@ writeDelta(PageData *pageData)
106
110
imageLower = ((PageHeader ) image )-> pd_lower ,
107
111
imageUpper = ((PageHeader ) image )-> pd_upper ;
108
112
113
+ pageData -> deltaLen = 0 ;
114
+
109
115
for (i = 0 ; i < BLCKSZ ; i ++ )
110
116
{
111
117
bool match ;
@@ -181,22 +187,22 @@ writeDelta(PageData *pageData)
181
187
char tmp [BLCKSZ ];
182
188
183
189
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 )
187
193
elog (ERROR , "result of generic xlog apply does not match" );
188
194
}
189
195
#endif
190
196
}
191
197
192
198
/*
193
- * Start new generic xlog record.
199
+ * Start new generic xlog record for modifications to specified relation .
194
200
*/
195
201
GenericXLogState *
196
202
GenericXLogStart (Relation relation )
197
203
{
198
- int i ;
199
204
GenericXLogState * state ;
205
+ int i ;
200
206
201
207
state = (GenericXLogState * ) palloc (sizeof (GenericXLogState ));
202
208
@@ -209,24 +215,30 @@ GenericXLogStart(Relation relation)
209
215
210
216
/*
211
217
* 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.
212
223
*/
213
224
Page
214
225
GenericXLogRegister (GenericXLogState * state , Buffer buffer , bool isNew )
215
226
{
216
227
int block_id ;
217
228
218
- /* Place new buffer to unused slot in array */
229
+ /* Search array for existing entry or first unused slot */
219
230
for (block_id = 0 ; block_id < MAX_GENERIC_XLOG_PAGES ; block_id ++ )
220
231
{
221
232
PageData * page = & state -> pages [block_id ];
222
233
223
234
if (BufferIsInvalid (page -> buffer ))
224
235
{
236
+ /* Empty slot, so use it (there cannot be a match later) */
225
237
page -> buffer = buffer ;
226
- memcpy (page -> image , BufferGetPage (buffer , NULL , NULL ,
227
- BGP_NO_SNAPSHOT_TEST ), BLCKSZ );
228
- page -> dataLen = 0 ;
229
238
page -> fullImage = isNew ;
239
+ memcpy (page -> image ,
240
+ BufferGetPage (buffer , NULL , NULL , BGP_NO_SNAPSHOT_TEST ),
241
+ BLCKSZ );
230
242
return (Page ) page -> image ;
231
243
}
232
244
else if (page -> buffer == buffer )
@@ -239,15 +251,16 @@ GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew)
239
251
}
240
252
}
241
253
242
- elog (ERROR , "maximum number of %d generic xlog buffers is exceeded" ,
254
+ elog (ERROR , "maximum number %d of generic xlog buffers is exceeded" ,
243
255
MAX_GENERIC_XLOG_PAGES );
244
-
245
256
/* keep compiler quiet */
246
257
return NULL ;
247
258
}
248
259
249
260
/*
250
261
* Unregister particular buffer for generic xlog record.
262
+ *
263
+ * XXX this is dangerous and should go away.
251
264
*/
252
265
void
253
266
GenericXLogUnregister (GenericXLogState * state , Buffer buffer )
@@ -274,7 +287,8 @@ GenericXLogUnregister(GenericXLogState *state, Buffer buffer)
274
287
}
275
288
276
289
/*
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.
278
292
*/
279
293
XLogRecPtr
280
294
GenericXLogFinish (GenericXLogState * state )
@@ -291,33 +305,35 @@ GenericXLogFinish(GenericXLogState *state)
291
305
292
306
for (i = 0 ; i < MAX_GENERIC_XLOG_PAGES ; i ++ )
293
307
{
308
+ PageData * pageData = & state -> pages [i ];
309
+ Page page ;
294
310
char tmp [BLCKSZ ];
295
- PageData * page = & state -> pages [i ];
296
311
297
- if (BufferIsInvalid (page -> buffer ))
312
+ if (BufferIsInvalid (pageData -> buffer ))
298
313
continue ;
299
314
315
+ page = BufferGetPage (pageData -> buffer , NULL , NULL ,
316
+ BGP_NO_SNAPSHOT_TEST );
317
+
300
318
/* 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 );
306
322
307
- if (page -> fullImage )
323
+ if (pageData -> fullImage )
308
324
{
309
325
/* 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 );
311
327
}
312
328
else
313
329
{
314
330
/*
315
- * In normal mode, calculate delta and write it as data
331
+ * In normal mode, calculate delta and write it as xlog data
316
332
* associated with this page.
317
333
*/
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 );
321
337
}
322
338
}
323
339
@@ -327,13 +343,13 @@ GenericXLogFinish(GenericXLogState *state)
327
343
/* Set LSN and mark buffers dirty */
328
344
for (i = 0 ; i < MAX_GENERIC_XLOG_PAGES ; i ++ )
329
345
{
330
- PageData * page = & state -> pages [i ];
346
+ PageData * pageData = & state -> pages [i ];
331
347
332
- if (BufferIsInvalid (page -> buffer ))
348
+ if (BufferIsInvalid (pageData -> buffer ))
333
349
continue ;
334
- PageSetLSN (BufferGetPage (page -> buffer , NULL , NULL ,
350
+ PageSetLSN (BufferGetPage (pageData -> buffer , NULL , NULL ,
335
351
BGP_NO_SNAPSHOT_TEST ), lsn );
336
- MarkBufferDirty (page -> buffer );
352
+ MarkBufferDirty (pageData -> buffer );
337
353
}
338
354
END_CRIT_SECTION ();
339
355
}
@@ -343,13 +359,15 @@ GenericXLogFinish(GenericXLogState *state)
343
359
START_CRIT_SECTION ();
344
360
for (i = 0 ; i < MAX_GENERIC_XLOG_PAGES ; i ++ )
345
361
{
346
- PageData * page = & state -> pages [i ];
362
+ PageData * pageData = & state -> pages [i ];
347
363
348
- if (BufferIsInvalid (page -> buffer ))
364
+ if (BufferIsInvalid (pageData -> buffer ))
349
365
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 );
353
371
}
354
372
END_CRIT_SECTION ();
355
373
}
@@ -360,7 +378,9 @@ GenericXLogFinish(GenericXLogState *state)
360
378
}
361
379
362
380
/*
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.
364
384
*/
365
385
void
366
386
GenericXLogAbort (GenericXLogState * state )
@@ -372,10 +392,10 @@ GenericXLogAbort(GenericXLogState *state)
372
392
* Apply delta to given page image.
373
393
*/
374
394
static void
375
- applyPageRedo (Page page , Pointer data , Size dataSize )
395
+ applyPageRedo (Page page , const char * delta , Size deltaSize )
376
396
{
377
- Pointer ptr = data ,
378
- end = data + dataSize ;
397
+ const char * ptr = delta ;
398
+ const char * end = delta + deltaSize ;
379
399
380
400
while (ptr < end )
381
401
{
@@ -399,10 +419,11 @@ applyPageRedo(Page page, Pointer data, Size dataSize)
399
419
void
400
420
generic_redo (XLogReaderState * record )
401
421
{
402
- uint8 block_id ;
403
- Buffer buffers [MAX_GENERIC_XLOG_PAGES ] = {InvalidBuffer };
404
422
XLogRecPtr lsn = record -> EndRecPtr ;
423
+ Buffer buffers [MAX_GENERIC_XLOG_PAGES ];
424
+ uint8 block_id ;
405
425
426
+ /* Protect limited size of buffers[] array */
406
427
Assert (record -> max_block_id < MAX_GENERIC_XLOG_PAGES );
407
428
408
429
/* Iterate over blocks */
@@ -411,20 +432,24 @@ generic_redo(XLogReaderState *record)
411
432
XLogRedoAction action ;
412
433
413
434
if (!XLogRecHasBlockRef (record , block_id ))
435
+ {
436
+ buffers [block_id ] = InvalidBuffer ;
414
437
continue ;
438
+ }
415
439
416
440
action = XLogReadBufferForRedo (record , block_id , & buffers [block_id ]);
417
441
418
442
/* Apply redo to given block if needed */
419
443
if (action == BLK_NEEDS_REDO )
420
444
{
421
- Pointer blockData ;
422
- Size blockDataSize ;
423
445
Page page ;
446
+ char * blockDelta ;
447
+ Size blockDeltaSize ;
424
448
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 );
428
453
429
454
PageSetLSN (page , lsn );
430
455
MarkBufferDirty (buffers [block_id ]);
0 commit comments