Created
May 31, 2016 16:53
-
-
Save stalkerg/7ce2394c4f3b36f995895190a633b4fa to your computer and use it in GitHub Desktop.
ptrack patch for Postgres 9.5 stable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile | |
index b83d496..788c55c 100644 | |
--- a/src/backend/access/heap/Makefile | |
+++ b/src/backend/access/heap/Makefile | |
@@ -12,6 +12,6 @@ subdir = src/backend/access/heap | |
top_builddir = ../../../.. | |
include $(top_builddir)/src/Makefile.global | |
-OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o | |
+OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o visibilitymap.o ptrack.o | |
include $(top_srcdir)/src/backend/common.mk | |
diff --git a/src/backend/access/heap/ptrack.c b/src/backend/access/heap/ptrack.c | |
new file mode 100644 | |
index 0000000..7482a61 | |
--- /dev/null | |
+++ b/src/backend/access/heap/ptrack.c | |
@@ -0,0 +1,551 @@ | |
+#include "postgres.h" | |
+ | |
+#include "access/heapam_xlog.h" | |
+#include "access/heapam.h" | |
+#include "access/ptrack.h" | |
+#include "access/xlog.h" | |
+#include "access/xlogutils.h" | |
+#include "access/skey.h" | |
+#include "access/genam.h" | |
+#include "catalog/pg_depend.h" | |
+#include "access/htup_details.h" | |
+#include "miscadmin.h" | |
+#include "storage/bufmgr.h" | |
+#include "storage/lmgr.h" | |
+#include "storage/smgr.h" | |
+#include "utils/inval.h" | |
+#include "utils/array.h" | |
+#include "utils/relfilenodemap.h" | |
+#include <unistd.h> | |
+ | |
+/* Effective data size */ | |
+#define MAPSIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData)) | |
+ | |
+/* Number of bits allocated for each heap block. */ | |
+#define BITS_PER_HEAPBLOCK 1 | |
+ | |
+/* Number of heap blocks we can represent in one byte. */ | |
+#define HEAPBLOCKS_PER_BYTE 8 | |
+ | |
+#define HEAPBLK_TO_MAPBLOCK(x) ((x) / HEAPBLOCKS_PER_PAGE) | |
+#define HEAPBLK_TO_MAPBYTE(x) (((x) % HEAPBLOCKS_PER_PAGE) / HEAPBLOCKS_PER_BYTE) | |
+#define HEAPBLK_TO_MAPBIT(x) ((x) % HEAPBLOCKS_PER_BYTE) | |
+ | |
+#define HEAPBLOCKS_PER_PAGE (MAPSIZE * HEAPBLOCKS_PER_BYTE) | |
+ | |
+typedef struct BlockTrack | |
+{ | |
+ BlockNumber block_number; | |
+ RelFileNode rel; | |
+} BlockTrack; | |
+ | |
+static BlockTrack blocks_track[XLR_MAX_BLOCK_ID]; | |
+unsigned int blocks_track_count = 0; | |
+bool ptrack_enable = false; | |
+ | |
+static Buffer ptrack_readbuf(RelFileNode rnode, BlockNumber blkno, bool extend); | |
+static void ptrack_extend(SMgrRelation smgr, BlockNumber nvmblocks); | |
+static void ptrack_set(BlockNumber heapBlk, Buffer vmBuf); | |
+void SetPtrackClearLSN(bool set_invalid); | |
+Datum pg_ptrack_test(PG_FUNCTION_ARGS); | |
+ | |
+/* Tracking memory block inside critical zone */ | |
+void | |
+ptrack_add_block(BlockNumber block_number, RelFileNode rel) | |
+{ | |
+ BlockTrack *bt = &blocks_track[blocks_track_count]; | |
+ bt->block_number = block_number; | |
+ bt->rel = rel; | |
+ blocks_track_count++; | |
+ Assert(blocks_track_count < XLR_MAX_BLOCK_ID); | |
+} | |
+ | |
+/* Save tracked memory block after end of critical zone */ | |
+void | |
+ptrack_save(void) | |
+{ | |
+ Buffer pbuf = InvalidBuffer; | |
+ unsigned int i; | |
+ | |
+ for (i = 0; i < blocks_track_count; i++) | |
+ { | |
+ BlockTrack *bt = &blocks_track[i]; | |
+ BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(bt->block_number); | |
+ | |
+ /* Reuse the old pinned buffer if possible */ | |
+ if (BufferIsValid(pbuf)) | |
+ { | |
+ if (BufferGetBlockNumber(pbuf) == mapBlock) | |
+ goto set_bit; | |
+ else | |
+ ReleaseBuffer(pbuf); | |
+ } | |
+ | |
+ pbuf = ptrack_readbuf(bt->rel, mapBlock, true); | |
+ set_bit: | |
+ ptrack_set(bt->block_number, pbuf); | |
+ } | |
+ if (pbuf != InvalidBuffer) | |
+ ReleaseBuffer(pbuf); | |
+ | |
+ blocks_track_count = 0; | |
+} | |
+ | |
+/* Set one bit to buffer */ | |
+void | |
+ptrack_set(BlockNumber heapBlk, Buffer vmBuf) | |
+{ | |
+ BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk); | |
+ uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk); | |
+ uint8 mapBit = HEAPBLK_TO_MAPBIT(heapBlk); | |
+ Page page; | |
+ char *map; | |
+ | |
+ /* Check that we have the right VM page pinned */ | |
+ if (!BufferIsValid(vmBuf) || BufferGetBlockNumber(vmBuf) != mapBlock) | |
+ elog(ERROR, "wrong VM buffer passed to ptrack_set"); | |
+ page = BufferGetPage(vmBuf); | |
+ map = PageGetContents(page); | |
+ LockBuffer(vmBuf, BUFFER_LOCK_SHARE); | |
+ | |
+ if (!(map[mapByte] & (1 << mapBit))) | |
+ { | |
+ /* Bad luck. Take an exclusive lock now after unlock share.*/ | |
+ LockBuffer(vmBuf, BUFFER_LOCK_UNLOCK); | |
+ LockBuffer(vmBuf, BUFFER_LOCK_EXCLUSIVE); | |
+ if (!(map[mapByte] & (1 << mapBit))) | |
+ { | |
+ START_CRIT_SECTION(); | |
+ | |
+ map[mapByte] |= (1 << mapBit); | |
+ MarkBufferDirty(vmBuf); | |
+ | |
+ END_CRIT_SECTION_WITHOUT_TRACK(); | |
+ } | |
+ } | |
+ | |
+ LockBuffer(vmBuf, BUFFER_LOCK_UNLOCK); | |
+} | |
+ | |
+static Buffer | |
+ptrack_readbuf(RelFileNode rnode, BlockNumber blkno, bool extend) | |
+{ | |
+ Buffer buf; | |
+ | |
+ SMgrRelation smgr = smgropen(rnode, InvalidBackendId); | |
+ | |
+ /* | |
+ * If we haven't cached the size of the ptrack map fork yet, check it | |
+ * first. | |
+ */ | |
+ if (smgr->smgr_ptrack_nblocks == InvalidBlockNumber) | |
+ { | |
+ if (smgrexists(smgr, PAGESTRACK_FORKNUM)) | |
+ smgr->smgr_ptrack_nblocks = smgrnblocks(smgr, | |
+ PAGESTRACK_FORKNUM); | |
+ else | |
+ smgr->smgr_ptrack_nblocks = 0; | |
+ } | |
+ /* Handle requests beyond EOF */ | |
+ if (blkno >= smgr->smgr_ptrack_nblocks) | |
+ { | |
+ if (extend) | |
+ ptrack_extend(smgr, blkno + 1); | |
+ else | |
+ return InvalidBuffer; | |
+ } | |
+ | |
+ /* | |
+ * Use ZERO_ON_ERROR mode, and initialize the page if necessary. It's | |
+ * always safe to clear bits, so it's better to clear corrupt pages than | |
+ * error out. | |
+ */ | |
+ buf = ReadBufferWithoutRelcache2(smgr, PAGESTRACK_FORKNUM, blkno, | |
+ RBM_ZERO_ON_ERROR, NULL); | |
+ | |
+ if (PageIsNew(BufferGetPage(buf))) | |
+ { | |
+ Page pg = BufferGetPage(buf); | |
+ PageInit(pg, BLCKSZ, 0); | |
+ } | |
+ return buf; | |
+} | |
+ | |
+static void | |
+ptrack_extend(SMgrRelation smgr, BlockNumber vm_nblocks) | |
+{ | |
+ BlockNumber vm_nblocks_now; | |
+ Page pg; | |
+ | |
+ pg = (Page) palloc(BLCKSZ); | |
+ PageInit(pg, BLCKSZ, 0); | |
+ | |
+ LockSmgrForExtension(smgr, ExclusiveLock); | |
+ /* | |
+ * Create the file first if it doesn't exist. If smgr_ptrack_nblocks is | |
+ * positive then it must exist, no need for an smgrexists call. | |
+ */ | |
+ if ((smgr->smgr_ptrack_nblocks == 0 || | |
+ smgr->smgr_ptrack_nblocks == InvalidBlockNumber) && | |
+ !smgrexists(smgr, PAGESTRACK_FORKNUM)) | |
+ smgrcreate(smgr, PAGESTRACK_FORKNUM, false); | |
+ | |
+ vm_nblocks_now = smgrnblocks(smgr, PAGESTRACK_FORKNUM); | |
+ | |
+ /* Now extend the file */ | |
+ while (vm_nblocks_now < vm_nblocks) | |
+ { | |
+ PageSetChecksumInplace(pg, vm_nblocks_now); | |
+ smgrextend(smgr, PAGESTRACK_FORKNUM, vm_nblocks_now, | |
+ (char *) pg, false); | |
+ vm_nblocks_now++; | |
+ } | |
+ /* | |
+ * Send a shared-inval message to force other backends to close any smgr | |
+ * references they may have for this rel, which we are about to change. | |
+ * This is a useful optimization because it means that backends don't have | |
+ * to keep checking for creation or extension of the file, which happens | |
+ * infrequently. | |
+ */ | |
+ CacheInvalidateSmgr(smgr->smgr_rnode); | |
+ /* Update local cache with the up-to-date size */ | |
+ smgr->smgr_ptrack_nblocks = vm_nblocks_now; | |
+ | |
+ pfree(pg); | |
+ | |
+ UnlockSmgrForExtension(smgr, ExclusiveLock); | |
+} | |
+ | |
+/* Clear all ptrack files */ | |
+void | |
+ptrack_clear(void) | |
+{ | |
+ HeapTuple tuple; | |
+ Relation catalog = heap_open(RelationRelationId, AccessShareLock); | |
+ SysScanDesc scan = systable_beginscan(catalog, InvalidOid, false, NULL, 0, NULL); | |
+ | |
+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) | |
+ { | |
+ BlockNumber nblock; | |
+ Relation rel = RelationIdGetRelation(HeapTupleGetOid(tuple)); | |
+ | |
+ RelationOpenSmgr(rel); | |
+ if (rel->rd_smgr == NULL) | |
+ goto end_rel; | |
+ | |
+ LockSmgrForExtension(rel->rd_smgr, ExclusiveLock); | |
+ | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) | |
+ { | |
+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM)) | |
+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr, | |
+ PAGESTRACK_FORKNUM); | |
+ else | |
+ rel->rd_smgr->smgr_ptrack_nblocks = 0; | |
+ } | |
+ | |
+ for(nblock = 0; nblock < rel->rd_smgr->smgr_ptrack_nblocks; nblock++) | |
+ { | |
+ Buffer buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM, | |
+ nblock, RBM_ZERO_ON_ERROR, NULL); | |
+ Page page = BufferGetPage(buf); | |
+ char *map = PageGetContents(page); | |
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); | |
+ START_CRIT_SECTION(); | |
+ MemSet(map, 0, MAPSIZE); | |
+ MarkBufferDirty(buf); | |
+ END_CRIT_SECTION_WITHOUT_TRACK(); | |
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK); | |
+ ReleaseBuffer(buf); | |
+ } | |
+ | |
+ UnlockSmgrForExtension(rel->rd_smgr, ExclusiveLock); | |
+ end_rel: | |
+ RelationClose(rel); | |
+ } | |
+ | |
+ systable_endscan(scan); | |
+ heap_close(catalog, AccessShareLock); | |
+ | |
+ SetPtrackClearLSN(false); | |
+} | |
+ | |
+/* Get ptrack file as bytea and clear him */ | |
+bytea * | |
+ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid) | |
+{ | |
+ bytea *result = NULL; | |
+ BlockNumber nblock; | |
+ if (table_oid == InvalidOid) | |
+ { | |
+ elog(WARNING, "InvalidOid"); | |
+ goto full_end; | |
+ } | |
+ Relation rel = RelationIdGetRelation(RelidByRelfilenode(tablespace_oid, | |
+ table_oid)); | |
+ | |
+ if (rel == InvalidRelation) | |
+ { | |
+ elog(WARNING, "InvalidRelation"); | |
+ goto full_end; | |
+ } | |
+ | |
+ RelationOpenSmgr(rel); | |
+ if (rel->rd_smgr == NULL) | |
+ goto end_rel; | |
+ | |
+ LockSmgrForExtension(rel->rd_smgr, ExclusiveLock); | |
+ | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) | |
+ { | |
+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM)) | |
+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr, | |
+ PAGESTRACK_FORKNUM); | |
+ else | |
+ rel->rd_smgr->smgr_ptrack_nblocks = 0; | |
+ } | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == 0) | |
+ { | |
+ UnlockSmgrForExtension(rel->rd_smgr, ExclusiveLock); | |
+ goto end_rel; | |
+ } | |
+ result = (bytea *) palloc(rel->rd_smgr->smgr_ptrack_nblocks*MAPSIZE + VARHDRSZ); | |
+ SET_VARSIZE(result, rel->rd_smgr->smgr_ptrack_nblocks*MAPSIZE + VARHDRSZ); | |
+ | |
+ for(nblock = 0; nblock < rel->rd_smgr->smgr_ptrack_nblocks; nblock++) | |
+ { | |
+ Buffer buf = ReadBufferExtended(rel, PAGESTRACK_FORKNUM, | |
+ nblock, RBM_ZERO_ON_ERROR, NULL); | |
+ Page page = BufferGetPage(buf); | |
+ char *map = PageGetContents(page); | |
+ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); | |
+ START_CRIT_SECTION(); | |
+ memcpy(VARDATA(result) + nblock*MAPSIZE, map, MAPSIZE); | |
+ MemSet(map, 0, MAPSIZE); | |
+ MarkBufferDirty(buf); | |
+ END_CRIT_SECTION_WITHOUT_TRACK(); | |
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK); | |
+ ReleaseBuffer(buf); | |
+ } | |
+ | |
+ UnlockSmgrForExtension(rel->rd_smgr, ExclusiveLock); | |
+ end_rel: | |
+ RelationClose(rel); | |
+ | |
+ SetPtrackClearLSN(false); | |
+ full_end: | |
+ if (result == NULL) | |
+ { | |
+ result = palloc0(VARHDRSZ); | |
+ SET_VARSIZE(result, VARHDRSZ); | |
+ } | |
+ | |
+ return result; | |
+} | |
+ | |
+void | |
+SetPtrackClearLSN(bool set_invalid) | |
+{ | |
+ int fd; | |
+ XLogRecPtr ptr; | |
+ if (set_invalid) | |
+ ptr = InvalidXLogRecPtr; | |
+ else | |
+ ptr = GetXLogInsertRecPtr(); | |
+ //LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); | |
+ fd = BasicOpenFile("global/ptrack_control", | |
+ O_RDWR | O_CREAT | PG_BINARY, | |
+ S_IRUSR | S_IWUSR); | |
+ if (fd < 0) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not create ptrack control file \"%s\": %m", | |
+ "global/ptrack_control"))); | |
+ | |
+ errno = 0; | |
+ if (write(fd, &ptr, sizeof(XLogRecPtr)) != sizeof(XLogRecPtr)) | |
+ { | |
+ /* if write didn't set errno, assume problem is no disk space */ | |
+ if (errno == 0) | |
+ errno = ENOSPC; | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not write to ptrack control file: %m"))); | |
+ } | |
+ | |
+ if (pg_fsync(fd) != 0) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not fsync ptrack control file: %m"))); | |
+ | |
+ if (close(fd)) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not close ptrack control file: %m"))); | |
+ //LWLockRelease(ControlFileLock); | |
+} | |
+ | |
+/* Test ptrack file */ | |
+Datum | |
+pg_ptrack_test(PG_FUNCTION_ARGS) | |
+{ | |
+ Oid relation_oid = PG_GETARG_OID(0); | |
+ BlockNumber nblock, num_blocks; | |
+ Relation rel; | |
+ XLogRecPtr ptrack_control_lsn; | |
+ Buffer ptrack_buf = InvalidBuffer; | |
+ Page page; | |
+ char *map; | |
+ int fd; | |
+ unsigned int excess_data_counter = 0; | |
+ unsigned int necessary_data_counter = 0; | |
+ ArrayType *result_array; | |
+ Datum result_elems[2]; | |
+ | |
+ if (!superuser() && !has_rolreplication(GetUserId())) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | |
+ (errmsg("must be superuser or replication role to clear ptrack files")))); | |
+ | |
+ /* get LSN from ptrack_control file */ | |
+ fd = BasicOpenFile("global/ptrack_control", | |
+ O_RDONLY | PG_BINARY, | |
+ 0); | |
+ | |
+ if (fd < 0) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not open ptrack control file \"%s\": %m", | |
+ "global/ptrack_control"))); | |
+ errno = 0; | |
+ if (read(fd, &ptrack_control_lsn, sizeof(XLogRecPtr)) != sizeof(XLogRecPtr)) | |
+ { | |
+ /* if write didn't set errno, assume problem is no disk space */ | |
+ if (errno == 0) | |
+ errno = ENOSPC; | |
+ | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not read to ptrack control file: %m"))); | |
+ } | |
+ | |
+ if (close(fd)) | |
+ ereport(PANIC, | |
+ (errcode_for_file_access(), | |
+ errmsg("could not close ptrack control file: %m"))); | |
+ | |
+ rel = RelationIdGetRelation(relation_oid); | |
+ if (rel == InvalidRelation) | |
+ { | |
+ elog(WARNING, "Relation not found."); | |
+ goto end_return; | |
+ } | |
+ LockRelationOid(relation_oid, AccessShareLock); | |
+ RelationOpenSmgr(rel); | |
+ if (rel->rd_smgr == NULL) | |
+ goto end_rel; | |
+ | |
+ LockSmgrForExtension(rel->rd_smgr, ExclusiveLock); | |
+ | |
+ num_blocks = smgrnblocks(rel->rd_smgr, MAIN_FORKNUM); | |
+ if (rel->rd_smgr->smgr_ptrack_nblocks == InvalidBlockNumber) | |
+ { | |
+ if (smgrexists(rel->rd_smgr, PAGESTRACK_FORKNUM)) | |
+ rel->rd_smgr->smgr_ptrack_nblocks = smgrnblocks(rel->rd_smgr, PAGESTRACK_FORKNUM); | |
+ else | |
+ rel->rd_smgr->smgr_ptrack_nblocks = 0; | |
+ } | |
+ | |
+ for(nblock = 0; nblock < num_blocks; nblock++) | |
+ { | |
+ Buffer main_buf = ReadBufferExtended(rel, | |
+ MAIN_FORKNUM, | |
+ nblock, | |
+ RBM_ZERO_ON_ERROR, | |
+ NULL); | |
+ Page main_page; | |
+ XLogRecPtr main_page_lsn; | |
+ | |
+ BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(nblock); | |
+ uint32 mapByte = HEAPBLK_TO_MAPBYTE(nblock); | |
+ uint8 mapBit = HEAPBLK_TO_MAPBIT(nblock); | |
+ | |
+ /* Get page lsn */ | |
+ LockBuffer(main_buf, BUFFER_LOCK_SHARE); | |
+ main_page = BufferGetPage(main_buf); | |
+ main_page_lsn = PageGetLSN(main_page); | |
+ LockBuffer(main_buf, BUFFER_LOCK_UNLOCK); | |
+ ReleaseBuffer(main_buf); | |
+ | |
+ /* Reuse the old pinned buffer if possible */ | |
+ if (BufferIsValid(ptrack_buf)) | |
+ { | |
+ if (BufferGetBlockNumber(ptrack_buf) == mapBlock) | |
+ goto read_bit; | |
+ else | |
+ ReleaseBuffer(ptrack_buf); | |
+ } | |
+ ptrack_buf = ptrack_readbuf(rel->rd_node, mapBlock, false); | |
+ | |
+ read_bit: | |
+ if (ptrack_buf == InvalidBuffer) | |
+ { | |
+ /* not tracked data */ | |
+ if(ptrack_control_lsn < main_page_lsn) | |
+ { | |
+ necessary_data_counter++; | |
+ elog(WARNING, "Block %ud not track. Ptrack lsn:%lx page lsn:%lx", | |
+ nblock, | |
+ ptrack_control_lsn, | |
+ main_page_lsn); | |
+ } | |
+ else | |
+ continue; | |
+ } | |
+ | |
+ page = BufferGetPage(ptrack_buf); | |
+ map = PageGetContents(page); | |
+ LockBuffer(ptrack_buf, BUFFER_LOCK_SHARE); | |
+ if(map[mapByte] & (1 << mapBit)) | |
+ { | |
+ /* excess data */ | |
+ if (ptrack_control_lsn >= main_page_lsn) | |
+ { | |
+ excess_data_counter++; | |
+ elog(WARNING, "Block %ud not needed. Ptrack lsn:%lx page lsn:%lx", | |
+ nblock, | |
+ ptrack_control_lsn, | |
+ main_page_lsn); | |
+ } | |
+ } | |
+ /* not tracked data */ | |
+ else if (ptrack_control_lsn < main_page_lsn) | |
+ { | |
+ necessary_data_counter++; | |
+ elog(WARNING, "Block %ud not tracked. Ptrack lsn:%lx page lsn:%lx", | |
+ nblock, | |
+ ptrack_control_lsn, | |
+ main_page_lsn); | |
+ } | |
+ LockBuffer(ptrack_buf, BUFFER_LOCK_UNLOCK); | |
+ } | |
+ | |
+ end_rel: | |
+ if (ptrack_buf != InvalidBuffer) | |
+ ReleaseBuffer(ptrack_buf); | |
+ RelationClose(rel); | |
+ UnlockRelationOid(relation_oid, AccessShareLock); | |
+ | |
+ end_return: | |
+ result_elems[0] = UInt32GetDatum(excess_data_counter); | |
+ result_elems[1] = UInt32GetDatum(necessary_data_counter); | |
+ result_array = construct_array(result_elems, 2, 23, 4, true, 'i'); | |
+ PG_RETURN_ARRAYTYPE_P(result_array); | |
+} | |
+ | |
+void | |
+assign_ptrack_enable(bool newval, void *extra) | |
+{ | |
+ if(DataDir != NULL && !IsBootstrapProcessingMode() && !newval) | |
+ SetPtrackClearLSN(true); | |
+} | |
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c | |
index f983d79..af06fcd 100644 | |
--- a/src/backend/access/transam/xlog.c | |
+++ b/src/backend/access/transam/xlog.c | |
@@ -6674,6 +6674,7 @@ StartupXLOG(void) | |
do | |
{ | |
bool switchedTLI = false; | |
+ int nblock; | |
#ifdef WAL_DEBUG | |
if (XLOG_DEBUG || | |
@@ -6827,6 +6828,17 @@ StartupXLOG(void) | |
/* Pop the error context stack */ | |
error_context_stack = errcallback.previous; | |
+ if (ptrack_enable) | |
+ for(nblock = 0; nblock < xlogreader->max_block_id; nblock++) | |
+ { | |
+ if(xlogreader->blocks[nblock].forknum == MAIN_FORKNUM) | |
+ { | |
+ ptrack_add_block(xlogreader->blocks[nblock].blkno, | |
+ xlogreader->blocks[nblock].rnode); | |
+ ptrack_save(); | |
+ } | |
+ } | |
+ | |
/* | |
* Update lastReplayedEndRecPtr after this record has been | |
* successfully replayed. | |
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c | |
index 329bb8c..5550396 100644 | |
--- a/src/backend/access/transam/xlogfuncs.c | |
+++ b/src/backend/access/transam/xlogfuncs.c | |
@@ -102,6 +102,30 @@ pg_stop_backup(PG_FUNCTION_ARGS) | |
PG_RETURN_LSN(stoppoint); | |
} | |
+Datum | |
+pg_ptrack_clear(PG_FUNCTION_ARGS) | |
+{ | |
+ if (!superuser() && !has_rolreplication(GetUserId())) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | |
+ (errmsg("must be superuser or replication role to clear ptrack files")))); | |
+ | |
+ ptrack_clear(); | |
+ | |
+ PG_RETURN_VOID(); | |
+} | |
+ | |
+Datum | |
+pg_ptrack_get_and_clear(PG_FUNCTION_ARGS) | |
+{ | |
+ if (!superuser() && !has_rolreplication(GetUserId())) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | |
+ (errmsg("must be superuser or replication role to clear ptrack files")))); | |
+ | |
+ PG_RETURN_BYTEA_P(ptrack_get_and_clear(PG_GETARG_OID(0), PG_GETARG_OID(1))); | |
+} | |
+ | |
/* | |
* pg_switch_xlog: switch to next xlog file | |
*/ | |
diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c | |
index 925c430..265d0f0 100644 | |
--- a/src/backend/access/transam/xloginsert.c | |
+++ b/src/backend/access/transam/xloginsert.c | |
@@ -23,6 +23,7 @@ | |
#include "access/xlog.h" | |
#include "access/xlog_internal.h" | |
#include "access/xloginsert.h" | |
+#include "access/ptrack.h" | |
#include "catalog/pg_control.h" | |
#include "common/pg_lzcompress.h" | |
#include "miscadmin.h" | |
@@ -256,6 +257,10 @@ XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags) | |
#endif | |
regbuf->in_use = true; | |
+ if (ptrack_enable && regbuf->forkno == MAIN_FORKNUM) | |
+ { | |
+ ptrack_add_block(regbuf->block, regbuf->rnode); | |
+ } | |
} | |
/* | |
diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c | |
index d4440c9..a49ab8b 100644 | |
--- a/src/backend/catalog/storage.c | |
+++ b/src/backend/catalog/storage.c | |
@@ -238,6 +238,7 @@ RelationTruncate(Relation rel, BlockNumber nblocks) | |
rel->rd_smgr->smgr_targblock = InvalidBlockNumber; | |
rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber; | |
rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber; | |
+ rel->rd_smgr->smgr_ptrack_nblocks = InvalidBlockNumber; | |
/* Truncate the FSM first if it exists */ | |
fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM); | |
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c | |
index 3647918..780c96d 100644 | |
--- a/src/backend/storage/buffer/bufmgr.c | |
+++ b/src/backend/storage/buffer/bufmgr.c | |
@@ -593,6 +593,17 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum, | |
mode, strategy, &hit); | |
} | |
+Buffer | |
+ReadBufferWithoutRelcache2(SMgrRelation smgr, ForkNumber forkNum, | |
+ BlockNumber blockNum, ReadBufferMode mode, | |
+ BufferAccessStrategy strategy) | |
+{ | |
+ bool hit; | |
+ | |
+ return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum, | |
+ mode, strategy, &hit); | |
+} | |
+ | |
/* | |
* ReadBuffer_common -- common logic for all ReadBuffer variants | |
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c | |
index c052949..07306a3 100644 | |
--- a/src/backend/storage/lmgr/lmgr.c | |
+++ b/src/backend/storage/lmgr/lmgr.c | |
@@ -355,6 +355,30 @@ UnlockRelationForExtension(Relation relation, LOCKMODE lockmode) | |
LockRelease(&tag, lockmode, false); | |
} | |
+void | |
+LockSmgrForExtension(SMgrRelation smgr, LOCKMODE lockmode) | |
+{ | |
+ LOCKTAG tag; | |
+ | |
+ SET_LOCKTAG_SMGR(tag, smgr->smgr_rnode.node.relNode); | |
+ | |
+ (void) LockAcquire(&tag, lockmode, false, false); | |
+} | |
+ | |
+/* | |
+ * UnlockRelationForExtension | |
+ */ | |
+void | |
+UnlockSmgrForExtension(SMgrRelation smgr, LOCKMODE lockmode) | |
+{ | |
+ LOCKTAG tag; | |
+ | |
+ SET_LOCKTAG_SMGR(tag ,smgr->smgr_rnode.node.relNode); | |
+ | |
+ LockRelease(&tag, lockmode, false); | |
+} | |
+ | |
+ | |
/* | |
* LockPage | |
* | |
@@ -987,6 +1011,11 @@ DescribeLockTag(StringInfo buf, const LOCKTAG *tag) | |
tag->locktag_field3, | |
tag->locktag_field4); | |
break; | |
+ case LOCKTAG_SMGR: | |
+ appendStringInfo(buf, | |
+ _("smgr lock %u"), | |
+ tag->locktag_field1); | |
+ break; | |
default: | |
appendStringInfo(buf, | |
_("unrecognized locktag type %d"), | |
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c | |
index 244b4ea..7156894 100644 | |
--- a/src/backend/storage/smgr/smgr.c | |
+++ b/src/backend/storage/smgr/smgr.c | |
@@ -168,6 +168,7 @@ smgropen(RelFileNode rnode, BackendId backend) | |
reln->smgr_targblock = InvalidBlockNumber; | |
reln->smgr_fsm_nblocks = InvalidBlockNumber; | |
reln->smgr_vm_nblocks = InvalidBlockNumber; | |
+ reln->smgr_ptrack_nblocks = InvalidBlockNumber; | |
reln->smgr_which = 0; /* we only have md.c at present */ | |
/* mark it not open */ | |
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c | |
index 5dba5d2..a0d472d 100644 | |
--- a/src/backend/utils/init/postinit.c | |
+++ b/src/backend/utils/init/postinit.c | |
@@ -24,6 +24,7 @@ | |
#include "access/sysattr.h" | |
#include "access/xact.h" | |
#include "access/xlog.h" | |
+#include "access/ptrack.h" | |
#include "catalog/catalog.h" | |
#include "catalog/indexing.h" | |
#include "catalog/namespace.h" | |
@@ -552,6 +553,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, | |
elog(DEBUG3, "InitPostgres"); | |
+ assign_ptrack_enable(ptrack_enable, NULL); | |
+ | |
/* | |
* Add my PGPROC struct to the ProcArray. | |
* | |
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c | |
index f249b390..ffa71de 100644 | |
--- a/src/backend/utils/misc/guc.c | |
+++ b/src/backend/utils/misc/guc.c | |
@@ -31,6 +31,7 @@ | |
#include "access/transam.h" | |
#include "access/twophase.h" | |
#include "access/xact.h" | |
+#include "access/ptrack.h" | |
#include "catalog/namespace.h" | |
#include "commands/async.h" | |
#include "commands/prepare.h" | |
@@ -987,6 +988,16 @@ static struct config_bool ConfigureNamesBool[] = | |
}, | |
{ | |
+ {"ptrack_enable", PGC_SU_BACKEND, WAL_SETTINGS, | |
+ gettext_noop("Enable page tracking."), | |
+ NULL | |
+ }, | |
+ &ptrack_enable, | |
+ false, | |
+ NULL, &assign_ptrack_enable, NULL | |
+ }, | |
+ | |
+ { | |
{"wal_compression", PGC_SUSET, WAL_SETTINGS, | |
gettext_noop("Compresses full-page writes written in WAL file."), | |
NULL | |
diff --git a/src/common/relpath.c b/src/common/relpath.c | |
index 66dfef1..8adb891 100644 | |
--- a/src/common/relpath.c | |
+++ b/src/common/relpath.c | |
@@ -35,7 +35,8 @@ const char *const forkNames[] = { | |
"main", /* MAIN_FORKNUM */ | |
"fsm", /* FSM_FORKNUM */ | |
"vm", /* VISIBILITYMAP_FORKNUM */ | |
- "init" /* INIT_FORKNUM */ | |
+ "init", /* INIT_FORKNUM */ | |
+ "ptrack" /* PAGESTRACK_FORKNUM */ | |
}; | |
/* | |
diff --git a/src/include/access/ptrack.h b/src/include/access/ptrack.h | |
new file mode 100644 | |
index 0000000..3421478 | |
--- /dev/null | |
+++ b/src/include/access/ptrack.h | |
@@ -0,0 +1,22 @@ | |
+#ifndef PTRACK_H | |
+#define PTRACK_H | |
+ | |
+#include "access/xlogdefs.h" | |
+#include "storage/block.h" | |
+#include "storage/buf.h" | |
+#include "storage/relfilenode.h" | |
+#include "utils/relcache.h" | |
+ | |
+extern unsigned int blocks_track_count; | |
+ | |
+extern bool ptrack_enable; | |
+ | |
+extern void ptrack_save(void); | |
+ | |
+extern void ptrack_add_block(BlockNumber block_number, RelFileNode rel); | |
+ | |
+extern void ptrack_clear(void); | |
+extern bytea *ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid); | |
+extern void assign_ptrack_enable(bool newval, void *extra); | |
+ | |
+#endif /* PTRACK_H */ | |
diff --git a/src/include/access/xlog_fn.h b/src/include/access/xlog_fn.h | |
index 3ebe966..c669ea2 100644 | |
--- a/src/include/access/xlog_fn.h | |
+++ b/src/include/access/xlog_fn.h | |
@@ -15,6 +15,7 @@ | |
extern Datum pg_start_backup(PG_FUNCTION_ARGS); | |
extern Datum pg_stop_backup(PG_FUNCTION_ARGS); | |
+extern Datum pg_ptrack_clear(PG_FUNCTION_ARGS); | |
extern Datum pg_switch_xlog(PG_FUNCTION_ARGS); | |
extern Datum pg_create_restore_point(PG_FUNCTION_ARGS); | |
extern Datum pg_current_xlog_location(PG_FUNCTION_ARGS); | |
@@ -31,5 +32,6 @@ extern Datum pg_is_xlog_replay_paused(PG_FUNCTION_ARGS); | |
extern Datum pg_xlog_location_diff(PG_FUNCTION_ARGS); | |
extern Datum pg_is_in_backup(PG_FUNCTION_ARGS); | |
extern Datum pg_backup_start_time(PG_FUNCTION_ARGS); | |
+extern Datum pg_ptrack_test(PG_FUNCTION_ARGS); | |
#endif /* XLOG_FN_H */ | |
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h | |
index 691fa6c..8b2a737 100644 | |
--- a/src/include/catalog/pg_proc.h | |
+++ b/src/include/catalog/pg_proc.h | |
@@ -3143,6 +3143,12 @@ DATA(insert OID = 3814 ( pg_backup_start_time PGNSP PGUID 12 1 0 0 0 f f f f t | |
DESCR("start time of an online backup"); | |
DATA(insert OID = 2848 ( pg_switch_xlog PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ )); | |
DESCR("switch to new xlog file"); | |
+DATA(insert OID = 6016 ( pg_ptrack_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 2278 "" _null_ _null_ _null_ _null_ _null_ pg_ptrack_clear _null_ _null_ _null_ )); | |
+DESCR("clear ptrack fork files"); | |
+DATA(insert OID = 6017 ( pg_ptrack_test PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 1007 "26" _null_ _null_ _null_ _null_ _null_ pg_ptrack_test _null_ _null_ _null_ )); | |
+DESCR("test ptrack fork relation"); | |
+DATA(insert OID = 6018 ( pg_ptrack_get_and_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 17 "26 26" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_and_clear _null_ _null_ _null_ )); | |
+DESCR("get ptrack file as bytea and clear him"); | |
DATA(insert OID = 3098 ( pg_create_restore_point PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 3220 "25" _null_ _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ )); | |
DESCR("create a named restore point"); | |
DATA(insert OID = 2849 ( pg_current_xlog_location PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ )); | |
diff --git a/src/include/common/relpath.h b/src/include/common/relpath.h | |
index a263779..be68567 100644 | |
--- a/src/include/common/relpath.h | |
+++ b/src/include/common/relpath.h | |
@@ -27,7 +27,8 @@ typedef enum ForkNumber | |
MAIN_FORKNUM = 0, | |
FSM_FORKNUM, | |
VISIBILITYMAP_FORKNUM, | |
- INIT_FORKNUM | |
+ INIT_FORKNUM, | |
+ PAGESTRACK_FORKNUM | |
/* | |
* NOTE: if you add a new fork, change MAX_FORKNUM and possibly | |
@@ -36,9 +37,9 @@ typedef enum ForkNumber | |
*/ | |
} ForkNumber; | |
-#define MAX_FORKNUM INIT_FORKNUM | |
+#define MAX_FORKNUM PAGESTRACK_FORKNUM | |
-#define FORKNAMECHARS 4 /* max chars for a fork name */ | |
+#define FORKNAMECHARS 6 /* max chars for a fork name */ | |
extern const char *const forkNames[]; | |
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h | |
index e0d464e..8a71c4d 100644 | |
--- a/src/include/miscadmin.h | |
+++ b/src/include/miscadmin.h | |
@@ -24,6 +24,7 @@ | |
#define MISCADMIN_H | |
#include "pgtime.h" /* for pg_time_t */ | |
+#include "access/ptrack.h" | |
#define PG_BACKEND_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n" | |
@@ -132,6 +133,14 @@ do { \ | |
do { \ | |
Assert(CritSectionCount > 0); \ | |
CritSectionCount--; \ | |
+ if (!CritSectionCount && ptrack_enable && blocks_track_count > 0) \ | |
+ ptrack_save(); \ | |
+} while(0) | |
+ | |
+#define END_CRIT_SECTION_WITHOUT_TRACK() \ | |
+do { \ | |
+ Assert(CritSectionCount > 0); \ | |
+ CritSectionCount--; \ | |
} while(0) | |
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h | |
index 175339b..78d697f 100644 | |
--- a/src/include/storage/bufmgr.h | |
+++ b/src/include/storage/bufmgr.h | |
@@ -18,6 +18,7 @@ | |
#include "storage/buf.h" | |
#include "storage/bufpage.h" | |
#include "storage/relfilenode.h" | |
+#include "storage/smgr.h" | |
#include "utils/relcache.h" | |
typedef void *Block; | |
@@ -153,6 +154,10 @@ extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, | |
extern Buffer ReadBufferWithoutRelcache(RelFileNode rnode, | |
ForkNumber forkNum, BlockNumber blockNum, | |
ReadBufferMode mode, BufferAccessStrategy strategy); | |
+extern Buffer ReadBufferWithoutRelcache2(SMgrRelation smgr, | |
+ ForkNumber forkNum, BlockNumber blockNum, | |
+ ReadBufferMode mode, BufferAccessStrategy strategy); | |
+ | |
extern void ReleaseBuffer(Buffer buffer); | |
extern void UnlockReleaseBuffer(Buffer buffer); | |
extern void MarkBufferDirty(Buffer buffer); | |
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h | |
index e7ccce2..8c62f77 100644 | |
--- a/src/include/storage/lmgr.h | |
+++ b/src/include/storage/lmgr.h | |
@@ -17,6 +17,7 @@ | |
#include "lib/stringinfo.h" | |
#include "storage/itemptr.h" | |
#include "storage/lock.h" | |
+#include "storage/smgr.h" | |
#include "utils/rel.h" | |
@@ -54,6 +55,9 @@ extern void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode); | |
extern void LockRelationForExtension(Relation relation, LOCKMODE lockmode); | |
extern void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode); | |
+extern void LockSmgrForExtension(SMgrRelation smgr, LOCKMODE lockmode); | |
+extern void UnlockSmgrForExtension(SMgrRelation smgr, LOCKMODE lockmode); | |
+ | |
/* Lock a page (currently only used within indexes) */ | |
extern void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode); | |
extern bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode); | |
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h | |
index 96fe3a6..00637b4 100644 | |
--- a/src/include/storage/lock.h | |
+++ b/src/include/storage/lock.h | |
@@ -187,7 +187,8 @@ typedef enum LockTagType | |
* Also, we use DB OID = 0 for shared objects such as tablespaces. | |
*/ | |
LOCKTAG_USERLOCK, /* reserved for old contrib/userlock code */ | |
- LOCKTAG_ADVISORY /* advisory user locks */ | |
+ LOCKTAG_ADVISORY, /* advisory user locks */ | |
+ LOCKTAG_SMGR /**/ | |
} LockTagType; | |
#define LOCKTAG_LAST_TYPE LOCKTAG_ADVISORY | |
@@ -231,6 +232,14 @@ typedef struct LOCKTAG | |
(locktag).locktag_type = LOCKTAG_RELATION_EXTEND, \ | |
(locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD) | |
+#define SET_LOCKTAG_SMGR(locktag,relfilenode) \ | |
+ ((locktag).locktag_field1 = (relfilenode), \ | |
+ (locktag).locktag_field2 = 0, \ | |
+ (locktag).locktag_field3 = 0, \ | |
+ (locktag).locktag_field4 = 0, \ | |
+ (locktag).locktag_type = LOCKTAG_SMGR, \ | |
+ (locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD) | |
+ | |
#define SET_LOCKTAG_PAGE(locktag,dboid,reloid,blocknum) \ | |
((locktag).locktag_field1 = (dboid), \ | |
(locktag).locktag_field2 = (reloid), \ | |
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h | |
index 69a624f..30e9567 100644 | |
--- a/src/include/storage/smgr.h | |
+++ b/src/include/storage/smgr.h | |
@@ -55,6 +55,7 @@ typedef struct SMgrRelationData | |
BlockNumber smgr_targblock; /* current insertion target block */ | |
BlockNumber smgr_fsm_nblocks; /* last known size of fsm fork */ | |
BlockNumber smgr_vm_nblocks; /* last known size of vm fork */ | |
+ BlockNumber smgr_ptrack_nblocks; /* last known size of vm fork */ | |
/* additional public fields may someday exist here */ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment