/* * Deactivate this module. * * This must be called when the track_commit_timestamp parameter is turned off. * This happens during postmaster or standalone-backend startup, or during WAL * replay. * * Resets CommitTs into invalid state to make sure we don't hand back * possibly-invalid data; also removes segments of old data. */ static void DeactivateCommitTs(void) { /* * Cleanup the status in the shared memory. * * We reset everything in the commitTsShared record to prevent user from * getting confusing data about last committed transaction on the standby * when the module was activated repeatedly on the primary. */ LWLockAcquire(CommitTsLock, LW_EXCLUSIVE); commitTsShared->commitTsActive = false; commitTsShared->xidLastCommit = InvalidTransactionId; TIMESTAMP_NOBEGIN(commitTsShared->dataLastCommit.time); commitTsShared->dataLastCommit.nodeid = InvalidRepOriginId; ShmemVariableCache->oldestCommitTsXid = InvalidTransactionId; ShmemVariableCache->newestCommitTsXid = InvalidTransactionId; LWLockRelease(CommitTsLock); /* * Remove *all* files. This is necessary so that there are no leftover * files; in the case where this feature is later enabled after running * with it disabled for some time there may be a gap in the file sequence. * (We can probably tolerate out-of-sequence files, as they are going to * be overwritten anyway when we wrap around, but it seems better to be * tidy.) */ LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE); (void) SlruScanDirectory(CommitTsCtl, SlruScanDirCbDeleteAll, NULL); LWLockRelease(CommitTsControlLock); }
/* * Initialize CommitTs at system startup (postmaster start or standalone * backend) */ void CommitTsShmemInit(void) { bool found; CommitTsCtl->PagePrecedes = CommitTsPagePrecedes; SimpleLruInit(CommitTsCtl, "commit_timestamp", CommitTsShmemBuffers(), 0, CommitTsControlLock, "pg_commit_ts", LWTRANCHE_COMMITTS_BUFFERS); commitTsShared = ShmemInitStruct("CommitTs shared", sizeof(CommitTimestampShared), &found); if (!IsUnderPostmaster) { Assert(!found); commitTsShared->xidLastCommit = InvalidTransactionId; TIMESTAMP_NOBEGIN(commitTsShared->dataLastCommit.time); commitTsShared->dataLastCommit.nodeid = InvalidRepOriginId; commitTsShared->commitTsActive = false; } else Assert(found); }
/* abstime_timestamptz() * Convert abstime to timestamp with time zone. */ Datum abstime_timestamptz(PG_FUNCTION_ARGS) { AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0); TimestampTz result; struct pg_tm tt, *tm = &tt; int tz; char zone[MAXDATELEN + 1], *tzn = zone; switch (abstime) { case INVALID_ABSTIME: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert abstime \"invalid\" to timestamp"), errOmitLocation(true))); TIMESTAMP_NOBEGIN(result); break; case NOSTART_ABSTIME: TIMESTAMP_NOBEGIN(result); break; case NOEND_ABSTIME: TIMESTAMP_NOEND(result); break; default: abstime2tm(abstime, &tz, tm, &tzn); if (tm2timestamp(tm, 0, &tz, &result) != 0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"), errOmitLocation(true))); break; }; PG_RETURN_TIMESTAMP(result); }
timestamp PGTYPEStimestamp_from_asc(char *str, char **endptr) { timestamp result; #ifdef HAVE_INT64_TIMESTAMP int64 noresult = 0; #else double noresult = 0.0; #endif fsec_t fsec; struct tm tt, *tm = &tt; int dtype; int nf; char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; char lowstr[MAXDATELEN + MAXDATEFIELDS]; char *realptr; char **ptr = (endptr != NULL) ? endptr : &realptr; if (strlen(str) >= sizeof(lowstr)) { errno = PGTYPES_TS_BAD_TIMESTAMP; return (noresult); } if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 || DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, 0) != 0) { errno = PGTYPES_TS_BAD_TIMESTAMP; return (noresult); } switch (dtype) { case DTK_DATE: if (tm2timestamp(tm, fsec, NULL, &result) != 0) { errno = PGTYPES_TS_BAD_TIMESTAMP; return (noresult); } break; case DTK_EPOCH: result = SetEpochTimestamp(); break; case DTK_LATE: TIMESTAMP_NOEND(result); break; case DTK_EARLY: TIMESTAMP_NOBEGIN(result); break; case DTK_INVALID: errno = PGTYPES_TS_BAD_TIMESTAMP; return (noresult); default: errno = PGTYPES_TS_BAD_TIMESTAMP; return (noresult); } /* AdjustTimestampForTypmod(&result, typmod); */ /* * Since it's difficult to test for noresult, make sure errno is 0 if no * error occured. */ errno = 0; return result; }
/* * Start new apply background worker, if possible. */ void logicalrep_worker_launch(Oid dbid, Oid subid, const char *subname, Oid userid, Oid relid) { BackgroundWorker bgw; BackgroundWorkerHandle *bgw_handle; uint16 generation; int i; int slot = 0; LogicalRepWorker *worker = NULL; int nsyncworkers; TimestampTz now; ereport(DEBUG1, (errmsg("starting logical replication worker for subscription \"%s\"", subname))); /* Report this after the initial starting message for consistency. */ if (max_replication_slots == 0) ereport(ERROR, (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("cannot start logical replication workers when max_replication_slots = 0"))); /* * We need to do the modification of the shared memory under lock so that * we have consistent view. */ LWLockAcquire(LogicalRepWorkerLock, LW_EXCLUSIVE); retry: /* Find unused worker slot. */ for (i = 0; i < max_logical_replication_workers; i++) { LogicalRepWorker *w = &LogicalRepCtx->workers[i]; if (!w->in_use) { worker = w; slot = i; break; } } nsyncworkers = logicalrep_sync_worker_count(subid); now = GetCurrentTimestamp(); /* * If we didn't find a free slot, try to do garbage collection. The * reason we do this is because if some worker failed to start up and its * parent has crashed while waiting, the in_use state was never cleared. */ if (worker == NULL || nsyncworkers >= max_sync_workers_per_subscription) { bool did_cleanup = false; for (i = 0; i < max_logical_replication_workers; i++) { LogicalRepWorker *w = &LogicalRepCtx->workers[i]; /* * If the worker was marked in use but didn't manage to attach in * time, clean it up. */ if (w->in_use && !w->proc && TimestampDifferenceExceeds(w->launch_time, now, wal_receiver_timeout)) { elog(WARNING, "logical replication worker for subscription %u took too long to start; canceled", w->subid); logicalrep_worker_cleanup(w); did_cleanup = true; } } if (did_cleanup) goto retry; } /* * If we reached the sync worker limit per subscription, just exit * silently as we might get here because of an otherwise harmless race * condition. */ if (nsyncworkers >= max_sync_workers_per_subscription) { LWLockRelease(LogicalRepWorkerLock); return; } /* * However if there are no more free worker slots, inform user about it * before exiting. */ if (worker == NULL) { LWLockRelease(LogicalRepWorkerLock); ereport(WARNING, (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("out of logical replication worker slots"), errhint("You might need to increase max_logical_replication_workers."))); return; } /* Prepare the worker slot. */ worker->launch_time = now; worker->in_use = true; worker->generation++; worker->proc = NULL; worker->dbid = dbid; worker->userid = userid; worker->subid = subid; worker->relid = relid; worker->relstate = SUBREL_STATE_UNKNOWN; worker->relstate_lsn = InvalidXLogRecPtr; worker->last_lsn = InvalidXLogRecPtr; TIMESTAMP_NOBEGIN(worker->last_send_time); TIMESTAMP_NOBEGIN(worker->last_recv_time); worker->reply_lsn = InvalidXLogRecPtr; TIMESTAMP_NOBEGIN(worker->reply_time); /* Before releasing lock, remember generation for future identification. */ generation = worker->generation; LWLockRelease(LogicalRepWorkerLock); /* Register the new dynamic worker. */ memset(&bgw, 0, sizeof(bgw)); bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; bgw.bgw_start_time = BgWorkerStart_RecoveryFinished; snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres"); snprintf(bgw.bgw_function_name, BGW_MAXLEN, "ApplyWorkerMain"); if (OidIsValid(relid)) snprintf(bgw.bgw_name, BGW_MAXLEN, "logical replication worker for subscription %u sync %u", subid, relid); else snprintf(bgw.bgw_name, BGW_MAXLEN, "logical replication worker for subscription %u", subid); snprintf(bgw.bgw_type, BGW_MAXLEN, "logical replication worker"); bgw.bgw_restart_time = BGW_NEVER_RESTART; bgw.bgw_notify_pid = MyProcPid; bgw.bgw_main_arg = Int32GetDatum(slot); if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle)) { /* Failed to start worker, so clean up the worker slot. */ LWLockAcquire(LogicalRepWorkerLock, LW_EXCLUSIVE); Assert(generation == worker->generation); logicalrep_worker_cleanup(worker); LWLockRelease(LogicalRepWorkerLock); ereport(WARNING, (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("out of background worker slots"), errhint("You might need to increase max_worker_processes."))); return; } /* Now wait until it attaches. */ WaitForReplicationWorkerAttach(worker, generation, bgw_handle); }