ResourceOwner bdr_saved_resowner;
static bool bdr_is_restart = false;
Oid BdrNodesRelid;
+BdrConnectionConfig **bdr_connection_configs;
/* GUC storage */
static char *connections = NULL;
int bdr_max_workers;
static bool bdr_skip_ddl_replication;
-/* TODO: Remove when bdr_apply_main moved into bdr_apply.c */
+/*
+ * These globals are valid only for apply bgworkers, not for
+ * bdr running in the postmaster or for per-db workers.
+ *
+ * TODO: move into bdr_apply.c when bdr_apply_main moved.
+ */
extern BdrApplyWorker *bdr_apply_worker;
+extern BdrConnectionConfig *bdr_apply_config;
/* shmem init hook to chain to on startup, if any */
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
/* shortcut for finding the the worker shmem block */
BdrWorkerControl *BdrWorkerCtl = NULL;
-/*
- * Used only in postmaster to pass data from _PG_init during
- * shared_preload_libraries into the shared memory startup hook.
- */
-typedef struct BdrStartupContext
-{
- /* List of palloc'd BdrApplyWorker instances to copy into shmem */
- List *workers;
-} BdrStartupContext;
-
-static BdrStartupContext *bdr_startup_context;
-
-/*
- * We need somewhere to store config options for each bdr apply worker when
- * we're creating the GUCs for each worker in the postmaster during startup.
- *
- * This isn't directly accessible to workers as we don't keep a pointer to it
- * anywhere, and in EXEC_BACKEND cases it'd be useless because it wouldn't be
- * preserved across fork() anyway. Workers have to use GetConfigOption to
- * access these values.
- */
-typedef struct BdrApplyWorkerConfigOptions
-{
- char *dsn;
- int apply_delay;
- bool bdr_init_replica;
- char *replica_local_dsn;
-} BdrApplyWorkerConfigOptions;
-
PG_MODULE_MAGIC;
void _PG_init(void);
PGC_BACKEND, PGC_S_OVERRIDE); /* other context? */
}
-/*
- * GetConfigOption wrapper that gets an option name qualified by the worker's
- * name.
- *
- * The returned string is *not* modifiable and will only be valid until the
- * next GUC-related call.
- */
-const char *
-bdr_get_worker_option(const char * worker_name, const char * option_name,
- bool missing_ok)
-{
- char *gucname;
- size_t namelen;
- const char *optval;
-
- /* Option names should omit leading underscore */
- Assert(option_name[0] != '_');
-
- namelen = sizeof("bdr.") + strlen(worker_name) + strlen(option_name) + 3;
- gucname = palloc(namelen);
- snprintf(gucname, namelen, "bdr.%s_%s", worker_name, option_name);
-
- optval = GetConfigOption(gucname, missing_ok, false);
-
- pfree(gucname);
-
- return optval;
-}
-
/*
*----------------------
* Connect to the BDR remote end, IDENTIFY_SYSTEM, and CREATE_SLOT if necessary.
*----------------------
*/
PGconn*
-bdr_establish_connection_and_slot(Name connection_name, Name out_slot_name,
+bdr_establish_connection_and_slot(BdrConnectionConfig *cfg, Name out_slot_name,
uint64 *out_sysid, TimeLineID* out_timeline, RepNodeId
*out_replication_identifier, char **out_snapshot)
{
char conninfo_repl[MAXCONNINFO + 75];
- const char *dsn;
char remote_ident[256];
PGconn *streamConn;
- dsn = bdr_get_worker_option(NameStr(*connection_name), "dsn", false);
snprintf(conninfo_repl, sizeof(conninfo_repl),
"%s replication=database fallback_application_name=bdr",
- dsn);
+ cfg->dsn);
/* Establish BDR conn and IDENTIFY_SYSTEM */
streamConn = bdr_connect(
Assert(bdr_worker_slot->worker_type == BDR_WORKER_APPLY);
bdr_apply_worker = &bdr_worker_slot->worker_data.apply_worker;
- bdr_worker_init(NameStr(bdr_apply_worker->dbname));
+ bdr_apply_config = bdr_connection_configs[bdr_apply_worker->connection_config_idx];
+ Assert(bdr_apply_config != NULL);
+
+ bdr_worker_init(NameStr(bdr_apply_config->dbname));
CurrentResourceOwner = ResourceOwnerCreate(NULL, "bdr apply top-level resource owner");
bdr_saved_resowner = CurrentResourceOwner;
elog(LOG, "%s initialized on %s",
- MyBgworkerEntry->bgw_name, NameStr(bdr_apply_worker->dbname));
+ MyBgworkerEntry->bgw_name, NameStr(bdr_apply_config->dbname));
streamConn = bdr_establish_connection_and_slot(
- &bdr_apply_worker->name, &slot_name, &bdr_apply_worker->sysid,
+ bdr_apply_config, &slot_name, &bdr_apply_worker->sysid,
&bdr_apply_worker->timeline, &replication_identifier, NULL);
bdr_apply_worker->origin_id = replication_identifier;
* num_used_databases
* Number of distinct databases named in conns
*
- * out_worker
- * Initialize this BdrApplyWorker with the name and dbname found.
+ * out_config
+ * Assigned a palloc'd pointer to GUC storage for this config'd connection
*
- * TODO At some point we'll have to make the GUC setup dynamic so we can
- * handle workers being added/removed with a config reload SIGHUP.
+ * out_config is set even if false is returned, as the GUCs have still been
+ * created. Test out_config->is_valid to see whether the connection is usable.
*/
static bool
bdr_create_con_gucs(char *name,
char **used_databases,
Size *num_used_databases,
char **database_initcons,
- BdrApplyWorker *out_worker)
+ BdrConnectionConfig **out_config)
{
int off;
char *errmsg = NULL;
PQconninfoOption *options;
PQconninfoOption *cur_option;
- BdrApplyWorkerConfigOptions *opts;
+ BdrConnectionConfig *opts;
/* don't free, referenced by the guc machinery! */
char *optname_dsn = palloc(strlen(name) + 30);
char *optname_delay = palloc(strlen(name) + 30);
char *optname_replica = palloc(strlen(name) + 30);
char *optname_local_dsn = palloc(strlen(name) + 30);
- opts = palloc(sizeof(BdrApplyWorkerConfigOptions));
Assert(process_shared_preload_libraries_in_progress);
- strncpy(NameStr(out_worker->name), name, NAMEDATALEN);
- NameStr(out_worker->name)[NAMEDATALEN-1] = '\0';
+ opts = palloc0(sizeof(BdrConnectionConfig));
+ opts->is_valid = false;
+ *out_config = opts;
+
+ strncpy(NameStr(opts->name), name, NAMEDATALEN);
+ NameStr(opts->name)[NAMEDATALEN-1] = '\0';
sprintf(optname_dsn, "bdr.%s_dsn", name);
DefineCustomStringVariable(optname_dsn,
DefineCustomBoolVariable(optname_replica,
optname_replica,
NULL,
- &opts->bdr_init_replica,
+ &opts->init_replica,
false,
PGC_SIGHUP,
0,
if (!opts->dsn)
{
- elog(WARNING, "no connection information for %s", name);
+ elog(WARNING, "bdr %s: no connection information", name);
return false;
}
- elog(LOG, "bgworkers, connection: %s", opts->dsn);
+ elog(DEBUG2, "bdr %s: dsn=%s", name, opts->dsn);
options = PQconninfoParse(opts->dsn, &errmsg);
if (errmsg != NULL)
if (strcmp(cur_option->keyword, "dbname") == 0)
{
if (cur_option->val == NULL)
- elog(ERROR, "no dbname set");
+ elog(ERROR, "bdr %s: no dbname set", name);
- strncpy(NameStr(out_worker->dbname), cur_option->val,
+ strncpy(NameStr(opts->dbname), cur_option->val,
NAMEDATALEN);
- NameStr(out_worker->dbname)[NAMEDATALEN-1] = '\0';
+ NameStr(opts->dbname)[NAMEDATALEN-1] = '\0';
+ elog(DEBUG2, "bdr %s: dbname=%s", name, NameStr(opts->dbname));
}
if (cur_option->val != NULL)
{
- elog(LOG, "option: %s, val: %s",
- cur_option->keyword, cur_option->val);
+ elog(DEBUG3, "bdr %s: opt %s, val: %s",
+ name, cur_option->keyword, cur_option->val);
}
cur_option++;
}
*/
for (off = 0; off < *num_used_databases; off++)
{
- if (strcmp(NameStr(out_worker->dbname), used_databases[off]) == 0)
+ if (strcmp(NameStr(opts->dbname), used_databases[off]) == 0)
break;
}
{
/* Didn't find a match, add new db name */
used_databases[(*num_used_databases)++] =
- pstrdup(NameStr(out_worker->dbname));
+ pstrdup(NameStr(opts->dbname));
+ elog(DEBUG2, "bdr %s: Saw new database %s, now %i known dbs",
+ name, NameStr(opts->dbname), (int)(*num_used_databases));
}
/*
* Make sure that at most one of the worker configs for each DB can be
* configured to run initialization.
*/
- if (opts->bdr_init_replica)
+ if (opts->init_replica)
{
+ elog(DEBUG2, "bdr %s: has init_replica=t", name);
if (database_initcons[off] != NULL)
elog(ERROR, "Connections %s and %s on database %s both have bdr_init_replica enabled, cannot continue",
name, database_initcons[off], used_databases[off]);
database_initcons[off] = name; /* no need to pstrdup, see _PG_init */
}
- /* optname vars and opts intentionally leaked, see above */
+ opts->is_valid = true;
+
+ /* optname vars intentionally leaked, see above */
return true;
}
case BDR_WORKER_APPLY:
{
BdrApplyWorker *con = &worker->worker_data.apply_worker;
- if ( strcmp(NameStr(con->dbname), dbname) == 0 )
+ BdrConnectionConfig *cfg =
+ bdr_connection_configs[con->connection_config_idx];
+ Assert(cfg != NULL);
+ if ( strcmp(NameStr(cfg->dbname), dbname) == 0 )
{
/* It's an apply worker for our DB; register it */
BackgroundWorkerHandle *bgw_handle;
snprintf(apply_worker.bgw_name, BGW_MAXLEN,
- "bdr apply: %s", NameStr(con->name));
+ "bdr apply: %s", NameStr(cfg->name));
apply_worker.bgw_main_arg = Int32GetDatum(i);
if (!RegisterDynamicBackgroundWorker(&apply_worker,
&bgw_handle))
{
- /* FIXME better error */
- elog(ERROR, "Failed to register background worker");
+ elog(ERROR, "bdr: Failed to register background worker"
+ " %s, see previous log messages",
+ NameStr(cfg->name));
}
- elog(LOG, "Registered worker");
apply_workers = lcons(bgw_handle, apply_workers);
}
}
*
* The shm segment is initialized now, so do that.
*/
-/*
- * TODO: Do the required GUC parsing, etc in bdr_worker_shmem_create_workers
- * instead of in _PG_init.
- */
static void
bdr_worker_shmem_create_workers(void)
{
- ListCell *c;
+ int off;
/*
- * Copy the BdrApplyWorker configs created in _PG_init into shared memory,
- * then free the palloc'd original.
+ * Create a BdrApplyWorker for each valid BdrConnectionConfig found during
+ * _PG_init so that the per-db worker will register it for startup after
+ * performing any BDR initialisation work.
*
- * This is necessary on EXEC_BACKEND (Windows) where postmaster memory
- * isn't accessible by other backends, and is also required when launching
- * one bgworker from another.
+ * Use of shared memory for this is required for EXEC_BACKEND (windows)
+ * where we can't share postmaster memory, and for when we're launching a
+ * bgworker from another bgworker where the fork() from postmaster doesn't
+ * provide access to the launching bgworker's memory.
*/
- foreach(c, bdr_startup_context->workers)
+ for (off = 0; off < bdr_max_workers; off++)
{
- BdrApplyWorker *worker = (BdrApplyWorker *) lfirst(c);
+ BdrConnectionConfig *cfg = bdr_connection_configs[off];
BdrWorker *shmworker;
+ BdrApplyWorker *worker;
+
+ if (cfg == NULL || !cfg->is_valid)
+ continue;
shmworker = (BdrWorker *) bdr_worker_shmem_alloc(BDR_WORKER_APPLY);
Assert(shmworker->worker_type == BDR_WORKER_APPLY);
- memcpy(&shmworker->worker_data, worker, sizeof(BdrApplyWorker));
+ worker = &shmworker->worker_data.apply_worker;
+ worker->connection_config_idx = off;
+ worker->replay_stop_lsn = InvalidXLogRecPtr;
+ worker->forward_changesets = false;
n_configured_bdr_nodes++;
}
char **used_databases;
char **database_initcons;
Size num_used_databases = 0;
+ int connection_config_idx;
if (!process_shared_preload_libraries_in_progress)
elog(ERROR, "bdr can only be loaded via shared_preload_libraries");
*
* This registers a hook on shm initialization, bdr_worker_shmem_startup(),
* which populates the shm segment with configured apply workers using data
- * in bdr_startup_context.
+ * in bdr_connection_configs.
*/
bdr_worker_alloc_shmem_segment();
- /* Prepare storage to pass data into our shared memory startup hook */
- bdr_startup_context = (BdrStartupContext *) palloc0(sizeof(BdrStartupContext));
+ /* Allocate space for BDR connection GUCs */
+ bdr_connection_configs = (BdrConnectionConfig**)
+ palloc0(bdr_max_workers * sizeof(BdrConnectionConfig*));
/* Names of all databases we're going to be doing BDR for */
used_databases = palloc0(sizeof(char *) * list_length(connames));
* parameters and sanity checking as we go. The structs are palloc'd, but
* will be copied into shared memory and free'd during shm init.
*/
+ connection_config_idx = 0;
foreach(c, connames)
{
+ char *name;
BdrApplyWorker *apply_worker;
- char *name;
apply_worker = (BdrApplyWorker *) palloc0(sizeof(BdrApplyWorker));
apply_worker->forward_changesets = false;
name = (char *) lfirst(c);
if (!bdr_create_con_gucs(name, used_databases, &num_used_databases,
- database_initcons, apply_worker))
+ database_initcons,
+ &bdr_connection_configs[connection_config_idx]));
continue;
- apply_worker->origin_id = InvalidRepNodeId;
- bdr_startup_context->workers = lcons(apply_worker, bdr_startup_context->workers);
+
+ Assert(bdr_connection_configs[connection_config_idx] != NULL);
}
/*
*/
typedef struct BdrApplyWorker
{
- /* local & remote database name */
- NameData dbname;
-
- /* connection name specified in configuration */
- NameData name;
+ /*
+ * Index in bdr_connection_configs of this workers's GUCs
+ * and config info (including dbname, name, etc).
+ */
+ int connection_config_idx;
/* TODO: Remove these from shm, into bdr worker global state */
RepNodeId origin_id;
} BdrWorker;
+/* GUC storage for a configured BDR connection. */
+typedef struct BdrConnectionConfig
+{
+ char *dsn;
+ int apply_delay;
+ bool init_replica;
+ char *replica_local_dsn;
+ /*
+ * These aren't technically GUCs, but are per-connection config
+ * information obtained from the GUCs.
+ */
+ NameData name;
+ NameData dbname;
+ /* Connection config might be broken (blank dsn, etc) */
+ bool is_valid;
+} BdrConnectionConfig;
+
+/*
+ * Params for every connection in bdr.connections.
+ *
+ * Contains n=bdr_max_workers elements, may have NULL entries.
+ */
+extern BdrConnectionConfig **bdr_connection_configs;
+
/* GUCs */
extern int bdr_default_apply_delay;
extern int bdr_max_workers;
extern Oid BdrSequenceElectionsRelid;
extern Oid BdrVotesRelid;
-/* Helpers for accessing configuration */
-const char *bdr_get_worker_option(const char * worker_name, const char * option_name, bool missing_ok);
-
/* apply support */
extern void process_remote_begin(StringInfo s);
extern bool process_remote_commit(StringInfo s);
uint64* remote_sysid_i, TimeLineID *remote_tlid_i);
extern struct pg_conn *
-bdr_establish_connection_and_slot(Name connection_name, Name out_slot_name,
+bdr_establish_connection_and_slot(BdrConnectionConfig *cfg, Name out_slot_name,
uint64 *out_sysid, TimeLineID* out_timeline, RepNodeId
*out_replication_identifier, char **out_snapshot);
*/
BdrApplyWorker *bdr_apply_worker = NULL;
+/*
+ * GUCs for this apply worker - again, this is fixed for the lifetime of the
+ * worker so we can stash it in a global.
+ */
+BdrConnectionConfig *bdr_apply_config = NULL;
+
static void build_index_scan_keys(EState *estate, ScanKey *scan_keys, TupleTableSlot *slot);
static void build_index_scan_key(ScanKey skey, Relation rel, Relation idx_rel, TupleTableSlot *slot);
static bool find_pkey_tuple(ScanKey skey, Relation rel, Relation idx_rel, TupleTableSlot *slot,
TimestampTz committime;
TimestampTz current;
char statbuf[100];
- int apply_delay = -1;
- const char *apply_delay_str;
+ int apply_delay = bdr_apply_config->apply_delay;
Assert(bdr_apply_worker != NULL);
snprintf(statbuf, sizeof(statbuf),
"bdr_apply: BEGIN origin(source, orig_lsn, timestamp): %s, %X/%X, %s",
- NameStr(bdr_apply_worker->name),
+ NameStr(bdr_apply_config->name),
(uint32) (origlsn >> 32), (uint32) origlsn,
timestamptz_to_str(committime));
pgstat_report_activity(STATE_RUNNING, statbuf);
- apply_delay_str = bdr_get_worker_option(NameStr(bdr_apply_worker->name), "apply_delay", true);
- if (apply_delay_str)
- /* This is an integer GUC, so parsing as an int can't fail */
- (void) parse_int(apply_delay_str, &apply_delay, 0, NULL);
-
if (apply_delay == -1)
apply_delay = bdr_default_apply_delay;
{
ereport(LOG,
(errmsg("bdr apply %s finished processing; replayed to %X/%X of required %X/%X",
- NameStr(bdr_apply_worker->name),
+ NameStr(bdr_apply_config->name),
(uint32)(end_lsn>>32), (uint32)end_lsn,
(uint32)(bdr_apply_worker->replay_stop_lsn>>32), (uint32)bdr_apply_worker->replay_stop_lsn)));
/*
char *bdr_temp_dump_directory = NULL;
-static void bdr_exec_init_replica(Name conn_name, char *snapshot);
+static void bdr_exec_init_replica(BdrConnectionConfig *cfg, char *snapshot);
-static void bdr_catchup_to_lsn(PGconn *conn, Name dbname, Name conn_name,
+static void bdr_catchup_to_lsn(int cfg_index,
XLogRecPtr target_lsn);
/*
* ensured there can only be one). If no match is found, return null.
*
* Must be called with at least a share lock on BdrWorkerCtl->lock
+ *
*/
static BdrWorker*
find_init_replica_worker(Name dbname)
/* Check whether one of our connections has init_replica set */
for (off = 0; off < bdr_max_workers; off++)
{
- BdrApplyWorker *aw;
+ BdrApplyWorker *aw;
+ BdrConnectionConfig *cfg;
if (BdrWorkerCtl->slots[off].worker_type != BDR_WORKER_APPLY)
continue;
aw = &BdrWorkerCtl->slots[off].worker_data.apply_worker;
+ cfg = bdr_connection_configs[aw->connection_config_idx];
- if (strcmp(NameStr(aw->dbname), NameStr(*dbname)) == 0)
+ if ((strcmp(NameStr(cfg->dbname), NameStr(*dbname)) == 0)
+ && cfg->init_replica)
{
- const char *init_replica_str;
- bool init_replica = false;
- init_replica_str = bdr_get_worker_option(NameStr(aw->name),
- "init_replica", true);
- if (init_replica_str
- && parse_bool(init_replica_str, &init_replica)
- && init_replica)
- return &BdrWorkerCtl->slots[off];
+ return &BdrWorkerCtl->slots[off];
}
}
return NULL;
}
static void
-bdr_drop_slot_and_replication_identifier(Name connection_name, Name dbname)
+bdr_drop_slot_and_replication_identifier(BdrConnectionConfig *cfg)
{
char conninfo_repl[MAXCONNINFO + 75];
- const char *dsn;
char remote_ident[256];
PGconn *streamConn;
RepNodeId replication_identifier;
char *sqlstate;
elog(LOG, "bdr %s: Dropping slot and local ident from connection %s",
- NameStr(*dbname), NameStr(*connection_name));
+ NameStr(cfg->dbname), NameStr(cfg->name));
- dsn = bdr_get_worker_option(NameStr(*connection_name), "dsn", false);
snprintf(conninfo_repl, sizeof(conninfo_repl),
"%s replication=database fallback_application_name=bdr",
- dsn);
+ cfg->dsn);
/* Establish BDR conn and IDENTIFY_SYSTEM */
streamConn = bdr_connect(
{
/* Local replication identifier exists and must be dropped. */
elog(DEBUG2, "bdr %s: Deleting local replication identifier %hu",
- NameStr(*dbname), replication_identifier);
+ NameStr(cfg->dbname), replication_identifier);
bdr_delete_replication_identifier(replication_identifier);
}
else
{
elog(DEBUG2, "bdr %s: No local replication identifier to delete",
- NameStr(*dbname));
+ NameStr(cfg->dbname));
}
/*
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
elog(DEBUG2, "bdr %s: remote replication slot %s deleted",
- NameStr(*dbname), NameStr(slot_name));
+ NameStr(cfg->dbname), NameStr(slot_name));
}
else
{
{
ereport(ERROR,
(errmsg("'DROP_REPLICATION_SLOT %s' on bdr connection %s failed with sqlstate %s: %s",
- NameStr(slot_name), NameStr(*connection_name),
+ NameStr(slot_name), NameStr(cfg->name),
sqlstate,PQresultErrorMessage(res))));
}
else
{
- elog(DEBUG2, "bdr %s: No slot to delete", NameStr(*dbname));
+ elog(DEBUG2, "bdr %s: No slot to delete", NameStr(cfg->dbname));
}
}
CommitTransactionCommand();
* replica from an existing node.
*/
static void
-bdr_exec_init_replica(Name conn_name, char *snapshot)
+bdr_exec_init_replica(BdrConnectionConfig *cfg, char *snapshot)
{
#ifndef WIN32
pid_t pid;
char *bindir;
char *tmpdir;
- char *remote_dsn;
- char *replica_local_dsn;
char bdr_init_replica_script_path[MAXPGPATH];
const char *envvar;
StringInfoData path;
bindir = pstrdup(my_exec_path);
get_parent_directory(bindir);
- replica_local_dsn = pstrdup(bdr_get_worker_option(NameStr(*conn_name), "replica_local_dsn", false));
- remote_dsn = pstrdup(bdr_get_worker_option(NameStr(*conn_name), "dsn", false));
-
if (!find_other_exec(my_exec_path, BDR_INIT_REPLICA_CMD,
BDR_INIT_REPLICA_CMD " " PG_VERSION,
&bdr_init_replica_script_path[0]))
PG_VERSION);
}
- if (!replica_local_dsn)
+ if (cfg->replica_local_dsn == NULL)
elog(FATAL, "bdr init_replica: no replica_local_dsn specified");
tmpdir = palloc(strlen(bdr_temp_dump_directory)+32);
char *const argv[] = {
bdr_init_replica_script_path,
"--snapshot", snapshot,
- "--source", remote_dsn,
- "--target", replica_local_dsn,
+ "--source", cfg->dsn,
+ "--target", cfg->replica_local_dsn,
"--tmp-directory", tmpdir,
NULL
};
envp[0] = path.data;
elog(LOG, "Creating replica with: %s --snapshot %s --source \"%s\" --target \"%s\" --tmp-directory \"%s\"",
- bdr_init_replica_script_path, snapshot, remote_dsn,
- replica_local_dsn, tmpdir);
+ bdr_init_replica_script_path, snapshot, cfg->dsn,
+ cfg->replica_local_dsn, tmpdir);
n = execve(bdr_init_replica_script_path, argv, envp);
if (n < 0)
bdr_init_replica_cleanup_tmpdir(0, CStringGetDatum(tmpdir));
}
- pfree(replica_local_dsn);
- pfree(remote_dsn);
pfree(tmpdir);
#else
/*
void
bdr_init_replica(Name dbname)
{
- const char *connstr;
char status;
XLogRecPtr min_remote_lsn;
PGconn *nonrepl_init_conn;
StringInfoData query;
BdrWorker *init_replica_worker;
- Name init_conn_name;
+ BdrConnectionConfig *init_replica_config;
initStringInfo(&query);
return;
}
- init_conn_name = &init_replica_worker->worker_data.apply_worker.name;
+ init_replica_config = bdr_connection_configs
+ [init_replica_worker->worker_data.apply_worker.connection_config_idx];
elog(DEBUG2, "bdr %s: bdr_init_replica init from connection %s",
- NameStr(*dbname), NameStr(*init_conn_name));
+ NameStr(*dbname), NameStr(init_replica_config->name));
/*
* Check the local bdr.bdr_nodes over SPI or direct scan to see if
* system identifier. If there is, that'll tell us what stage of startup
* we are up to and let us resume an incomplete start.
*/
- connstr = bdr_get_worker_option(NameStr(*init_conn_name), "dsn", false);
- nonrepl_init_conn = PQconnectdb(connstr);
+ nonrepl_init_conn = PQconnectdb(init_replica_config->dsn);
if (PQstatus(nonrepl_init_conn) != CONNECTION_OK)
{
ereport(FATAL,
*/
elog(DEBUG2, "bdr %s: previous failed initalization detected, cleaning up",
NameStr(*dbname));
- bdr_drop_slot_and_replication_identifier(init_conn_name, dbname);
+ bdr_drop_slot_and_replication_identifier(init_replica_config);
status = bdr_set_remote_status(nonrepl_init_conn, dbname,
'\0', status);
break;
LWLockAcquire(BdrWorkerCtl->lock, LW_SHARED);
for (off = 0; off < bdr_max_workers; off++)
{
- BdrWorker *worker = &BdrWorkerCtl->slots[off];
- const char *worker_name =
- NameStr(worker->worker_data.apply_worker.dbname);
+ BdrWorker *worker = &BdrWorkerCtl->slots[off];
+ BdrConnectionConfig *cfg;
+
+ cfg = bdr_connection_configs
+ [worker->worker_data.apply_worker.connection_config_idx];
if (worker->worker_type == BDR_WORKER_APPLY
- && strcmp(worker_name, NameStr(*dbname)) == 0)
+ && strcmp(NameStr(cfg->dbname), NameStr(*dbname)) == 0)
my_conn_idxs[n_conns++] = off;
}
LWLockRelease(BdrWorkerCtl->lock);
for (off = 0; off < n_conns; off++)
{
BdrWorker *w = &BdrWorkerCtl->slots[my_conn_idxs[off]];
+ BdrConnectionConfig *cfg;
char *snapshot = NULL;
PGconn *conn = NULL;
RepNodeId replication_identifier;
uint64 sysid;
TimeLineID timeline;
+ cfg = bdr_connection_configs
+ [w->worker_data.apply_worker.connection_config_idx];
+
elog(DEBUG1, "bdr %s: checking/creating slot for %s",
- NameStr(*dbname), NameStr(w->worker_data.apply_worker.name));
+ NameStr(*dbname), NameStr(cfg->name));
/*
* Create the slot on the remote. The returned remote sysid and
* timeline, the slot name, and the local replication identifier
* are all discarded; they're not needed here, and will be obtained
* again by the apply workers when they're launched after init.
*/
- conn = bdr_establish_connection_and_slot(
- &w->worker_data.apply_worker.name, &slot_name,
- &sysid, &timeline, &replication_identifier, &snapshot);
+ conn = bdr_establish_connection_and_slot(cfg, &slot_name, &sysid,
+ &timeline, &replication_identifier, &snapshot);
/* Always throws rather than returning failure */
Assert(conn);
* copied over.
*/
elog(LOG, "bdr %s: creating and restoring dump for %s",
- NameStr(*dbname), NameStr(*init_conn_name));
- bdr_exec_init_replica(init_conn_name, init_snapshot);
+ NameStr(*dbname), NameStr(init_replica_config->name));
+ bdr_exec_init_replica(init_replica_config, init_snapshot);
PQfinish(init_repl_conn);
pfree(init_snapshot);
/* Launch the catchup worker and wait for it to finish */
elog(LOG, "bdr %s: launching catchup mode apply worker", NameStr(*dbname));
min_remote_lsn = bdr_get_remote_lsn(nonrepl_init_conn);
- bdr_catchup_to_lsn(nonrepl_init_conn, dbname, init_conn_name, min_remote_lsn);
+ bdr_catchup_to_lsn(
+ init_replica_worker->worker_data.apply_worker.connection_config_idx,
+ min_remote_lsn);
status = bdr_set_remote_status(nonrepl_init_conn, dbname, 'r', status);
elog(LOG, "bdr %s: catchup worker finished, ready for normal replication",
* When we finish applying and the worker exits, we'll be caught up with the
* remote and in a consistent state where all our local replication identifiers
* are consistent with the actual state of the local DB.
+ *
+ * Arguments:
+ *
+ * cfg_index: Index of the bdr connection for this dbname with init_worker=t
+ * set within bdr_connection_configs. Used to start the worker.
+ *
+ * target_lsn: LSN of immediate origin node at which catchup should stop.
*/
static void
-bdr_catchup_to_lsn(PGconn *conn, Name dbname, Name conn_name, XLogRecPtr target_lsn)
+bdr_catchup_to_lsn(int cfg_index,
+ XLogRecPtr target_lsn)
{
int worker_shmem_idx;
pid_t bgw_pid;
BackgroundWorker bgw;
BackgroundWorkerHandle *bgw_handle;
BgwHandleStatus bgw_status;
+ BdrConnectionConfig *cfg;
+
+ cfg = bdr_connection_configs[cfg_index];
+ Assert(cfg != NULL);
+ Assert(cfg->init_replica);
elog(DEBUG1, "Registering bdr apply catchup worker %s for db %s to lsn %X/%X",
- NameStr(*conn_name), NameStr(*dbname),
+ NameStr(cfg->name), NameStr(cfg->dbname),
(uint32)(target_lsn>>32), (uint32)target_lsn);
- /* Create the shm entry for the catchup worker */
+ /* Create the shmem entry for the catchup worker */
LWLockAcquire(BdrWorkerCtl->lock, LW_SHARED);
for (worker_shmem_idx = 0; worker_shmem_idx < bdr_max_workers; worker_shmem_idx++)
{
LWLockRelease(BdrWorkerCtl->lock);
/*
- * Make sure we free the shmem slot for the catchup worker even if we
- * hit an error.
+ * Launch the catchup worker, ensuring that we free the shmem slot for the
+ * catchup worker even if we hit an error.
*
* There's a small race between claiming the worker and entering the ensure
- * cleanup block. Real consequences, pretty much nil, since this is really
- * just startup code.
+ * cleanup block. The real consequences are pretty much nil, since this is
+ * really just startup code and all we leak is one shmem slot.
*/
PG_ENSURE_ERROR_CLEANUP(bdr_catchup_to_lsn_cleanup,
Int32GetDatum(worker_shmem_idx));
{
pid_t prev_bgw_pid = 0;
+ /* Make sure the catchup worker can find its bdr.xxx_ GUCs */
+ catchup_worker->connection_config_idx = cfg_index;
+
/* Set up the BdrApplyWorker struct in shmem */
- strncpy(NameStr(catchup_worker->name), NameStr(*conn_name), NAMEDATALEN);
- strncpy(NameStr(catchup_worker->dbname), NameStr(*dbname), NAMEDATALEN);
catchup_worker->origin_id = InvalidRepNodeId;
catchup_worker->sysid = 0;
catchup_worker->timeline = 0;
snprintf(bgw.bgw_name, BGW_MAXLEN,
"bdr %s: catchup apply to %X/%X on %s",
- NameStr(*dbname),
+ NameStr(cfg->dbname),
(uint32)(target_lsn >> 32), (uint32)target_lsn,
- NameStr(*conn_name));
+ NameStr(cfg->name));
bgw.bgw_name[BGW_MAXLEN-1] = '\0';
/* Launch the catchup worker and wait for it to start */
/* Worker must've died before it finished */
elog(ERROR,
"bdr %s: catchup worker exited before catching up to target LSN %X/%X",
- NameStr(*dbname),
+ NameStr(cfg->dbname),
(uint32)(target_lsn>>32), (uint32)target_lsn);
}
else
{
elog(DEBUG1, "bdr %s: catchup worker caught up to target LSN",
- NameStr(*dbname));
+ NameStr(cfg->dbname));
}
}
PG_END_ENSURE_ERROR_CLEANUP(bdr_catchup_to_lsn_cleanup,