/* * Load all replication slots from disk into memory at server startup. This * needs to be run before we start crash recovery. */ void StartupReplicationSlots(void) { DIR *replication_dir; struct dirent *replication_de; elog(DEBUG1, "starting up replication slots"); /* restore all slots by iterating over all on-disk entries */ replication_dir = AllocateDir("pg_replslot"); while ((replication_de = ReadDir(replication_dir, "pg_replslot")) != NULL) { struct stat statbuf; char path[MAXPGPATH + 12]; if (strcmp(replication_de->d_name, ".") == 0 || strcmp(replication_de->d_name, "..") == 0) continue; snprintf(path, sizeof(path), "pg_replslot/%s", replication_de->d_name); /* we're only creating directories here, skip if it's not our's */ if (lstat(path, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode)) continue; /* we crashed while a slot was being setup or deleted, clean up */ if (pg_str_endswith(replication_de->d_name, ".tmp")) { if (!rmtree(path, true)) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not remove directory \"%s\"", path))); continue; } fsync_fname("pg_replslot", true); continue; } /* looks like a slot in a normal state, restore */ RestoreSlotFromDisk(replication_de->d_name); } FreeDir(replication_dir); /* currently no slots exist, we're done. */ if (max_replication_slots <= 0) return; /* Now that we have recovered all the data, compute replication xmin */ ReplicationSlotsComputeRequiredXmin(false); ReplicationSlotsComputeRequiredLSN(); }
/* * Callback for processing source file list. * * This is called once for every file in the source server. We decide what * action needs to be taken for the file, depending on whether the file * exists in the target and whether the size matches. */ void process_source_file(const char *path, file_type_t type, size_t newsize, const char *link_target) { bool exists; char localpath[MAXPGPATH]; struct stat statbuf; filemap_t *map = filemap; file_action_t action = FILE_ACTION_NONE; size_t oldsize = 0; file_entry_t *entry; Assert(map->array == NULL); /* * Completely ignore some special files in source and destination. */ if (strcmp(path, "postmaster.pid") == 0 || strcmp(path, "postmaster.opts") == 0) return; /* * Skip temporary files, .../pgsql_tmp/... and .../pgsql_tmp.* in source. * This has the effect that all temporary files in the destination will be * removed. */ if (strstr(path, "/" PG_TEMP_FILE_PREFIX) != NULL) return; if (strstr(path, "/" PG_TEMP_FILES_DIR "/") != NULL) return; /* * sanity check: a filename that looks like a data file better be a * regular file */ if (type != FILE_TYPE_REGULAR && isRelDataFile(path)) pg_fatal("data file in source \"%s\" is not a regular file\n", path); snprintf(localpath, sizeof(localpath), "%s/%s", datadir_target, path); /* Does the corresponding file exist in the target data dir? */ if (lstat(localpath, &statbuf) < 0) { if (errno != ENOENT) pg_fatal("could not stat file \"%s\": %s\n", localpath, strerror(errno)); exists = false; } else exists = true; switch (type) { case FILE_TYPE_DIRECTORY: if (exists && !S_ISDIR(statbuf.st_mode)) { /* it's a directory in source, but not in target. Strange.. */ pg_fatal("\"%s\" is not a directory\n", localpath); } if (!exists) action = FILE_ACTION_CREATE; else action = FILE_ACTION_NONE; oldsize = 0; break; case FILE_TYPE_SYMLINK: if (exists && #ifndef WIN32 !S_ISLNK(statbuf.st_mode) #else !pgwin32_is_junction(localpath) #endif ) { /* * It's a symbolic link in source, but not in target. * Strange.. */ pg_fatal("\"%s\" is not a symbolic link\n", localpath); } if (!exists) action = FILE_ACTION_CREATE; else action = FILE_ACTION_NONE; oldsize = 0; break; case FILE_TYPE_REGULAR: if (exists && !S_ISREG(statbuf.st_mode)) pg_fatal("\"%s\" is not a regular file\n", localpath); if (!exists || !isRelDataFile(path)) { /* * File exists in source, but not in target. Or it's a * non-data file that we have no special processing for. Copy * it in toto. * * An exception: PG_VERSIONs should be identical, but avoid * overwriting it for paranoia. */ if (pg_str_endswith(path, "PG_VERSION")) { action = FILE_ACTION_NONE; oldsize = statbuf.st_size; } else { action = FILE_ACTION_COPY; oldsize = 0; } } else { /* * It's a data file that exists in both. * * If it's larger in target, we can truncate it. There will * also be a WAL record of the truncation in the source * system, so WAL replay would eventually truncate the target * too, but we might as well do it now. * * If it's smaller in the target, it means that it has been * truncated in the target, or enlarged in the source, or * both. If it was truncated in the target, we need to copy * the missing tail from the source system. If it was enlarged * in the source system, there will be WAL records in the * source system for the new blocks, so we wouldn't need to * copy them here. But we don't know which scenario we're * dealing with, and there's no harm in copying the missing * blocks now, so do it now. * * If it's the same size, do nothing here. Any blocks modified * in the target will be copied based on parsing the target * system's WAL, and any blocks modified in the source will be * updated after rewinding, when the source system's WAL is * replayed. */ oldsize = statbuf.st_size; if (oldsize < newsize) action = FILE_ACTION_COPY_TAIL; else if (oldsize > newsize) action = FILE_ACTION_TRUNCATE; else action = FILE_ACTION_NONE; } break; } /* Create a new entry for this file */ entry = pg_malloc(sizeof(file_entry_t)); entry->path = pg_strdup(path); entry->type = type; entry->action = action; entry->oldsize = oldsize; entry->newsize = newsize; entry->link_target = link_target ? pg_strdup(link_target) : NULL; entry->next = NULL; entry->pagemap.bitmap = NULL; entry->pagemap.bitmapsize = 0; entry->isrelfile = isRelDataFile(path); if (map->last) { map->last->next = entry; map->last = entry; } else map->first = map->last = entry; map->nlist++; }