]> granicus.if.org Git - postgresql/commitdiff
XLogReader general code cleanup
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 30 Mar 2016 21:56:13 +0000 (18:56 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 30 Mar 2016 21:56:13 +0000 (18:56 -0300)
Some minor tweaks and comment additions, for cleanliness sake and to
avoid having the upcoming timeline-following patch be polluted with
unrelated cleanup.

Extracted from a larger patch by Craig Ringer, reviewed by Andres
Freund, with some additions by myself.

src/backend/access/transam/xlogreader.c
src/backend/access/transam/xlogutils.c
src/backend/replication/logical/logicalfuncs.c
src/include/access/xlogreader.h
src/include/access/xlogutils.h

index fcb08720c0acb26484b6089a94a8cfecca862e3f..018fdf3d34efd31d839dd50afe1e1d065536b38c 100644 (file)
  *
  * NOTES
  *             See xlogreader.h for more notes on this facility.
+ *
+ *             This file is compiled as both front-end and backend code, so it
+ *             may not use ereport, server-defined static variables, etc.
  *-------------------------------------------------------------------------
  */
-
 #include "postgres.h"
 
 #include "access/transam.h"
@@ -192,7 +194,7 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
 {
        XLogRecord *record;
        XLogRecPtr      targetPagePtr;
-       bool            randAccess = false;
+       bool            randAccess;
        uint32          len,
                                total_len;
        uint32          targetRecOff;
@@ -200,6 +202,13 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
        bool            gotheader;
        int                     readOff;
 
+       /*
+        * randAccess indicates whether to verify the previous-record pointer of
+        * the record we're reading.  We only do this if we're reading
+        * sequentially, which is what we initially assume.
+        */
+       randAccess = false;
+
        /* reset error state */
        *errormsg = NULL;
        state->errormsg_buf[0] = '\0';
@@ -208,6 +217,7 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
 
        if (RecPtr == InvalidXLogRecPtr)
        {
+               /* No explicit start point; read the record after the one we just read */
                RecPtr = state->EndRecPtr;
 
                if (state->ReadRecPtr == InvalidXLogRecPtr)
@@ -223,11 +233,13 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
        else
        {
                /*
+                * Caller supplied a position to start at.
+                *
                 * In this case, the passed-in record pointer should already be
                 * pointing to a valid record starting position.
                 */
                Assert(XRecOffIsValid(RecPtr));
-               randAccess = true;              /* allow readPageTLI to go backwards too */
+               randAccess = true;
        }
 
        state->currRecPtr = RecPtr;
@@ -309,8 +321,10 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
                /* XXX: more validation should be done here */
                if (total_len < SizeOfXLogRecord)
                {
-                       report_invalid_record(state, "invalid record length at %X/%X",
-                                                                 (uint32) (RecPtr >> 32), (uint32) RecPtr);
+                       report_invalid_record(state,
+                                               "invalid record length at %X/%X: wanted %lu, got %u",
+                                                                 (uint32) (RecPtr >> 32), (uint32) RecPtr,
+                                                                 SizeOfXLogRecord, total_len);
                        goto err;
                }
                gotheader = false;
@@ -463,12 +477,10 @@ XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
 err:
 
        /*
-        * Invalidate the xlog page we've cached. We might read from a different
-        * source after failure.
+        * Invalidate the read state. We might read from a different source after
+        * failure.
         */
-       state->readSegNo = 0;
-       state->readOff = 0;
-       state->readLen = 0;
+       XLogReaderInvalReadState(state);
 
        if (state->errormsg_buf[0] != '\0')
                *errormsg = state->errormsg_buf;
@@ -572,7 +584,7 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
        if (!ValidXLogPageHeader(state, pageptr, hdr))
                goto err;
 
-       /* update cache information */
+       /* update read state information */
        state->readSegNo = targetSegNo;
        state->readOff = targetPageOff;
        state->readLen = readLen;
@@ -580,10 +592,19 @@ ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
        return readLen;
 
 err:
+       XLogReaderInvalReadState(state);
+       return -1;
+}
+
+/*
+ * Invalidate the xlogreader's read state to force a re-read.
+ */
+void
+XLogReaderInvalReadState(XLogReaderState *state)
+{
        state->readSegNo = 0;
        state->readOff = 0;
        state->readLen = 0;
-       return -1;
 }
 
 /*
@@ -600,8 +621,9 @@ ValidXLogRecordHeader(XLogReaderState *state, XLogRecPtr RecPtr,
        if (record->xl_tot_len < SizeOfXLogRecord)
        {
                report_invalid_record(state,
-                                                         "invalid record length at %X/%X",
-                                                         (uint32) (RecPtr >> 32), (uint32) RecPtr);
+                                               "invalid record length at %X/%X: wanted %lu, got %u",
+                                                         (uint32) (RecPtr >> 32), (uint32) RecPtr,
+                                                         SizeOfXLogRecord, record->xl_tot_len);
                return false;
        }
        if (record->xl_rmid > RM_MAX_ID)
@@ -907,11 +929,9 @@ XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
 err:
 out:
        /* Reset state to what we had before finding the record */
-       state->readSegNo = 0;
-       state->readOff = 0;
-       state->readLen = 0;
        state->ReadRecPtr = saved_state.ReadRecPtr;
        state->EndRecPtr = saved_state.EndRecPtr;
+       XLogReaderInvalReadState(state);
 
        return found;
 }
index 444e2180b0c2e1dcf861cfd10410cd1dc960ebc6..2635d80dc0c980a16d3c65fbd7dda7d343e981ea 100644 (file)
 
 #include <unistd.h>
 
-#include "miscadmin.h"
-
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
+#include "miscadmin.h"
 #include "storage/smgr.h"
 #include "utils/guc.h"
 #include "utils/hsearch.h"
@@ -638,8 +637,17 @@ XLogTruncateRelation(RelFileNode rnode, ForkNumber forkNum,
 }
 
 /*
- * TODO: This is duplicate code with pg_xlogdump, similar to walsender.c, but
- * we currently don't have the infrastructure (elog!) to share it.
+ * Read 'count' bytes from WAL into 'buf', starting at location 'startptr'
+ * in timeline 'tli'.
+ *
+ * Will open, and keep open, one WAL segment stored in the static file
+ * descriptor 'sendFile'. This means if XLogRead is used once, there will
+ * always be one descriptor left open until the process ends, but never
+ * more than one.
+ *
+ * XXX This is very similar to pg_xlogdump's XLogDumpXLogRead and to XLogRead
+ * in walsender.c but for small differences (such as lack of elog() in
+ * frontend).  Probably these should be merged at some point.
  */
 static void
 XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
@@ -648,6 +656,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
        XLogRecPtr      recptr;
        Size            nbytes;
 
+       /* state maintained across calls */
        static int      sendFile = -1;
        static XLogSegNo sendSegNo = 0;
        static uint32 sendOff = 0;
@@ -664,11 +673,11 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 
                startoff = recptr % XLogSegSize;
 
+               /* Do we need to switch to a different xlog segment? */
                if (sendFile < 0 || !XLByteInSeg(recptr, sendSegNo))
                {
                        char            path[MAXPGPATH];
 
-                       /* Switch to another logfile segment */
                        if (sendFile >= 0)
                                close(sendFile);
 
@@ -745,7 +754,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
  * Public because it would likely be very helpful for someone writing another
  * output method outside walsender, e.g. in a bgworker.
  *
- * TODO: The walsender has it's own version of this, but it relies on the
+ * TODO: The walsender has its own version of this, but it relies on the
  * walsender's latch being set whenever WAL is flushed. No such infrastructure
  * exists for normal backends, so we have to do a check/sleep/repeat style of
  * loop for now.
index f789fc127d0288302ae7eef75504b244f3351a1f..3853ab4cf5f19119bcc3df1dc61b045bad22898a 100644 (file)
@@ -115,7 +115,7 @@ logical_read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
        int reqLen, XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI)
 {
        return read_local_xlog_page(state, targetPagePtr, reqLen,
-                                                targetRecPtr, cur_page, pageTLI);
+                                                               targetRecPtr, cur_page, pageTLI);
 }
 
 /*
@@ -241,6 +241,10 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 
        PG_TRY();
        {
+               /*
+                * Passing InvalidXLogRecPtr here causes replay to start at the slot's
+                * confirmed_flush.
+                */
                ctx = CreateDecodingContext(InvalidXLogRecPtr,
                                                                        options,
                                                                        logical_read_local_xlog_page,
@@ -263,6 +267,14 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
 
                ctx->output_writer_private = p;
 
+               /*
+                * We start reading xlog from the restart lsn, even though in
+                * CreateDecodingContext we set the snapshot builder up using the
+                * slot's confirmed_flush. This means we might read xlog we don't
+                * actually decode rows from, but the snapshot builder might need it
+                * to get to a consistent point. The point we start returning data to
+                * *users* at is the candidate restart lsn from the decoding context.
+                */
                startptr = MyReplicationSlot->data.restart_lsn;
 
                CurrentResourceOwner = ResourceOwnerCreate(CurrentResourceOwner, "logical decoding");
@@ -280,6 +292,10 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
                        if (errm)
                                elog(ERROR, "%s", errm);
 
+                       /*
+                        * Now that we've set up the xlog reader state, subsequent calls
+                        * pass InvalidXLogRecPtr to say "continue from last record"
+                        */
                        startptr = InvalidXLogRecPtr;
 
                        /*
index 7553cc44cb37ac01881fb11a3e0482261165fa2f..deaa7f5128b54fb26e9ba9b5b0c4b2091ceb36c4 100644 (file)
@@ -139,16 +139,22 @@ struct XLogReaderState
         * ----------------------------------------
         */
 
-       /* Buffer for currently read page (XLOG_BLCKSZ bytes) */
+       /*
+        * Buffer for currently read page (XLOG_BLCKSZ bytes, valid up to at least
+        * readLen bytes)
+        */
        char       *readBuf;
+       uint32          readLen;
 
-       /* last read segment, segment offset, read length, TLI */
+       /* last read segment, segment offset, TLI for data currently in readBuf */
        XLogSegNo       readSegNo;
        uint32          readOff;
-       uint32          readLen;
        TimeLineID      readPageTLI;
 
-       /* beginning of last page read, and its TLI  */
+       /*
+        * beginning of prior page read, and its TLI.  Doesn't necessarily
+        * correspond to what's in readBuf; used for timeline sanity checks.
+        */
        XLogRecPtr      latestPagePtr;
        TimeLineID      latestPageTLI;
 
@@ -174,6 +180,9 @@ extern void XLogReaderFree(XLogReaderState *state);
 extern struct XLogRecord *XLogReadRecord(XLogReaderState *state,
                           XLogRecPtr recptr, char **errormsg);
 
+/* Invalidate read state */
+extern void XLogReaderInvalReadState(XLogReaderState *state);
+
 #ifdef FRONTEND
 extern XLogRecPtr XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr);
 #endif   /* FRONTEND */
index 1b9abce9ad3b61e62f921361432280afc7549817..d027ea173b17b8f89e4fa3e7024072c27a2870a6 100644 (file)
@@ -47,7 +47,9 @@ extern Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum,
 extern Relation CreateFakeRelcacheEntry(RelFileNode rnode);
 extern void FreeFakeRelcacheEntry(Relation fakerel);
 
-extern int read_local_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr,
-       int reqLen, XLogRecPtr targetRecPtr, char *cur_page, TimeLineID *pageTLI);
+extern int read_local_xlog_page(XLogReaderState *state,
+                                        XLogRecPtr targetPagePtr, int reqLen,
+                                        XLogRecPtr targetRecPtr, char *cur_page,
+                                        TimeLineID *pageTLI);
 
 #endif