|
| 1 | +diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c |
| 2 | +index 596d2ca5836..3a72b8d2bf4 100644 |
| 3 | +--- a/src/backend/access/transam/xlog.c |
| 4 | ++++ b/src/backend/access/transam/xlog.c |
| 5 | +@@ -136,6 +136,7 @@ intwal_retrieve_retry_interval = 5000; |
| 6 | + intmax_slot_wal_keep_size_mb = -1; |
| 7 | + intwal_decode_buffer_size = 512 * 1024; |
| 8 | + booltrack_wal_io_timing = false; |
| 9 | ++backup_checkpoint_request_hook_type backup_checkpoint_request_hook = NULL; |
| 10 | + |
| 11 | + #ifdef WAL_DEBUG |
| 12 | + boolXLOG_DEBUG = false; |
| 13 | +@@ -8929,6 +8930,12 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces, |
| 14 | + { |
| 15 | + boolcheckpointfpw; |
| 16 | + |
| 17 | ++/* |
| 18 | ++ * Before we call RequestCheckpoint() we need to set |
| 19 | ++ * init_lsn for ptrack map |
| 20 | ++ */ |
| 21 | ++if (backup_checkpoint_request_hook) |
| 22 | ++backup_checkpoint_request_hook(); |
| 23 | + /* |
| 24 | + * Force a CHECKPOINT. Aside from being necessary to prevent torn |
| 25 | + * page problems, this guarantees that two successive backup runs |
| 26 | +diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c |
| 27 | +index f0f88838dc2..239d50be357 100644 |
| 28 | +--- a/src/backend/backup/basebackup.c |
| 29 | ++++ b/src/backend/backup/basebackup.c |
| 30 | +@@ -219,6 +219,12 @@ static const struct exclude_list_item excludeFiles[] = |
| 31 | + |
| 32 | + {"postmaster.pid", false}, |
| 33 | + {"postmaster.opts", false}, |
| 34 | ++/* |
| 35 | ++ * Skip all transient ptrack files, but do copy ptrack.map, since it may |
| 36 | ++ * be successfully used immediately after backup. TODO: check, test? |
| 37 | ++ */ |
| 38 | ++{"ptrack.map.mmap", false}, |
| 39 | ++{"ptrack.map.tmp", false}, |
| 40 | + |
| 41 | + /* end of list */ |
| 42 | + {NULL, false} |
| 43 | +diff --git a/src/backend/storage/file/copydir.c b/src/backend/storage/file/copydir.c |
| 44 | +index aa8c64a2c9e..56ed1b4df0a 100644 |
| 45 | +--- a/src/backend/storage/file/copydir.c |
| 46 | ++++ b/src/backend/storage/file/copydir.c |
| 47 | +@@ -30,6 +30,8 @@ |
| 48 | + #include "storage/copydir.h" |
| 49 | + #include "storage/fd.h" |
| 50 | + |
| 51 | ++copydir_hook_type copydir_hook = NULL; |
| 52 | ++ |
| 53 | + /* GUCs */ |
| 54 | + intfile_copy_method = FILE_COPY_METHOD_COPY; |
| 55 | + |
| 56 | +@@ -91,6 +93,9 @@ copydir(const char *fromdir, const char *todir, bool recurse) |
| 57 | + } |
| 58 | + FreeDir(xldir); |
| 59 | + |
| 60 | ++if (copydir_hook) |
| 61 | ++copydir_hook(todir); |
| 62 | ++ |
| 63 | + /* |
| 64 | + * Be paranoid here and fsync all files to ensure the copy is really done. |
| 65 | + * But if fsync is disabled, we're done. |
| 66 | +diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c |
| 67 | +index 2ccb0faceb5..ad08c0c6960 100644 |
| 68 | +--- a/src/backend/storage/smgr/md.c |
| 69 | ++++ b/src/backend/storage/smgr/md.c |
| 70 | +@@ -86,6 +86,8 @@ typedef struct _MdfdVec |
| 71 | + |
| 72 | + static MemoryContext MdCxt;/* context for all MdfdVec objects */ |
| 73 | + |
| 74 | ++mdextend_hook_type mdextend_hook = NULL; |
| 75 | ++mdwrite_hook_type mdwrite_hook = NULL; |
| 76 | + |
| 77 | + /* Populate a file tag describing an md.c segment file. */ |
| 78 | + #define INIT_MD_FILETAG(a,xx_rlocator,xx_forknum,xx_segno) \ |
| 79 | +@@ -530,6 +532,9 @@ mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, |
| 80 | + register_dirty_segment(reln, forknum, v); |
| 81 | + |
| 82 | + Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE)); |
| 83 | ++ |
| 84 | ++if (mdextend_hook) |
| 85 | ++mdextend_hook(reln->smgr_rlocator, forknum, blocknum); |
| 86 | + } |
| 87 | + |
| 88 | + /* |
| 89 | +@@ -637,6 +642,12 @@ mdzeroextend(SMgrRelation reln, ForkNumber forknum, |
| 90 | + |
| 91 | + remblocks -= numblocks; |
| 92 | + curblocknum += numblocks; |
| 93 | ++ |
| 94 | ++if (mdextend_hook) |
| 95 | ++{ |
| 96 | ++for (; blocknum < curblocknum; blocknum++) |
| 97 | ++mdextend_hook(reln->smgr_rlocator, forknum, blocknum); |
| 98 | ++} |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | +@@ -1139,7 +1150,14 @@ mdwritev(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, |
| 103 | + |
| 104 | + nblocks -= nblocks_this_segment; |
| 105 | + buffers += nblocks_this_segment; |
| 106 | +-blocknum += nblocks_this_segment; |
| 107 | ++ |
| 108 | ++if (mdwrite_hook) |
| 109 | ++{ |
| 110 | ++for (; nblocks_this_segment--; blocknum++) |
| 111 | ++mdwrite_hook(reln->smgr_rlocator, forknum, blocknum); |
| 112 | ++} |
| 113 | ++else |
| 114 | ++blocknum += nblocks_this_segment; |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | +diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c |
| 119 | +index fc16db90133..08cd553ad55 100644 |
| 120 | +--- a/src/backend/storage/sync/sync.c |
| 121 | ++++ b/src/backend/storage/sync/sync.c |
| 122 | +@@ -74,6 +74,8 @@ static MemoryContext pendingOpsCxt; /* context for the above */ |
| 123 | + static CycleCtr sync_cycle_ctr = 0; |
| 124 | + static CycleCtr checkpoint_cycle_ctr = 0; |
| 125 | + |
| 126 | ++ProcessSyncRequests_hook_type ProcessSyncRequests_hook = NULL; |
| 127 | ++ |
| 128 | + /* Intervals for calling AbsorbSyncRequests */ |
| 129 | + #define FSYNCS_PER_ABSORB10 |
| 130 | + #define UNLINKS_PER_ABSORB10 |
| 131 | +@@ -470,6 +472,9 @@ ProcessSyncRequests(void) |
| 132 | + CheckpointStats.ckpt_longest_sync = longest; |
| 133 | + CheckpointStats.ckpt_agg_sync_time = total_elapsed; |
| 134 | + |
| 135 | ++if (ProcessSyncRequests_hook) |
| 136 | ++ProcessSyncRequests_hook(); |
| 137 | ++ |
| 138 | + /* Flag successful completion of ProcessSyncRequests */ |
| 139 | + sync_in_progress = false; |
| 140 | + } |
| 141 | +diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c |
| 142 | +index df182577a19..8e4d7526db1 100644 |
| 143 | +--- a/src/backend/utils/init/miscinit.c |
| 144 | ++++ b/src/backend/utils/init/miscinit.c |
| 145 | +@@ -80,6 +80,11 @@ static Latch LocalLatchData; |
| 146 | + |
| 147 | + boolIgnoreSystemIndexes = false; |
| 148 | + |
| 149 | ++/* ---------------------------------------------------------------- |
| 150 | ++ *Ptrack functions support |
| 151 | ++ * ---------------------------------------------------------------- |
| 152 | ++ */ |
| 153 | ++static void check_use_ptrack(const char *libraries); |
| 154 | + |
| 155 | + /* ---------------------------------------------------------------- |
| 156 | + *common process startup code |
| 157 | +@@ -1908,6 +1913,8 @@ process_shared_preload_libraries(void) |
| 158 | + false); |
| 159 | + process_shared_preload_libraries_in_progress = false; |
| 160 | + process_shared_preload_libraries_done = true; |
| 161 | ++ |
| 162 | ++check_use_ptrack(shared_preload_libraries_string); |
| 163 | + } |
| 164 | + |
| 165 | + /* |
| 166 | +@@ -1950,3 +1957,71 @@ pg_bindtextdomain(const char *domain) |
| 167 | + } |
| 168 | + #endif |
| 169 | + } |
| 170 | ++ |
| 171 | ++/*------------------------------------------------------------------------- |
| 172 | ++ *Ptrack functions support |
| 173 | ++ *------------------------------------------------------------------------- |
| 174 | ++ */ |
| 175 | ++ |
| 176 | ++/* Persistent copy of ptrack.map to restore after crash */ |
| 177 | ++#define PTRACK_PATH "global/ptrack.map" |
| 178 | ++/* Used for atomical crash-safe update of ptrack.map */ |
| 179 | ++#define PTRACK_PATH_TMP "global/ptrack.map.tmp" |
| 180 | ++ |
| 181 | ++/* |
| 182 | ++ * Check that path is accessible by us and return true if it is |
| 183 | ++ * not a directory. |
| 184 | ++ */ |
| 185 | ++static bool |
| 186 | ++ptrack_file_exists(const char *path) |
| 187 | ++{ |
| 188 | ++struct stat st; |
| 189 | ++ |
| 190 | ++Assert(path != NULL); |
| 191 | ++ |
| 192 | ++if (stat(path, &st) == 0) |
| 193 | ++return S_ISDIR(st.st_mode) ? false : true; |
| 194 | ++else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES)) |
| 195 | ++ereport(ERROR, |
| 196 | ++(errcode_for_file_access(), |
| 197 | ++ errmsg("could not access file \"%s\": %m", path))); |
| 198 | ++ |
| 199 | ++return false; |
| 200 | ++} |
| 201 | ++ |
| 202 | ++/* |
| 203 | ++ * Delete ptrack files when ptrack is disabled. |
| 204 | ++ * |
| 205 | ++ * This is performed by postmaster at start, |
| 206 | ++ * so that there are no concurrent delete issues. |
| 207 | ++ */ |
| 208 | ++static void |
| 209 | ++ptrackCleanFiles(void) |
| 210 | ++{ |
| 211 | ++charptrack_path[MAXPGPATH]; |
| 212 | ++charptrack_path_tmp[MAXPGPATH]; |
| 213 | ++ |
| 214 | ++sprintf(ptrack_path, "%s/%s", DataDir, PTRACK_PATH); |
| 215 | ++sprintf(ptrack_path_tmp, "%s/%s", DataDir, PTRACK_PATH_TMP); |
| 216 | ++ |
| 217 | ++elog(DEBUG1, "ptrack: clean map files"); |
| 218 | ++ |
| 219 | ++if (ptrack_file_exists(ptrack_path_tmp)) |
| 220 | ++durable_unlink(ptrack_path_tmp, LOG); |
| 221 | ++ |
| 222 | ++if (ptrack_file_exists(ptrack_path)) |
| 223 | ++durable_unlink(ptrack_path, LOG); |
| 224 | ++} |
| 225 | ++ |
| 226 | ++static void |
| 227 | ++check_use_ptrack(const char *libraries) |
| 228 | ++{ |
| 229 | ++/* We need to delete the ptrack.map file when ptrack was turned off */ |
| 230 | ++if (strstr(shared_preload_libraries_string, "ptrack") == NULL) |
| 231 | ++{ |
| 232 | ++ptrackCleanFiles(); |
| 233 | ++} |
| 234 | ++} |
| 235 | ++ |
| 236 | ++#undef PTRACK_PATH |
| 237 | ++#undef PTRACK_PATH_TMP |
| 238 | +diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c |
| 239 | +index f20be82862a..84c1d82b9cd 100644 |
| 240 | +--- a/src/bin/pg_checksums/pg_checksums.c |
| 241 | ++++ b/src/bin/pg_checksums/pg_checksums.c |
| 242 | +@@ -109,6 +109,11 @@ static const struct exclude_list_item skip[] = { |
| 243 | + {"pg_filenode.map", false}, |
| 244 | + {"pg_internal.init", true}, |
| 245 | + {"PG_VERSION", false}, |
| 246 | ++ |
| 247 | ++{"ptrack.map.mmap", false}, |
| 248 | ++{"ptrack.map", false}, |
| 249 | ++{"ptrack.map.tmp", false}, |
| 250 | ++ |
| 251 | + #ifdef EXEC_BACKEND |
| 252 | + {"config_exec_params", true}, |
| 253 | + #endif |
| 254 | +diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c |
| 255 | +index e876f35f38e..1fa3bf4d222 100644 |
| 256 | +--- a/src/bin/pg_resetwal/pg_resetwal.c |
| 257 | ++++ b/src/bin/pg_resetwal/pg_resetwal.c |
| 258 | +@@ -87,6 +87,7 @@ static void FindEndOfXLOG(void); |
| 259 | + static void KillExistingXLOG(void); |
| 260 | + static void KillExistingArchiveStatus(void); |
| 261 | + static void KillExistingWALSummaries(void); |
| 262 | ++static void KillExistingPtrack(void); |
| 263 | + static void WriteEmptyXLOG(void); |
| 264 | + static void usage(void); |
| 265 | + |
| 266 | +@@ -517,6 +518,7 @@ main(int argc, char *argv[]) |
| 267 | + KillExistingXLOG(); |
| 268 | + KillExistingArchiveStatus(); |
| 269 | + KillExistingWALSummaries(); |
| 270 | ++KillExistingPtrack(); |
| 271 | + WriteEmptyXLOG(); |
| 272 | + |
| 273 | + printf(_("Write-ahead log reset\n")); |
| 274 | +@@ -1022,6 +1024,40 @@ KillExistingXLOG(void) |
| 275 | + pg_fatal("could not close directory \"%s\": %m", XLOGDIR); |
| 276 | + } |
| 277 | + |
| 278 | ++/* |
| 279 | ++ * Remove existing ptrack files |
| 280 | ++ */ |
| 281 | ++static void |
| 282 | ++KillExistingPtrack(void) |
| 283 | ++{ |
| 284 | ++#define PTRACKDIR "global" |
| 285 | ++ |
| 286 | ++DIR *xldir; |
| 287 | ++struct dirent *xlde; |
| 288 | ++char path[MAXPGPATH + sizeof(PTRACKDIR)]; |
| 289 | ++ |
| 290 | ++xldir = opendir(PTRACKDIR); |
| 291 | ++if (xldir == NULL) |
| 292 | ++pg_fatal("could not open directory \"%s\": %m", PTRACKDIR); |
| 293 | ++ |
| 294 | ++while (errno = 0, (xlde = readdir(xldir)) != NULL) |
| 295 | ++{ |
| 296 | ++if (strcmp(xlde->d_name, "ptrack.map.mmap") == 0 || |
| 297 | ++strcmp(xlde->d_name, "ptrack.map") == 0 || |
| 298 | ++strcmp(xlde->d_name, "ptrack.map.tmp") == 0) |
| 299 | ++{ |
| 300 | ++snprintf(path, sizeof(path), "%s/%s", PTRACKDIR, xlde->d_name); |
| 301 | ++if (unlink(path) < 0) |
| 302 | ++pg_fatal("could not delete file \"%s\": %m", path); |
| 303 | ++} |
| 304 | ++} |
| 305 | ++ |
| 306 | ++if (errno) |
| 307 | ++pg_fatal("could not read directory \"%s\": %m", PTRACKDIR); |
| 308 | ++ |
| 309 | ++if (closedir(xldir)) |
| 310 | ++pg_fatal("could not close directory \"%s\": %m", PTRACKDIR); |
| 311 | ++} |
| 312 | + |
| 313 | + /* |
| 314 | + * Remove existing archive status files |
| 315 | +diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c |
| 316 | +index c933871ca9f..f34c0edf350 100644 |
| 317 | +--- a/src/bin/pg_rewind/filemap.c |
| 318 | ++++ b/src/bin/pg_rewind/filemap.c |
| 319 | +@@ -186,6 +186,10 @@ static const struct exclude_list_item excludeFiles[] = |
| 320 | + {"postmaster.pid", false}, |
| 321 | + {"postmaster.opts", false}, |
| 322 | + |
| 323 | ++{"ptrack.map.mmap", false}, |
| 324 | ++{"ptrack.map", false}, |
| 325 | ++{"ptrack.map.tmp", false}, |
| 326 | ++ |
| 327 | + /* end of list */ |
| 328 | + {NULL, false} |
| 329 | + }; |
| 330 | +diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h |
| 331 | +index adddac6710e..d57d04a6fd5 100644 |
| 332 | +--- a/src/include/access/xlog.h |
| 333 | ++++ b/src/include/access/xlog.h |
| 334 | +@@ -59,6 +59,9 @@ extern PGDLLIMPORT int wal_decode_buffer_size; |
| 335 | + |
| 336 | + extern PGDLLIMPORT int CheckPointSegments; |
| 337 | + |
| 338 | ++typedef void (*backup_checkpoint_request_hook_type) (void); |
| 339 | ++extern PGDLLIMPORT backup_checkpoint_request_hook_type backup_checkpoint_request_hook; |
| 340 | ++ |
| 341 | + /* Archive modes */ |
| 342 | + typedef enum ArchiveMode |
| 343 | + { |
| 344 | +diff --git a/src/include/storage/copydir.h b/src/include/storage/copydir.h |
| 345 | +index f1d7beeed1a..f162c4405dc 100644 |
| 346 | +--- a/src/include/storage/copydir.h |
| 347 | ++++ b/src/include/storage/copydir.h |
| 348 | +@@ -22,6 +22,9 @@ typedef enum FileCopyMethod |
| 349 | + /* GUC parameters */ |
| 350 | + extern PGDLLIMPORT int file_copy_method; |
| 351 | + |
| 352 | ++typedef void (*copydir_hook_type) (const char *path); |
| 353 | ++extern PGDLLIMPORT copydir_hook_type copydir_hook; |
| 354 | ++ |
| 355 | + extern void copydir(const char *fromdir, const char *todir, bool recurse); |
| 356 | + extern void copy_file(const char *fromfile, const char *tofile); |
| 357 | + |
| 358 | +diff --git a/src/include/storage/md.h b/src/include/storage/md.h |
| 359 | +index b563c27abf0..29fbc1c80c3 100644 |
| 360 | +--- a/src/include/storage/md.h |
| 361 | ++++ b/src/include/storage/md.h |
| 362 | +@@ -22,6 +22,13 @@ |
| 363 | + |
| 364 | + extern PGDLLIMPORT const PgAioHandleCallbacks aio_md_readv_cb; |
| 365 | + |
| 366 | ++typedef void (*mdextend_hook_type) (RelFileLocatorBackend smgr_rlocator, |
| 367 | ++ForkNumber forknum, BlockNumber blocknum); |
| 368 | ++extern PGDLLIMPORT mdextend_hook_type mdextend_hook; |
| 369 | ++typedef void (*mdwrite_hook_type) (RelFileLocatorBackend smgr_rlocator, |
| 370 | ++ForkNumber forknum, BlockNumber blocknum); |
| 371 | ++extern PGDLLIMPORT mdwrite_hook_type mdwrite_hook; |
| 372 | ++ |
| 373 | + /* md storage manager functionality */ |
| 374 | + extern void mdinit(void); |
| 375 | + extern void mdopen(SMgrRelation reln); |
| 376 | +diff --git a/src/include/storage/sync.h b/src/include/storage/sync.h |
| 377 | +index c2272d14175..4132cfd92c6 100644 |
| 378 | +--- a/src/include/storage/sync.h |
| 379 | ++++ b/src/include/storage/sync.h |
| 380 | +@@ -55,6 +55,9 @@ typedef struct FileTag |
| 381 | + uint64segno; |
| 382 | + } FileTag; |
| 383 | + |
| 384 | ++typedef void (*ProcessSyncRequests_hook_type) (void); |
| 385 | ++extern PGDLLIMPORT ProcessSyncRequests_hook_type ProcessSyncRequests_hook; |
| 386 | ++ |
| 387 | + extern void InitSync(void); |
| 388 | + extern void SyncPreCheckpoint(void); |
| 389 | + extern void SyncPostCheckpoint(void); |