3434 * the delta. For this reason, we merge adjacent fragments if the unchanged
3535 * region between them is <= MATCH_THRESHOLD bytes.
3636 *
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.
4043 *-------------------------------------------------------------------------
4144 */
4245#define FRAGMENT_HEADER_SIZE (2 * sizeof(OffsetNumber))
4346#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)
4548
4649/* Struct of generic xlog data for single page */
4750typedef struct
@@ -62,7 +65,11 @@ struct GenericXLogState
6265
6366static void writeFragment (PageData * pageData , OffsetNumber offset ,
6467 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 );
6673static void applyPageRedo (Page page , const char * delta , Size deltaSize );
6774
6875
@@ -94,102 +101,155 @@ writeFragment(PageData *pageData, OffsetNumber offset, OffsetNumber length,
94101}
95102
96103/*
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.
98112 */
99113static 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 )
101118{
102- Page page = BufferGetPage (pageData -> buffer , NULL , NULL ,
103- BGP_NO_SNAPSHOT_TEST ),
104- image = (Page ) pageData -> image ;
105119 int i ,
120+ loopEnd ,
106121 fragmentBegin = -1 ,
107122 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 ;
114123
115- for (i = 0 ; i < BLCKSZ ; i ++ )
124+ /* Deal with any invalid start region by including it in first fragment */
125+ if (validStart > targetStart )
116126 {
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+ }
143130
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 );
151133
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 ])
167139 {
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 */
169141 if (fragmentBegin < 0 )
170142 fragmentBegin = i ;
143+ /* Mark unmatched-data endpoint as uncertain */
171144 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 */
172194 }
173195 }
174196
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 */
175206 if (fragmentBegin >= 0 )
207+ {
208+ if (fragmentEnd < 0 )
209+ fragmentEnd = targetEnd ;
176210 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 );
179238
180239 /*
181240 * 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 .
183242 */
184243#ifdef WAL_DEBUG
185244 if (XLOG_DEBUG )
186245 {
187246 char tmp [BLCKSZ ];
188247
189- memcpy (tmp , image , BLCKSZ );
248+ memcpy (tmp , curpage , BLCKSZ );
190249 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 )
193253 elog (ERROR , "result of generic xlog apply does not match" );
194254 }
195255#endif
@@ -264,7 +324,7 @@ GenericXLogRegister(GenericXLogState *state, Buffer buffer, bool isNew)
264324XLogRecPtr
265325GenericXLogFinish (GenericXLogState * state )
266326{
267- XLogRecPtr lsn = InvalidXLogRecPtr ;
327+ XLogRecPtr lsn ;
268328 int i ;
269329
270330 if (state -> isLogged )
@@ -278,22 +338,17 @@ GenericXLogFinish(GenericXLogState *state)
278338 {
279339 PageData * pageData = & state -> pages [i ];
280340 Page page ;
281- char tmp [BLCKSZ ];
282341
283342 if (BufferIsInvalid (pageData -> buffer ))
284343 continue ;
285344
286345 page = BufferGetPage (pageData -> buffer , NULL , NULL ,
287346 BGP_NO_SNAPSHOT_TEST );
288347
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-
294348 if (pageData -> fullImage )
295349 {
296350 /* A full page image does not require anything special */
351+ memcpy (page , pageData -> image , BLCKSZ );
297352 XLogRegisterBuffer (i , pageData -> buffer , REGBUF_FORCE_IMAGE );
298353 }
299354 else
@@ -302,8 +357,9 @@ GenericXLogFinish(GenericXLogState *state)
302357 * In normal mode, calculate delta and write it as xlog data
303358 * associated with this page.
304359 */
360+ computeDelta (pageData , page , (Page ) pageData -> image );
361+ memcpy (page , pageData -> image , BLCKSZ );
305362 XLogRegisterBuffer (i , pageData -> buffer , REGBUF_STANDARD );
306- computeDelta (pageData );
307363 XLogRegisterBufData (i , pageData -> delta , pageData -> deltaLen );
308364 }
309365 }
@@ -341,6 +397,8 @@ GenericXLogFinish(GenericXLogState *state)
341397 MarkBufferDirty (pageData -> buffer );
342398 }
343399 END_CRIT_SECTION ();
400+ /* We don't have a LSN to return, in this case */
401+ lsn = InvalidXLogRecPtr ;
344402 }
345403
346404 pfree (state );
0 commit comments