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 ,
6467OffsetNumber 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 ;
105119int i ,
120+ loopEnd ,
106121fragmentBegin = -1 ,
107122fragmentEnd = -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 ifit is notdone yet */
140+ /* On unmatched byte, start new fragment if notalready in one */
169141if (fragmentBegin < 0 )
170142fragmentBegin = i ;
143+ /* Mark unmatched-data endpoint as uncertain */
171144fragmentEnd = -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 */
175206if (fragmentBegin >=0 )
207+ {
208+ if (fragmentEnd < 0 )
209+ fragmentEnd = targetEnd ;
176210writeFragment (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 tosaved image should bethe same as current page state .
241+ * application tocurpage should beequivalent to targetpage .
183242 */
184243#ifdef WAL_DEBUG
185244if (XLOG_DEBUG )
186245{
187246char tmp [BLCKSZ ];
188247
189- memcpy (tmp ,image ,BLCKSZ );
248+ memcpy (tmp ,curpage ,BLCKSZ );
190249applyPageRedo (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 )
193253elog (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 ;
268328int i ;
269329
270330if (state -> isLogged )
@@ -278,22 +338,17 @@ GenericXLogFinish(GenericXLogState *state)
278338{
279339PageData * pageData = & state -> pages [i ];
280340Page page ;
281- char tmp [BLCKSZ ];
282341
283342if (BufferIsInvalid (pageData -> buffer ))
284343continue ;
285344
286345page = BufferGetPage (pageData -> buffer ,NULL ,NULL ,
287346BGP_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-
294348if (pageData -> fullImage )
295349{
296350/* A full page image does not require anything special */
351+ memcpy (page ,pageData -> image ,BLCKSZ );
297352XLogRegisterBuffer (i ,pageData -> buffer ,REGBUF_FORCE_IMAGE );
298353}
299354else
@@ -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 );
305362XLogRegisterBuffer (i ,pageData -> buffer ,REGBUF_STANDARD );
306- computeDelta (pageData );
307363XLogRegisterBufData (i ,pageData -> delta ,pageData -> deltaLen );
308364}
309365}
@@ -341,6 +397,8 @@ GenericXLogFinish(GenericXLogState *state)
341397MarkBufferDirty (pageData -> buffer );
342398}
343399END_CRIT_SECTION ();
400+ /* We don't have a LSN to return, in this case */
401+ lsn = InvalidXLogRecPtr ;
344402}
345403
346404pfree (state );