34
34
* the delta. For this reason, we merge adjacent fragments if the unchanged
35
35
* region between them is <= MATCH_THRESHOLD bytes.
36
36
*
37
- * The worst case for delta sizes occurs when we did not find any unchanged
38
- * region in the page. The size of the delta will be the size of the page plus
39
- * the size of the fragment header in that case.
37
+ * We do not bother to merge fragments across the "lower" and "upper" parts
38
+ * of a page; it's very seldom the case that pd_lower and pd_upper are within
39
+ * MATCH_THRESHOLD bytes of each other, and handling that infrequent case
40
+ * would complicate and slow down the delta-computation code unduly.
41
+ * Therefore, the worst-case delta size includes two fragment headers plus
42
+ * a full page's worth of data.
40
43
*-------------------------------------------------------------------------
41
44
*/
42
45
#define FRAGMENT_HEADER_SIZE (2 * sizeof(OffsetNumber))
43
46
#define MATCH_THRESHOLD FRAGMENT_HEADER_SIZE
44
- #define MAX_DELTA_SIZE (BLCKSZ + FRAGMENT_HEADER_SIZE)
47
+ #define MAX_DELTA_SIZE (BLCKSZ + 2 * FRAGMENT_HEADER_SIZE)
45
48
46
49
/* Struct of generic xlog data for single page */
47
50
typedef struct
@@ -62,7 +65,11 @@ struct GenericXLogState
62
65
63
66
static void writeFragment (PageData * pageData , OffsetNumber offset ,
64
67
OffsetNumber len , const char * data );
65
- static void computeDelta (PageData * pageData );
68
+ static void computeRegionDelta (PageData * pageData ,
69
+ const char * curpage , const char * targetpage ,
70
+ int targetStart , int targetEnd ,
71
+ int validStart , int validEnd );
72
+ static void computeDelta (PageData * pageData , Page curpage , Page targetpage );
66
73
static void applyPageRedo (Page page , const char * delta , Size deltaSize );
67
74
68
75
@@ -94,102 +101,155 @@ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
94
101
}
95
102
96
103
/*
97
- * Compute the delta record for given page.
104
+ * Compute the XLOG fragments needed to transform a region of curpage into the
105
+ * corresponding region of targetpage, and append them to pageData's delta
106
+ * field. The region to transform runs from targetStart to targetEnd-1.
107
+ * Bytes in curpage outside the range validStart to validEnd-1 should be
108
+ * considered invalid, and always overwritten with target data.
109
+ *
110
+ * This function is a hot spot, so it's worth being as tense as possible
111
+ * about the data-matching loops.
98
112
*/
99
113
static void
100
- computeDelta (PageData * pageData )
114
+ computeRegionDelta (PageData * pageData ,
115
+ const char * curpage , const char * targetpage ,
116
+ int targetStart , int targetEnd ,
117
+ int validStart , int validEnd )
101
118
{
102
- Page page = BufferGetPage (pageData -> buffer , NULL , NULL ,
103
- BGP_NO_SNAPSHOT_TEST ),
104
- image = (Page ) pageData -> image ;
105
119
int i ,
120
+ loopEnd ,
106
121
fragmentBegin = -1 ,
107
122
fragmentEnd = -1 ;
108
- uint16 pageLower = ((PageHeader ) page )-> pd_lower ,
109
- pageUpper = ((PageHeader ) page )-> pd_upper ,
110
- imageLower = ((PageHeader ) image )-> pd_lower ,
111
- imageUpper = ((PageHeader ) image )-> pd_upper ;
112
-
113
- pageData -> deltaLen = 0 ;
114
123
115
- for (i = 0 ; i < BLCKSZ ; i ++ )
124
+ /* Deal with any invalid start region by including it in first fragment */
125
+ if (validStart > targetStart )
116
126
{
117
- bool match ;
118
-
119
- /*
120
- * Check if bytes in old and new page images match. We do not care
121
- * about data in the unallocated area between pd_lower and pd_upper.
122
- * We assume the unallocated area to expand with unmatched bytes.
123
- * Bytes inside the unallocated area are assumed to always match.
124
- */
125
- if (i < pageLower )
126
- {
127
- if (i < imageLower )
128
- match = (page [i ] == image [i ]);
129
- else
130
- match = false;
131
- }
132
- else if (i >= pageUpper )
133
- {
134
- if (i >= imageUpper )
135
- match = (page [i ] == image [i ]);
136
- else
137
- match = false;
138
- }
139
- else
140
- {
141
- match = true;
142
- }
127
+ fragmentBegin = targetStart ;
128
+ targetStart = validStart ;
129
+ }
143
130
144
- if (match )
145
- {
146
- if (fragmentBegin >= 0 )
147
- {
148
- /* Matched byte is potentially part of a fragment. */
149
- if (fragmentEnd < 0 )
150
- fragmentEnd = i ;
131
+ /* We'll deal with any invalid end region after the main loop */
132
+ loopEnd = Min (targetEnd , validEnd );
151
133
152
- /*
153
- * Write next fragment if sequence of matched bytes is longer
154
- * than MATCH_THRESHOLD.
155
- */
156
- if (i - fragmentEnd >= MATCH_THRESHOLD )
157
- {
158
- writeFragment (pageData , fragmentBegin ,
159
- fragmentEnd - fragmentBegin ,
160
- page + fragmentBegin );
161
- fragmentBegin = -1 ;
162
- fragmentEnd = -1 ;
163
- }
164
- }
165
- }
166
- else
134
+ /* Examine all the potentially matchable bytes */
135
+ i = targetStart ;
136
+ while (i < loopEnd )
137
+ {
138
+ if (curpage [i ] != targetpage [i ])
167
139
{
168
- /* On unmatched byte, start new fragment if it is not done yet */
140
+ /* On unmatched byte, start new fragment if not already in one */
169
141
if (fragmentBegin < 0 )
170
142
fragmentBegin = i ;
143
+ /* Mark unmatched-data endpoint as uncertain */
171
144
fragmentEnd = -1 ;
145
+ /* Extend the fragment as far as possible in a tight loop */
146
+ i ++ ;
147
+ while (i < loopEnd && curpage [i ] != targetpage [i ])
148
+ i ++ ;
149
+ if (i >= loopEnd )
150
+ break ;
151
+ }
152
+
153
+ /* Found a matched byte, so remember end of unmatched fragment */
154
+ fragmentEnd = i ;
155
+
156
+ /*
157
+ * Extend the match as far as possible in a tight loop. (On typical
158
+ * workloads, this inner loop is the bulk of this function's runtime.)
159
+ */
160
+ i ++ ;
161
+ while (i < loopEnd && curpage [i ] == targetpage [i ])
162
+ i ++ ;
163
+
164
+ /*
165
+ * There are several possible cases at this point:
166
+ *
167
+ * 1. We have no unwritten fragment (fragmentBegin < 0). There's
168
+ * nothing to write; and it doesn't matter what fragmentEnd is.
169
+ *
170
+ * 2. We found more than MATCH_THRESHOLD consecutive matching bytes.
171
+ * Dump out the unwritten fragment, stopping at fragmentEnd.
172
+ *
173
+ * 3. The match extends to loopEnd. We'll do nothing here, exit the
174
+ * loop, and then dump the unwritten fragment, after merging it with
175
+ * the invalid end region if any. If we don't so merge, fragmentEnd
176
+ * establishes how much the final writeFragment call needs to write.
177
+ *
178
+ * 4. We found an unmatched byte before loopEnd. The loop will repeat
179
+ * and will enter the unmatched-byte stanza above. So in this case
180
+ * also, it doesn't matter what fragmentEnd is. The matched bytes
181
+ * will get merged into the continuing unmatched fragment.
182
+ *
183
+ * Only in case 3 do we reach the bottom of the loop with a meaningful
184
+ * fragmentEnd value, which is why it's OK that we unconditionally
185
+ * assign "fragmentEnd = i" above.
186
+ */
187
+ if (fragmentBegin >= 0 && i - fragmentEnd > MATCH_THRESHOLD )
188
+ {
189
+ writeFragment (pageData , fragmentBegin ,
190
+ fragmentEnd - fragmentBegin ,
191
+ targetpage + fragmentBegin );
192
+ fragmentBegin = -1 ;
193
+ fragmentEnd = -1 ; /* not really necessary */
172
194
}
173
195
}
174
196
197
+ /* Deal with any invalid end region by including it in final fragment */
198
+ if (loopEnd < targetEnd )
199
+ {
200
+ if (fragmentBegin < 0 )
201
+ fragmentBegin = loopEnd ;
202
+ fragmentEnd = targetEnd ;
203
+ }
204
+
205
+ /* Write final fragment if any */
175
206
if (fragmentBegin >= 0 )
207
+ {
208
+ if (fragmentEnd < 0 )
209
+ fragmentEnd = targetEnd ;
176
210
writeFragment (pageData , fragmentBegin ,
177
- BLCKSZ - fragmentBegin ,
178
- page + fragmentBegin );
211
+ fragmentEnd - fragmentBegin ,
212
+ targetpage + fragmentBegin );
213
+ }
214
+ }
215
+
216
+ /*
217
+ * Compute the XLOG delta record needed to transform curpage into targetpage,
218
+ * and store it in pageData's delta field.
219
+ */
220
+ static void
221
+ computeDelta (PageData * pageData , Page curpage , Page targetpage )
222
+ {
223
+ int targetLower = ((PageHeader ) targetpage )-> pd_lower ,
224
+ targetUpper = ((PageHeader ) targetpage )-> pd_upper ,
225
+ curLower = ((PageHeader ) curpage )-> pd_lower ,
226
+ curUpper = ((PageHeader ) curpage )-> pd_upper ;
227
+
228
+ pageData -> deltaLen = 0 ;
229
+
230
+ /* Compute delta records for lower part of page ... */
231
+ computeRegionDelta (pageData , curpage , targetpage ,
232
+ 0 , targetLower ,
233
+ 0 , curLower );
234
+ /* ... and for upper part, ignoring what's between */
235
+ computeRegionDelta (pageData , curpage , targetpage ,
236
+ targetUpper , BLCKSZ ,
237
+ curUpper , BLCKSZ );
179
238
180
239
/*
181
240
* If xlog debug is enabled, then check produced delta. Result of delta
182
- * application to saved image should be the same as current page state .
241
+ * application to curpage should be equivalent to targetpage .
183
242
*/
184
243
#ifdef WAL_DEBUG
185
244
if (XLOG_DEBUG )
186
245
{
187
246
char tmp [BLCKSZ ];
188
247
189
- memcpy (tmp , image , BLCKSZ );
248
+ memcpy (tmp , curpage , BLCKSZ );
190
249
applyPageRedo (tmp , pageData -> delta , pageData -> deltaLen );
191
- if (memcmp (tmp , page , pageLower ) != 0 ||
192
- memcmp (tmp + pageUpper , page + pageUpper , BLCKSZ - pageUpper ) != 0 )
250
+ if (memcmp (tmp , targetpage , targetLower ) != 0 ||
251
+ memcmp (tmp + targetUpper , targetpage + targetUpper ,
252
+ BLCKSZ - targetUpper ) != 0 )
193
253
elog (ERROR , "result of generic xlog apply does not match" );
194
254
}
195
255
#endif
@@ -264,7 +324,7 @@ GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew)
264
324
XLogRecPtr
265
325
GenericXLogFinish (GenericXLogState * state )
266
326
{
267
- XLogRecPtr lsn = InvalidXLogRecPtr ;
327
+ XLogRecPtr lsn ;
268
328
int i ;
269
329
270
330
if (state -> isLogged )
@@ -278,22 +338,17 @@ GenericXLogFinish(GenericXLogState *state)
278
338
{
279
339
PageData * pageData = & state -> pages [i ];
280
340
Page page ;
281
- char tmp [BLCKSZ ];
282
341
283
342
if (BufferIsInvalid (pageData -> buffer ))
284
343
continue ;
285
344
286
345
page = BufferGetPage (pageData -> buffer , NULL , NULL ,
287
346
BGP_NO_SNAPSHOT_TEST );
288
347
289
- /* Swap current and saved page image. */
290
- memcpy (tmp , pageData -> image , BLCKSZ );
291
- memcpy (pageData -> image , page , BLCKSZ );
292
- memcpy (page , tmp , BLCKSZ );
293
-
294
348
if (pageData -> fullImage )
295
349
{
296
350
/* A full page image does not require anything special */
351
+ memcpy (page , pageData -> image , BLCKSZ );
297
352
XLogRegisterBuffer (i , pageData -> buffer , REGBUF_FORCE_IMAGE );
298
353
}
299
354
else
@@ -302,8 +357,9 @@ GenericXLogFinish(GenericXLogState *state)
302
357
* In normal mode, calculate delta and write it as xlog data
303
358
* associated with this page.
304
359
*/
360
+ computeDelta (pageData , page , (Page ) pageData -> image );
361
+ memcpy (page , pageData -> image , BLCKSZ );
305
362
XLogRegisterBuffer (i , pageData -> buffer , REGBUF_STANDARD );
306
- computeDelta (pageData );
307
363
XLogRegisterBufData (i , pageData -> delta , pageData -> deltaLen );
308
364
}
309
365
}
@@ -341,6 +397,8 @@ GenericXLogFinish(GenericXLogState *state)
341
397
MarkBufferDirty (pageData -> buffer );
342
398
}
343
399
END_CRIT_SECTION ();
400
+ /* We don't have a LSN to return, in this case */
401
+ lsn = InvalidXLogRecPtr ;
344
402
}
345
403
346
404
pfree (state );
0 commit comments