Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2aee420

Browse files
committed
Update ptrack to version ptrack_v1.5. Fix several race conditions which could lead to incorrect ptrack backups
1 parent 3c3bd22 commit 2aee420

File tree

3 files changed

+201
-33
lines changed

3 files changed

+201
-33
lines changed

src/backend/access/heap/ptrack.c

Lines changed: 193 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,36 @@
33
* ptrack.c
44
* bitmap for tracking updates of relation's pages
55
*
6-
* TODO Add description
7-
*
86
* IDENTIFICATION
97
* src/backend/access/heap/ptrack.c
8+
*
9+
* INTERFACE ROUTINES (PostgreSQL side)
10+
*
11+
* ptrack_add_block - set a bit to track dirtied page
12+
* ptrack_add_block_redo - set a bit to track recovered page
13+
* create_ptrack_init_file - create PTRACK_INIT_FILE
14+
* in the given database directory
15+
*
16+
* EXTERNAL INTERFACE ROUTINES (Backup utility side)
17+
* pg_ptrack_version() - Returns PTRACK version currently in use.
18+
* pg_ptrack_control_lsn() - Gets LSN from ptrack_control file.
19+
* pg_ptrack_clear() - Resets bits in all PTRACK files.
20+
* This function must be called for each database in the cluster.
21+
* pg_ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid) -
22+
* Reads a PTRACK file for the given relation and resets it.
23+
* Returns the PTRACK content as bytea. It is essential to receive and clear the map
24+
* atomically in order to avoid losing PTRACK bits because of race conditions.
25+
* (Imagine that your backup tool reads the map, then some blocks of the relation are
26+
* updated and the ptrack bits are set, after that the backup tool cleans up the map
27+
* and resets ptrack_clear_lsn. So, we may lose some of the updates).
28+
* This function must be called for each database in the cluster.
29+
* pg_ptrack_init_get_and_clear(Oid db_oid, Oid tablespace_oid) -
30+
* Checks whether PTRACK_INIT_FILE exists in the given database and deletes it.
31+
* Returns true if the file was found. This function is analogous to
32+
* pg_ptrack_get_and_clear(), but it handles directory-level changes
33+
* (i.e. CREATE DATABASE, ALTER DATABASE SET TABLESPACE).
34+
* This function must be called for each database in the cluster.
35+
*
1036
*/
1137

1238
#include "postgres.h"
@@ -31,6 +57,8 @@
3157
#include "utils/relfilenodemap.h"
3258
#include "utils/builtins.h"
3359
#include "utils/pg_lsn.h"
60+
#include "utils/lsyscache.h"
61+
#include "nodes/makefuncs.h"
3462
#include <unistd.h>
3563
#include <sys/stat.h>
3664

@@ -53,15 +81,16 @@ bool ptrack_enable = false;
5381

5482
static Buffer ptrack_readbuf(Relation rel, BlockNumber blkno, bool extend);
5583
static void ptrack_extend(Relation rel, BlockNumber nvmblocks);
84+
static void ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf);
85+
5686
void SetPtrackClearLSN(bool set_invalid);
57-
Datum pg_ptrack_test(PG_FUNCTION_ARGS);
87+
5888
Datum pg_ptrack_clear(PG_FUNCTION_ARGS);
5989
Datum pg_ptrack_get_and_clear(PG_FUNCTION_ARGS);
6090
Datum pg_ptrack_get_and_clear_db(PG_FUNCTION_ARGS);
6191
Datum pg_ptrack_control_lsn(PG_FUNCTION_ARGS);
6292

63-
void create_ptrack_init_file(char *dest_dir);
64-
void drop_ptrack_init_file(char *dest_dir);
93+
static void drop_ptrack_init_file(char *dest_dir);
6594

6695
/*
6796
* Mark tracked memory block during recovery.
@@ -83,48 +112,43 @@ ptrack_add_block(Relation rel, BlockNumber heapBlk)
83112
{
84113
Buffer ptrackbuf = InvalidBuffer;
85114

115+
/*
116+
* Do not track changes for unlogged and temp relations,
117+
* since we are not going to backup them anyway.
118+
*/
119+
if (rel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
120+
return;
121+
86122
if (ptrack_enable)
87123
{
88-
ptrack_pin(rel, heapBlk, &ptrackbuf);
124+
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
125+
ptrackbuf = ptrack_readbuf(rel, mapBlock, true);
89126
ptrack_set(heapBlk, ptrackbuf);
90127
ReleaseBuffer(ptrackbuf);
91128
}
92129
}
93130

94-
/* Pin a ptrack map page for setting a bit */
95-
void
96-
ptrack_pin(Relation rel, BlockNumber heapBlk, Buffer *buf)
97-
{
98-
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
99-
100-
*buf = ptrack_readbuf(rel, mapBlock, true);
101-
}
102-
103-
/* Set one bit to buffer */
104-
void
131+
/* Set one bit to buffer */
132+
static void
105133
ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf)
106134
{
107-
BlockNumber mapBlock = HEAPBLK_TO_MAPBLOCK(heapBlk);
108135
uint32 mapByte = HEAPBLK_TO_MAPBYTE(heapBlk);
109136
uint8 mapOffset = HEAPBLK_TO_MAPBIT(heapBlk);
110137
Page page;
111138
char *map;
112139

113-
/* Check that we have the right ptrack page pinned */
114-
if (!BufferIsValid(ptrackBuf)
115-
|| BufferGetBlockNumber(ptrackBuf) != mapBlock)
116-
elog(ERROR, "wrong ptrack buffer passed to ptrack_set");
117-
118140
page = BufferGetPage(ptrackBuf);
119141
map = PageGetContents(page);
120142
LockBuffer(ptrackBuf, BUFFER_LOCK_SHARE);
121143

144+
/* Check if the bit already set */
122145
if (!(map[mapByte] & (1 << mapOffset)))
123146
{
124147
/* Bad luck. Take an exclusive lock now after unlock share.*/
125148
LockBuffer(ptrackBuf, BUFFER_LOCK_UNLOCK);
126149
LockBuffer(ptrackBuf, BUFFER_LOCK_EXCLUSIVE);
127150

151+
/* The bit could have been set concurrently */
128152
if (!(map[mapByte] & (1 << mapOffset)))
129153
{
130154
START_CRIT_SECTION();
@@ -133,8 +157,8 @@ ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf)
133157
MarkBufferDirty(ptrackBuf);
134158

135159
/*
136-
* We don't have Xlog entry for ptrack, but update pages
137-
* on recovery.
160+
* We don't have Xlog entry for ptrack, update pages
161+
* on recovery instead.
138162
*/
139163
END_CRIT_SECTION();
140164
}
@@ -261,6 +285,8 @@ ptrack_extend(Relation rel, BlockNumber ptrack_nblocks)
261285
pfree(pg);
262286
}
263287

288+
289+
264290
/* Clear all blocks of relation's ptrack map */
265291
static void
266292
ptrack_clear_one_rel(Oid relid)
@@ -410,6 +436,7 @@ ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid)
410436
return result;
411437
}
412438

439+
413440
/*
414441
* Reset LSN in ptrack_control file.
415442
* If server started with ptrack_enable = off,
@@ -535,7 +562,11 @@ pg_ptrack_get_and_clear_db(PG_FUNCTION_ARGS)
535562
}
536563
}
537564

538-
/* create empty ptrack_init_file */
565+
/*
566+
* Create PTRACK_INIT_FILE which allows to track changes
567+
* on directory level made by operations which do not go
568+
* through Shared Buffers.
569+
*/
539570
void
540571
create_ptrack_init_file(char *dest_dir)
541572
{
@@ -559,6 +590,7 @@ create_ptrack_init_file(char *dest_dir)
559590
errmsg("could not close file \"%s\": %m", ptrack_init_file_path)));
560591
}
561592

593+
/* Delete PTRACK_INIT_FILE */
562594
void
563595
drop_ptrack_init_file(char *dest_dir)
564596
{
@@ -616,3 +648,138 @@ pg_ptrack_control_lsn(PG_FUNCTION_ARGS)
616648

617649
PG_RETURN_LSN(lsn);
618650
}
651+
652+
653+
static RangeVar *
654+
makeRangeVarFromRelid(Oid relid)
655+
{
656+
char *relname = get_rel_name(relid);
657+
char *nspname = get_namespace_name(get_rel_namespace(relid));
658+
659+
if (relname == NULL)
660+
elog(ERROR, "Invalid relation OID");
661+
662+
return makeRangeVar(nspname, relname, -1);
663+
}
664+
665+
PG_FUNCTION_INFO_V1(pg_ptrack_get_block);
666+
667+
Datum
668+
pg_ptrack_get_block(PG_FUNCTION_ARGS)
669+
{
670+
Oid tablespace_oid = PG_GETARG_OID(0);
671+
Oid relfilenode = PG_GETARG_OID(1);
672+
uint32 blkno = PG_GETARG_UINT32(2);
673+
bytea *raw_page;
674+
RangeVar *relrv;
675+
char *raw_page_data;
676+
Buffer buf;
677+
Oid relationOid = RelidByRelfilenode(tablespace_oid, relfilenode);
678+
Relation rel;
679+
680+
if (relationOid == InvalidOid)
681+
elog(ERROR, "InvalidOid");
682+
683+
elog(DEBUG1, "pg_ptrack_get_block(%i, %i, %u). relationOid %i",
684+
tablespace_oid, relfilenode, blkno, relationOid);
685+
686+
rel = relation_open(relationOid, AccessShareLock);
687+
688+
if (rel == InvalidRelation)
689+
elog(ERROR, "InvalidRelation");
690+
691+
692+
/* Check that this relation has storage */
693+
if (rel->rd_rel->relkind == RELKIND_VIEW)
694+
ereport(ERROR,
695+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
696+
errmsg("cannot get raw page from view \"%s\"",
697+
RelationGetRelationName(rel))));
698+
if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
699+
ereport(ERROR,
700+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
701+
errmsg("cannot get raw page from composite type \"%s\"",
702+
RelationGetRelationName(rel))));
703+
if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
704+
ereport(ERROR,
705+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
706+
errmsg("cannot get raw page from foreign table \"%s\"",
707+
RelationGetRelationName(rel))));
708+
709+
/*
710+
* Reject attempts to read non-local temporary relations; we would be
711+
* likely to get wrong data since we have no visibility into the owning
712+
* session's local buffers.
713+
*/
714+
if (RELATION_IS_OTHER_TEMP(rel))
715+
ereport(ERROR,
716+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
717+
errmsg("cannot access temporary tables of other sessions")));
718+
719+
/* Initialize buffer to copy to */
720+
raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
721+
SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
722+
raw_page_data = VARDATA(raw_page);
723+
724+
/* Take a verbatim copy of the page */
725+
726+
buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, NULL);
727+
LockBuffer(buf, BUFFER_LOCK_SHARE);
728+
729+
memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
730+
731+
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
732+
ReleaseBuffer(buf);
733+
734+
relation_close(rel, AccessShareLock);
735+
736+
PG_RETURN_BYTEA_P(raw_page);
737+
}
738+
739+
PG_FUNCTION_INFO_V1(pg_ptrack_get_block_2);
740+
741+
Datum
742+
pg_ptrack_get_block_2(PG_FUNCTION_ARGS)
743+
{
744+
Oid tablespace_oid = PG_GETARG_OID(0);
745+
Oid db_oid = PG_GETARG_OID(1);
746+
Oid relfilenode = PG_GETARG_OID(2);
747+
BlockNumber blkno = PG_GETARG_UINT32(3);
748+
bytea *raw_page;
749+
char *raw_page_data;
750+
Buffer buf;
751+
RelFileNode rnode;
752+
SMgrRelation smgr;
753+
BlockNumber nblocks;
754+
755+
rnode.dbNode = db_oid;
756+
rnode.spcNode = tablespace_oid;
757+
rnode.relNode = relfilenode;
758+
759+
elog(DEBUG1, "pg_ptrack_get_block_2(%i, %i, %i, %u)",
760+
tablespace_oid, db_oid, relfilenode, blkno);
761+
smgr = smgropen(rnode, InvalidBackendId);
762+
nblocks = smgrnblocks(smgr, MAIN_FORKNUM);
763+
764+
if (blkno >= nblocks)
765+
PG_RETURN_NULL();
766+
767+
/* Initialize buffer to copy to */
768+
raw_page = (bytea *) palloc0(BLCKSZ + VARHDRSZ);
769+
SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
770+
raw_page_data = VARDATA(raw_page);
771+
772+
buf = ReadBufferWithoutRelcache(rnode, MAIN_FORKNUM, blkno, RBM_NORMAL, NULL);
773+
774+
if (buf == InvalidBuffer)
775+
elog(ERROR, "Block is not found in the buffer cache");
776+
777+
LockBuffer(buf, BUFFER_LOCK_SHARE);
778+
779+
memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
780+
781+
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
782+
ReleaseBuffer(buf);
783+
784+
PG_RETURN_BYTEA_P(raw_page);
785+
}

src/include/access/ptrack.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
#include "utils/relcache.h"
99

1010
/* Ptrack version as a string */
11-
#define PTRACK_VERSION "1.3"
11+
#define PTRACK_VERSION "1.5"
1212
/* Ptrack version as a number */
13-
#define PTRACK_VERSION_NUM 103
13+
#define PTRACK_VERSION_NUM 105
1414

1515
/* Number of bits allocated for each heap block. */
1616
#define PTRACK_BITS_PER_HEAPBLOCK 1
@@ -21,10 +21,7 @@ extern PGDLLIMPORT bool ptrack_enable;
2121

2222
extern void ptrack_add_block(Relation rel, BlockNumber heapBlk);
2323
extern void ptrack_add_block_redo(RelFileNode rnode, BlockNumber heapBlk);
24-
extern void ptrack_pin(Relation rel, BlockNumber heapBlk, Buffer *buf);
25-
extern void ptrack_set(BlockNumber heapBlk, Buffer ptrackBuf);
2624
extern void create_ptrack_init_file(char *dest_dir);
27-
extern void drop_ptrack_init_file(char *dest_dir);
2825

2926
extern void ptrack_clear(void);
3027
extern bytea *ptrack_get_and_clear(Oid tablespace_oid, Oid table_oid);

src/include/catalog/pg_proc.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5507,16 +5507,20 @@ DESCR("list of files in the WAL directory");
55075507
#define PROPARALLEL_UNSAFE 'u' /* banned while in parallel mode */
55085508

55095509
/* ptrack related functions*/
5510-
DATA(insert OID = 6016 ( pg_ptrack_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v u 0 0 2278 "" _null_ _null_ _null_ _null_ _null_ pg_ptrack_clear _null_ _null_ _null_ ));
5510+
DATA(insert OID = 6016 ( pg_ptrack_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v u 0 0 2278 "" _null_ _null_ _null_ _null_ _null_ pg_ptrack_clear _null_ _null_ _null_ ));
55115511
DESCR("clear ptrack fork files");
5512-
DATA(insert OID = 6018 ( pg_ptrack_get_and_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 17 "26 26" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_and_clear _null_ _null_ _null_ ));
5512+
DATA(insert OID = 6018 ( pg_ptrack_get_and_clear PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 17 "26 26" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_and_clear _null_ _null_ _null_ ));
55135513
DESCR("get ptrack file as bytea and clear it");
55145514
DATA(insert OID = 6022 ( pg_ptrack_get_and_clear_db PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "26 26" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_and_clear_db _null_ _null_ _null_ ));
55155515
DESCR("check if ptrack_init_file exists in the given database");
55165516
DATA(insert OID = 6021 ( ptrack_version PGNSP PGUID 12 1 0 0 0 f f f f t f s s 0 0 25 "" _null_ _null_ _null_ _null_ _null_ ptrack_version _null_ _null_ _null_ ));
55175517
DESCR("Ptrack version string");
55185518
DATA(insert OID = 6023 ( pg_ptrack_control_lsn PGNSP PGUID 12 1 0 0 0 f f f f t f s s 0 0 3220 "" _null_ _null_ _null_ _null_ _null_ pg_ptrack_control_lsn _null_ _null_ _null_ ));
55195519
DESCR("read LSN from ptrack_control file");
5520+
DATA(insert OID = 6024 ( pg_ptrack_get_block PGNSP PGUID 12 1 0 0 0 f f f f t f s s 3 0 17 "26 26 20" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_block _null_ _null_ _null_ ));
5521+
DESCR("get one block of the relation");
5522+
DATA(insert OID = 6025 ( pg_ptrack_get_block_2 PGNSP PGUID 12 1 0 0 0 f f f f t f s s 4 0 17 "26 26 26 20" _null_ _null_ _null_ _null_ _null_ pg_ptrack_get_block_2 _null_ _null_ _null_ ));
5523+
DESCR("get one block of the relation");
55205524

55215525
/*
55225526
* Symbolic values for proargmodes column. Note that these must agree with

0 commit comments

Comments
 (0)