Hi, The replication slots data is stored in binary format on the disk under the pg_replslot/<<slot_name>> directory which isn't human readable. If the server is crashed/down (for whatever reasons) and unable to come up, currently there's no way for the user/admin/developer to know what were all the replication slots available at the time of server crash/down to figure out what's the restart lsn, xid, two phase info or types of slots etc.
pg_replslotdata is a tool that interprets the replication slots information and displays it onto the stdout even if the server is crashed/down. The design of this tool is similar to other tools available in the core today i.e. pg_controldata, pg_waldump. Attaching initial patch herewith. I will improve it with documentation and other stuff a bit later. Please see the attached picture for the sample output. Thoughts? Regards, Bharath Rupireddy.
From dd31d7ff207b5ac98adb0bac18fdcaa5bf9883ff Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> Date: Sun, 21 Nov 2021 13:47:56 +0000 Subject: [PATCH v1] pg_replslotdata --- src/backend/replication/slot.c | 47 +-- src/bin/Makefile | 1 + src/bin/pg_replslotdata/.gitignore | 2 + src/bin/pg_replslotdata/Makefile | 44 +++ src/bin/pg_replslotdata/nls.mk | 6 + src/bin/pg_replslotdata/pg_replslotdata.c | 364 ++++++++++++++++++++++ src/bin/pg_replslotdata/t/001_basic.pl | 11 + src/include/replication/slot.h | 82 +---- src/include/replication/slot_common.h | 132 ++++++++ 9 files changed, 565 insertions(+), 124 deletions(-) create mode 100644 src/bin/pg_replslotdata/.gitignore create mode 100644 src/bin/pg_replslotdata/Makefile create mode 100644 src/bin/pg_replslotdata/nls.mk create mode 100644 src/bin/pg_replslotdata/pg_replslotdata.c create mode 100644 src/bin/pg_replslotdata/t/001_basic.pl create mode 100644 src/include/replication/slot_common.h diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index e4f5c6fb36..236ebe999a 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -50,45 +50,6 @@ #include "storage/procarray.h" #include "utils/builtins.h" -/* - * Replication slot on-disk data structure. - */ -typedef struct ReplicationSlotOnDisk -{ - /* first part of this struct needs to be version independent */ - - /* data not covered by checksum */ - uint32 magic; - pg_crc32c checksum; - - /* data covered by checksum */ - uint32 version; - uint32 length; - - /* - * The actual data in the slot that follows can differ based on the above - * 'version'. - */ - - ReplicationSlotPersistentData slotdata; -} ReplicationSlotOnDisk; - -/* size of version independent data */ -#define ReplicationSlotOnDiskConstantSize \ - offsetof(ReplicationSlotOnDisk, slotdata) -/* size of the part of the slot not covered by the checksum */ -#define SnapBuildOnDiskNotChecksummedSize \ - offsetof(ReplicationSlotOnDisk, version) -/* size of the part covered by the checksum */ -#define SnapBuildOnDiskChecksummedSize \ - sizeof(ReplicationSlotOnDisk) - SnapBuildOnDiskNotChecksummedSize -/* size of the slot data that is version dependent */ -#define ReplicationSlotOnDiskV2Size \ - sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskConstantSize - -#define SLOT_MAGIC 0x1051CA1 /* format identifier */ -#define SLOT_VERSION 2 /* version for new files */ - /* Control array for replication slot management */ ReplicationSlotCtlData *ReplicationSlotCtl = NULL; @@ -1571,8 +1532,8 @@ SaveSlotToPath(ReplicationSlot *slot, const char *dir, int elevel) SpinLockRelease(&slot->mutex); COMP_CRC32C(cp.checksum, - (char *) (&cp) + SnapBuildOnDiskNotChecksummedSize, - SnapBuildOnDiskChecksummedSize); + (char *) (&cp) + ReplicationSlotOnDiskNotChecksummedSize, + ReplicationSlotOnDiskChecksummedSize); FIN_CRC32C(cp.checksum); errno = 0; @@ -1787,8 +1748,8 @@ RestoreSlotFromDisk(const char *name) /* now verify the CRC */ INIT_CRC32C(checksum); COMP_CRC32C(checksum, - (char *) &cp + SnapBuildOnDiskNotChecksummedSize, - SnapBuildOnDiskChecksummedSize); + (char *) &cp + ReplicationSlotOnDiskNotChecksummedSize, + ReplicationSlotOnDiskChecksummedSize); FIN_CRC32C(checksum); if (!EQ_CRC32C(checksum, cp.checksum)) diff --git a/src/bin/Makefile b/src/bin/Makefile index 2fe0ae6652..5e20009beb 100644 --- a/src/bin/Makefile +++ b/src/bin/Makefile @@ -23,6 +23,7 @@ SUBDIRS = \ pg_controldata \ pg_ctl \ pg_dump \ + pg_replslotdata \ pg_resetwal \ pg_rewind \ pg_test_fsync \ diff --git a/src/bin/pg_replslotdata/.gitignore b/src/bin/pg_replslotdata/.gitignore new file mode 100644 index 0000000000..13a4afb8ef --- /dev/null +++ b/src/bin/pg_replslotdata/.gitignore @@ -0,0 +1,2 @@ +/pg_replslotdata +/tmp_check/ diff --git a/src/bin/pg_replslotdata/Makefile b/src/bin/pg_replslotdata/Makefile new file mode 100644 index 0000000000..69518ee53b --- /dev/null +++ b/src/bin/pg_replslotdata/Makefile @@ -0,0 +1,44 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/bin/pg_replslotdata +# +# Copyright (c) 1998-2021, PostgreSQL Global Development Group +# +# src/bin/pg_replslotdata/Makefile +# +#------------------------------------------------------------------------- + +PGFILEDESC = "pg_replslotdata - provides information about the replication slots from $PGDATA/pg_replslot/<slot_name> $PGDATA/pg_replslot/<slot_name>" +PGAPPICON=win32 + +subdir = src/bin/pg_replslotdata +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +OBJS = \ + $(WIN32RES) \ + pg_replslotdata.o + +all: pg_replslotdata + +pg_replslotdata: $(OBJS) | submake-libpgport + $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) + +install: all installdirs + $(INSTALL_PROGRAM) pg_replslotdata$(X) '$(DESTDIR)$(bindir)/pg_replslotdata$(X)' + +installdirs: + $(MKDIR_P) '$(DESTDIR)$(bindir)' + +uninstall: + rm -f '$(DESTDIR)$(bindir)/pg_replslotdata$(X)' + +clean distclean maintainer-clean: + rm -f pg_replslotdata$(X) $(OBJS) + rm -rf tmp_check + +check: + $(prove_check) + +installcheck: + $(prove_installcheck) diff --git a/src/bin/pg_replslotdata/nls.mk b/src/bin/pg_replslotdata/nls.mk new file mode 100644 index 0000000000..74bee593c9 --- /dev/null +++ b/src/bin/pg_replslotdata/nls.mk @@ -0,0 +1,6 @@ +# src/bin/pg_replslotdata/nls.mk +CATALOG_NAME = pg_replslotdata +AVAIL_LANGUAGES = cs de el es fr ja ko pl ru sv tr uk vi zh_CN +GETTEXT_FILES = $(FRONTEND_COMMON_GETTEXT_FILES) pg_replslotdata.c +GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) +GETTEXT_FLAGS = $(FRONTEND_COMMON_GETTEXT_FLAGS) diff --git a/src/bin/pg_replslotdata/pg_replslotdata.c b/src/bin/pg_replslotdata/pg_replslotdata.c new file mode 100644 index 0000000000..f9b4b56b0e --- /dev/null +++ b/src/bin/pg_replslotdata/pg_replslotdata.c @@ -0,0 +1,364 @@ +/*------------------------------------------------------------------------- + * + * pg_replslotdata.c - provides information about the replication slots + * from $PGDATA/pg_replslot/<slot_name>. + * + * Copyright (c) 2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/bin/pg_replslotdata/pg_replslotdata.c + *------------------------------------------------------------------------- + */ +/* + * We have to use postgres.h not postgres_fe.h here, because there's so much + * backend-only stuff in the XLOG include files we need. But we need a + * frontend-ish environment otherwise. Hence this ugly hack. + */ +#define FRONTEND 1 + +#include "postgres.h" + +#include <sys/stat.h> + +#include "access/xlog.h" +#include "access/xlog_internal.h" +#include "common/logging.h" +#include "common/string.h" +#include "getopt_long.h" +#include "pg_getopt.h" +#include "replication/slot_common.h" + +static bool verbose = false; + +static void process_replslots(void); +static void read_and_display_repl_slot(const char *name); + +static void +usage(const char *progname) +{ + printf(_("%s displays information about the replication slots from $PGDATA/pg_replslot/<slot_name>.\n\n"), progname); + printf(_("Usage:\n")); + printf(_(" %s [OPTION] [DATADIR]\n"), progname); + printf(_("\nOptions:\n")); + printf(_(" [-D, --pgdata=]DATADIR data directory\n")); + printf(_(" -V, --version output version information, then exit\n")); + printf(_(" -v, --verbose write a lot of output\n")); + printf(_(" -?, --help show this help, then exit\n")); + printf(_("\nIf no data directory (DATADIR) is specified, " + "the environment variable PGDATA\nis used.\n\n")); + printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT); + printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); +} + +static void +process_replslots(void) +{ + DIR *rsdir; + struct dirent *rsde; + uint32 cnt = 0; + + rsdir = opendir("pg_replslot"); + if (rsdir == NULL) + { + pg_log_error("could not open directory \"%s\": %m", "pg_replslot"); + exit(1); + } + + /* XXX: comment here about the format spefiiers */ + printf("%-64s %9s %10s %11s %10s %12s %21s %21s %21s %21s %10s %20s\n" + "%-64s %9s %10s %11s %10s %12s %21s %21s %21s %21s %10s %20s\n", + "slot_name", "slot_type", "datoid", "persistency", "xmin", "catalog_xmin", "restart_lsn", "invalidated_at", "confirmed_flush", "two_phase_at", "two_phase", "plugin", + "---------", "---------", "------", "-----------", "----", "------------", "-----------", "--------------", "---------------", "------------", "---------", "------"); + + while (errno = 0, (rsde = readdir(rsdir)) != NULL) + { + struct stat statbuf; + char path[MAXPGPATH]; + + if (strcmp(rsde->d_name, ".") == 0 || + strcmp(rsde->d_name, "..") == 0) + continue; + + snprintf(path, sizeof(path), "pg_replslot/%s", rsde->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(rsde->d_name, ".tmp")) + { + pg_log_warning("server was crashed while the slot \"%s\" was being setup or deleted", + rsde->d_name); + continue; + } + + /* looks like a slot in a normal state, restore */ + read_and_display_repl_slot(rsde->d_name); + cnt++; + } + + if (errno) + { + pg_log_error("could not read directory \"%s\": %m", "pg_replslot"); + exit(1); + } + + if (cnt == 0) + { + pg_log_info("no replication slots were found"); + exit(0); + } + + if (closedir(rsdir)) + { + pg_log_error("could not close directory \"%s\": %m", "pg_replslot"); + exit(1); + } +} + +static void +read_and_display_repl_slot(const char *name) +{ + ReplicationSlotOnDisk cp; + char slotdir[MAXPGPATH]; + char path[MAXPGPATH]; + char restart_lsn[NAMEDATALEN]; + char invalidated_at[NAMEDATALEN]; + char confirmed_flush[NAMEDATALEN]; + char two_phase_at[NAMEDATALEN]; + char persistency[NAMEDATALEN]; + int fd; + int readBytes; + pg_crc32c checksum; + + /* delete temp file if it exists */ + sprintf(slotdir, "pg_replslot/%s", name); + sprintf(path, "%s/state.tmp", slotdir); + + fd = open(path, O_RDONLY | PG_BINARY); + + if (fd > 0) + { + pg_log_error("found temporary state file \"%s\": %m", path); + exit(1); + } + + sprintf(path, "%s/state", slotdir); + + if (verbose) + pg_log_info("reading replication slot from \"%s\"", path); + + fd = open(path, O_RDONLY | PG_BINARY); + + /* + * We do not need to handle this as we are rename()ing the directory into + * place only after we fsync()ed the state file. + */ + if (fd < 0) + { + pg_log_error("could not open file \"%s\": %m", path); + exit(1); + } + + if (verbose) + pg_log_info("reading version independent replication slot state file"); + + /* read part of statefile that's guaranteed to be version independent */ + readBytes = read(fd, &cp, ReplicationSlotOnDiskConstantSize); + if (readBytes != ReplicationSlotOnDiskConstantSize) + { + if (readBytes < 0) + { + pg_log_error("could not read file \"%s\": %m", path); + exit(1); + } + else + { + pg_log_error("could not read file \"%s\": read %d of %zu", + path, readBytes, + (Size) ReplicationSlotOnDiskConstantSize); + exit(1); + } + } + + /* verify magic */ + if (cp.magic != SLOT_MAGIC) + { + pg_log_error("replication slot file \"%s\" has wrong magic number: %u instead of %u", + path, cp.magic, SLOT_MAGIC); + exit(1); + } + + /* verify version */ + if (cp.version != SLOT_VERSION) + { + pg_log_error("replication slot file \"%s\" has unsupported version %u", + path, cp.version); + exit(1); + } + + /* boundary check on length */ + if (cp.length != ReplicationSlotOnDiskV2Size) + { + pg_log_error("replication slot file \"%s\" has corrupted length %u", + path, cp.length); + exit(1); + } + + if (verbose) + pg_log_info("reading the entire replication slot state file"); + + /* now that we know the size, read the entire file */ + readBytes = read(fd, + (char *) &cp + ReplicationSlotOnDiskConstantSize, + cp.length); + if (readBytes != cp.length) + { + if (readBytes < 0) + { + pg_log_error("could not read file \"%s\": %m", path); + exit(1); + } + else + { + pg_log_error("could not read file \"%s\": read %d of %zu", + path, readBytes, (Size) cp.length); + exit(1); + } + } + + if (close(fd) != 0) + { + pg_log_error("could not close file \"%s\": %m", path); + exit(1); + } + + /* now verify the CRC */ + INIT_CRC32C(checksum); + COMP_CRC32C(checksum, + (char *) &cp + ReplicationSlotOnDiskNotChecksummedSize, + ReplicationSlotOnDiskChecksummedSize); + FIN_CRC32C(checksum); + + if (!EQ_CRC32C(checksum, cp.checksum)) + { + pg_log_error("checksum mismatch for replication slot file \"%s\": is %u, should be %u", + path, checksum, cp.checksum); + exit(1); + } + + sprintf(restart_lsn, "%X/%X", LSN_FORMAT_ARGS(cp.slotdata.restart_lsn)); + sprintf(invalidated_at, "%X/%X", LSN_FORMAT_ARGS(cp.slotdata.invalidated_at)); + sprintf(confirmed_flush, "%X/%X", LSN_FORMAT_ARGS(cp.slotdata.confirmed_flush)); + sprintf(two_phase_at, "%X/%X", LSN_FORMAT_ARGS(cp.slotdata.two_phase_at)); + + if (cp.slotdata.persistency == RS_PERSISTENT) + sprintf(persistency, "persistent"); + else if (cp.slotdata.persistency == RS_EPHEMERAL) + sprintf(persistency, "ephemeral"); + else if (cp.slotdata.persistency == RS_TEMPORARY) + sprintf(persistency, "temporary"); + + /* display the slot information */ + printf("%-64s %9s %10u %11s %10u %12u %21s %21s %21s %21s %10d %20s\n", + NameStr(cp.slotdata.name), + cp.slotdata.database == InvalidOid ? "physical" : "logical", + cp.slotdata.database, + persistency, + cp.slotdata.xmin, + cp.slotdata.catalog_xmin, + restart_lsn, + invalidated_at, + confirmed_flush, + two_phase_at, + cp.slotdata.two_phase, + NameStr(cp.slotdata.plugin)); +} + +int +main(int argc, char *argv[]) +{ + static struct option long_options[] = { + {"pgdata", required_argument, NULL, 'D'}, + {"verbose", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + char *DataDir = NULL; + const char *progname; + int c; + + pg_logging_init(argv[0]); + set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_replslotdata")); + progname = get_progname(argv[0]); + + if (argc > 1) + { + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) + { + usage(progname); + exit(0); + } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) + { + puts("pg_replslotdata (PostgreSQL) " PG_VERSION); + exit(0); + } + } + + while ((c = getopt_long(argc, argv, "D:", long_options, NULL)) != -1) + { + switch (c) + { + case 'D': + DataDir = optarg; + break; + case 'v': + verbose = true; + break; + default: + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(1); + } + } + + if (DataDir == NULL) + { + if (optind < argc) + DataDir = argv[optind++]; + else + DataDir = getenv("PGDATA"); + } + + /* Complain if any arguments remain */ + if (optind < argc) + { + pg_log_error("too many command-line arguments (first is \"%s\")", + argv[optind]); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), + progname); + exit(1); + } + + if (DataDir == NULL) + { + pg_log_error("no data directory specified"); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(1); + } + + if (verbose) + pg_log_info("data directory: \"%s\"", DataDir); + + if (chdir(DataDir) < 0) + { + pg_log_error("could not change directory to \"%s\": %m", + DataDir); + exit(1); + } + + process_replslots(); + + return 0; +} diff --git a/src/bin/pg_replslotdata/t/001_basic.pl b/src/bin/pg_replslotdata/t/001_basic.pl new file mode 100644 index 0000000000..d6830dc2ac --- /dev/null +++ b/src/bin/pg_replslotdata/t/001_basic.pl @@ -0,0 +1,11 @@ + +# Copyright (c) 2021, PostgreSQL Global Development Group + +use strict; +use warnings; +use PostgreSQL::Test::Utils; +use Test::More tests => 8; + +program_help_ok('pg_replslotdata'); +program_version_ok('pg_replslotdata'); +program_options_handling_ok('pg_replslotdata'); diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h index 53d773ccff..479d504a68 100644 --- a/src/include/replication/slot.h +++ b/src/include/replication/slot.h @@ -15,89 +15,9 @@ #include "storage/lwlock.h" #include "storage/shmem.h" #include "storage/spin.h" +#include "replication/slot_common.h" #include "replication/walreceiver.h" -/* - * Behaviour of replication slots, upon release or crash. - * - * Slots marked as PERSISTENT are crash-safe and will not be dropped when - * released. Slots marked as EPHEMERAL will be dropped when released or after - * restarts. Slots marked TEMPORARY will be dropped at the end of a session - * or on error. - * - * EPHEMERAL is used as a not-quite-ready state when creating persistent - * slots. EPHEMERAL slots can be made PERSISTENT by calling - * ReplicationSlotPersist(). For a slot that goes away at the end of a - * session, TEMPORARY is the appropriate choice. - */ -typedef enum ReplicationSlotPersistency -{ - RS_PERSISTENT, - RS_EPHEMERAL, - RS_TEMPORARY -} ReplicationSlotPersistency; - -/* - * On-Disk data of a replication slot, preserved across restarts. - */ -typedef struct ReplicationSlotPersistentData -{ - /* The slot's identifier */ - NameData name; - - /* database the slot is active on */ - Oid database; - - /* - * The slot's behaviour when being dropped (or restored after a crash). - */ - ReplicationSlotPersistency persistency; - - /* - * xmin horizon for data - * - * NB: This may represent a value that hasn't been written to disk yet; - * see notes for effective_xmin, below. - */ - TransactionId xmin; - - /* - * xmin horizon for catalog tuples - * - * NB: This may represent a value that hasn't been written to disk yet; - * see notes for effective_xmin, below. - */ - TransactionId catalog_xmin; - - /* oldest LSN that might be required by this replication slot */ - XLogRecPtr restart_lsn; - - /* restart_lsn is copied here when the slot is invalidated */ - XLogRecPtr invalidated_at; - - /* - * Oldest LSN that the client has acked receipt for. This is used as the - * start_lsn point in case the client doesn't specify one, and also as a - * safety measure to jump forwards in case the client specifies a - * start_lsn that's further in the past than this value. - */ - XLogRecPtr confirmed_flush; - - /* - * LSN at which we enabled two_phase commit for this slot or LSN at which - * we found a consistent point at the time of slot creation. - */ - XLogRecPtr two_phase_at; - - /* - * Allow decoding of prepared transactions? - */ - bool two_phase; - - /* plugin name */ - NameData plugin; -} ReplicationSlotPersistentData; - /* * Shared memory state of a single replication slot. * diff --git a/src/include/replication/slot_common.h b/src/include/replication/slot_common.h new file mode 100644 index 0000000000..2d221637bb --- /dev/null +++ b/src/include/replication/slot_common.h @@ -0,0 +1,132 @@ +/*------------------------------------------------------------------------- + * slot_common.h + * Replication slot management. + * + * Copyright (c) 2021, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ +#ifndef SLOT_COMMON_H +#define SLOT_COMMON_H + +/* + * Behaviour of replication slots, upon release or crash. + * + * Slots marked as PERSISTENT are crash-safe and will not be dropped when + * released. Slots marked as EPHEMERAL will be dropped when released or after + * restarts. Slots marked TEMPORARY will be dropped at the end of a session + * or on error. + * + * EPHEMERAL is used as a not-quite-ready state when creating persistent + * slots. EPHEMERAL slots can be made PERSISTENT by calling + * ReplicationSlotPersist(). For a slot that goes away at the end of a + * session, TEMPORARY is the appropriate choice. + */ +typedef enum ReplicationSlotPersistency +{ + RS_PERSISTENT, + RS_EPHEMERAL, + RS_TEMPORARY +} ReplicationSlotPersistency; + +/* + * On-Disk data of a replication slot, preserved across restarts. + */ +typedef struct ReplicationSlotPersistentData +{ + /* The slot's identifier */ + NameData name; + + /* database the slot is active on */ + Oid database; + + /* + * The slot's behaviour when being dropped (or restored after a crash). + */ + ReplicationSlotPersistency persistency; + + /* + * xmin horizon for data + * + * NB: This may represent a value that hasn't been written to disk yet; + * see notes for effective_xmin, below. + */ + TransactionId xmin; + + /* + * xmin horizon for catalog tuples + * + * NB: This may represent a value that hasn't been written to disk yet; + * see notes for effective_xmin, below. + */ + TransactionId catalog_xmin; + + /* oldest LSN that might be required by this replication slot */ + XLogRecPtr restart_lsn; + + /* restart_lsn is copied here when the slot is invalidated */ + XLogRecPtr invalidated_at; + + /* + * Oldest LSN that the client has acked receipt for. This is used as the + * start_lsn point in case the client doesn't specify one, and also as a + * safety measure to jump forwards in case the client specifies a + * start_lsn that's further in the past than this value. + */ + XLogRecPtr confirmed_flush; + + /* + * LSN at which we enabled two_phase commit for this slot or LSN at which + * we found a consistent point at the time of slot creation. + */ + XLogRecPtr two_phase_at; + + /* + * Allow decoding of prepared transactions? + */ + bool two_phase; + + /* plugin name */ + NameData plugin; +} ReplicationSlotPersistentData; + +/* + * Replication slot on-disk data structure. + */ +typedef struct ReplicationSlotOnDisk +{ + /* first part of this struct needs to be version independent */ + + /* data not covered by checksum */ + uint32 magic; + pg_crc32c checksum; + + /* data covered by checksum */ + uint32 version; + uint32 length; + + /* + * The actual data in the slot that follows can differ based on the above + * 'version'. + */ + + ReplicationSlotPersistentData slotdata; +} ReplicationSlotOnDisk; + +/* size of version independent data */ +#define ReplicationSlotOnDiskConstantSize \ + offsetof(ReplicationSlotOnDisk, slotdata) +/* size of the part of the slot not covered by the checksum */ +#define ReplicationSlotOnDiskNotChecksummedSize \ + offsetof(ReplicationSlotOnDisk, version) +/* size of the part covered by the checksum */ +#define ReplicationSlotOnDiskChecksummedSize \ + sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskNotChecksummedSize +/* size of the slot data that is version dependent */ +#define ReplicationSlotOnDiskV2Size \ + sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskConstantSize + +#define SLOT_MAGIC 0x1051CA1 /* format identifier */ +#define SLOT_VERSION 2 /* version for new files */ + +#endif /* SLOT_COMMON_H */ -- 2.25.1