From e4e45df82f0bf396b7322e8e764384c57f87e46b Mon Sep 17 00:00:00 2001 From: Robins Tharakan Date: Thu, 6 Mar 2025 21:32:59 +1030 Subject: [PATCH] Add support for pg_postmaster_open_time() It is not always reliable to depend on pg_postmaster_start_time() for database uptime calculations, owing to how postmaster catches child process crashes, startup recovery etc. and continues without a restart. This could lead to multiple seconds (minutes or even hours) of difference when although postmaster was up, the database was not accepting connections. This function returns the start time when the database was ready to accept new database connections, allowing better calculation of database availability. On a standby, this time reflects the time the database was ready to accept read-only connections, whereas in single-user mode this time reflects the time the database was ready to accept user commands. --- doc/src/sgml/func.sgml | 16 ++++++++++++++++ src/backend/postmaster/launch_backend.c | 3 +++ src/backend/postmaster/postmaster.c | 10 ++++++++++ src/backend/tcop/postgres.c | 5 +++++ src/backend/utils/adt/timestamp.c | 9 +++++++++ src/include/catalog/pg_proc.dat | 6 ++++++ src/include/utils/timestamp.h | 3 +++ 7 files changed, 52 insertions(+) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 9ab070adffba..ccc5b9d7a255 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -24916,6 +24916,22 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n); + + + + pg_postmaster_open_time + + pg_postmaster_open_time () + timestamp with time zone + + + Returns the time when the server was open to connections. On a standby + this will be the time when the server was open to read-only connections. + In single-user mode this returns the time the server was ready to accept + user commands. + + + diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index bf6b55ee8304..bad35abfe6e8 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -111,6 +111,7 @@ typedef struct ProcSignalHeader *ProcSignal; pid_t PostmasterPid; TimestampTz PgStartTime; + TimestampTz PgOpenStartTime; TimestampTz PgReloadTime; pg_time_t first_syslogger_file_time; bool redirection_done; @@ -771,6 +772,7 @@ save_backend_variables(BackendParameters *param, param->PostmasterPid = PostmasterPid; param->PgStartTime = PgStartTime; + param->PgOpenStartTime = PgOpenStartTime; param->PgReloadTime = PgReloadTime; param->first_syslogger_file_time = first_syslogger_file_time; @@ -1031,6 +1033,7 @@ restore_backend_variables(BackendParameters *param) PostmasterPid = param->PostmasterPid; PgStartTime = param->PgStartTime; + PgOpenStartTime = param->PgOpenStartTime; PgReloadTime = param->PgReloadTime; first_syslogger_file_time = param->first_syslogger_file_time; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 17fed96fe20c..7a820997617a 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -2342,6 +2342,11 @@ process_pm_child_exit(void) */ StartWorkerNeeded = true; + /* + * Remember time when database was open to connections + */ + PgOpenStartTime = GetCurrentTimestamp(); + /* at this point we are really open for business */ ereport(LOG, (errmsg("database system is ready to accept connections"))); @@ -3737,6 +3742,11 @@ process_pm_pmsignal(void) ereport(LOG, (errmsg("database system is ready to accept read-only connections"))); + /* + * Remember time when database was open to connections + */ + PgOpenStartTime = GetCurrentTimestamp(); + /* Report status */ AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_READY); #ifdef USE_SYSTEMD diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 6ae9f38f0c84..ee9bfdd604cc 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -4157,6 +4157,11 @@ PostgresSingleUserMain(int argc, char *argv[], */ PgStartTime = GetCurrentTimestamp(); + /* + * Remember when stand-alone was open to user commands. + */ + PgOpenStartTime = GetCurrentTimestamp(); + /* * Create a per-backend PGPROC struct in shared memory. We must do this * before we can use LWLocks. diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 347089b76264..0badfd215c97 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -53,6 +53,9 @@ /* Set at postmaster start */ TimestampTz PgStartTime; +/* Set when database is open to connections */ +TimestampTz PgOpenStartTime; + /* Set at configuration reload */ TimestampTz PgReloadTime; @@ -1629,6 +1632,12 @@ pg_postmaster_start_time(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMPTZ(PgStartTime); } +Datum +pg_postmaster_open_time(PG_FUNCTION_ARGS) +{ + PG_RETURN_TIMESTAMPTZ(PgOpenStartTime); +} + Datum pg_conf_load_time(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 37a484147a8f..b5155150a3a4 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8741,6 +8741,12 @@ prorettype => 'timestamptz', proargtypes => '', prosrc => 'pg_postmaster_start_time' }, +# open to connections start time function +{ oid => '8600', descr => 'open connections start time', + proname => 'pg_postmaster_open_time', provolatile => 's', + prorettype => 'timestamptz', proargtypes => '', + prosrc => 'pg_postmaster_open_time' }, + # config reload time function { oid => '2034', descr => 'configuration load time', proname => 'pg_conf_load_time', provolatile => 's', proparallel => 'r', diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 8c205859c3be..7960d15f37fe 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -98,6 +98,9 @@ TimestampDifferenceMicroseconds(TimestampTz start_time, /* Set at postmaster start */ extern PGDLLIMPORT TimestampTz PgStartTime; +/* Set when database is open to connections */ +extern PGDLLIMPORT TimestampTz PgOpenStartTime; + /* Set at configuration reload */ extern PGDLLIMPORT TimestampTz PgReloadTime;