aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>2016-10-19 14:43:34 +0300
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>2016-10-19 15:00:10 +0300
commit2523bef15e446d25d16b206bac3b6ef6ad6a8a7d (patch)
tree511f732c95c42ab65ecd4f2a492aa49ecafae4f8
parentad8cd4baa3a451887b22b104b2dc2f53133537d4 (diff)
downloadpostgresql-2523bef15e446d25d16b206bac3b6ef6ad6a8a7d.tar.gz
postgresql-2523bef15e446d25d16b206bac3b6ef6ad6a8a7d.zip
Fix WAL-logging of FSM and VM truncation.
When a relation is truncated, it is important that the FSM is truncated as well. Otherwise, after recovery, the FSM can return a page that has been truncated away, leading to errors like: ERROR: could not read block 28991 in file "base/16390/572026": read only 0 of 8192 bytes We were using MarkBufferDirtyHint() to dirty the buffer holding the last remaining page of the FSM, but during recovery, that might in fact not dirty the page, and the FSM update might be lost. To fix, use the stronger MarkBufferDirty() function. MarkBufferDirty() requires us to do WAL-logging ourselves, to protect from a torn page, if checksumming is enabled. Also fix an oversight in visibilitymap_truncate: it also needs to WAL-log when checksumming is enabled. Analysis by Pavan Deolasee. Discussion: <CABOikdNr5vKucqyZH9s1Mh0XebLs_jRhKv6eJfNnD2wxTn=_9A@mail.gmail.com> Backpatch to 9.3, where we got data checksums.
-rw-r--r--src/backend/access/heap/visibilitymap.c16
-rw-r--r--src/backend/storage/freespace/freespace.c22
2 files changed, 37 insertions, 1 deletions
diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c
index a0c0c7f2a6b..ed44a8c0d12 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -473,6 +473,9 @@ visibilitymap_truncate(Relation rel, BlockNumber nheapblocks)
LockBuffer(mapBuffer, BUFFER_LOCK_EXCLUSIVE);
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
/* Clear out the unwanted bytes. */
MemSet(&map[truncByte + 1], 0, MAPSIZE - (truncByte + 1));
@@ -488,7 +491,20 @@ visibilitymap_truncate(Relation rel, BlockNumber nheapblocks)
*/
map[truncByte] &= (1 << truncBit) - 1;
+ /*
+ * Truncation of a relation is WAL-logged at a higher-level, and we
+ * will be called at WAL replay. But if checksums are enabled, we need
+ * to still write a WAL record to protect against a torn page, if the
+ * page is flushed to disk before the truncation WAL record. We cannot
+ * use MarkBufferDirtyHint here, because that will not dirty the page
+ * during recovery.
+ */
MarkBufferDirty(mapBuffer);
+ if (!InRecovery && RelationNeedsWAL(rel) && XLogHintBitIsNeeded())
+ log_newpage_buffer(mapBuffer, false);
+
+ END_CRIT_SECTION();
+
UnlockReleaseBuffer(mapBuffer);
}
else
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c
index 8eee0ce80e6..7362263e418 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -23,7 +23,9 @@
*/
#include "postgres.h"
+#include "access/heapam_xlog.h"
#include "access/htup_details.h"
+#include "access/xlog.h"
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "storage/freespace.h"
@@ -285,8 +287,26 @@ FreeSpaceMapTruncateRel(Relation rel, BlockNumber nblocks)
if (!BufferIsValid(buf))
return; /* nothing to do; the FSM was already smaller */
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
+
+ /* NO EREPORT(ERROR) from here till changes are logged */
+ START_CRIT_SECTION();
+
fsm_truncate_avail(BufferGetPage(buf), first_removed_slot);
- MarkBufferDirtyHint(buf, false);
+
+ /*
+ * Truncation of a relation is WAL-logged at a higher-level, and we
+ * will be called at WAL replay. But if checksums are enabled, we need
+ * to still write a WAL record to protect against a torn page, if the
+ * page is flushed to disk before the truncation WAL record. We cannot
+ * use MarkBufferDirtyHint here, because that will not dirty the page
+ * during recovery.
+ */
+ MarkBufferDirty(buf);
+ if (!InRecovery && RelationNeedsWAL(rel) && XLogHintBitIsNeeded())
+ log_newpage_buffer(buf, false);
+
+ END_CRIT_SECTION();
+
UnlockReleaseBuffer(buf);
new_nfsmblocks = fsm_logical_to_physical(first_removed_address) + 1;