From 2b1ec37a0f7671726ad478974d5133db2810c5a3 Mon Sep 17 00:00:00 2001 From: flyer Date: Sun, 7 Apr 2024 22:16:22 +0800 Subject: [PATCH 1/4] Using the last replayed LSN as the value of LSN location if the last received LSN is at the starting point of the WAL segment --- script/pgsqlms | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/script/pgsqlms b/script/pgsqlms index 6c3ad3d..e13237c 100755 --- a/script/pgsqlms +++ b/script/pgsqlms @@ -207,6 +207,41 @@ sub _get_last_received_lsn { return undef; } +# Get the last write-ahead log location that has been replayed during recovery on a standby +# if the first argument is true, returns the value as decimal +# if the first argument is false, returns the value as LSN +# Returns undef if query failed +sub _get_last_replay_lsn { + my ( $dec ) = @_; + my $pg_last_wal_replay_lsn = 'pg_last_wal_replay_lsn()'; + my $pg_wal_lsn_diff = 'pg_wal_lsn_diff'; + my $query; + my $rc; + my @rs; + + if ( $PGVERNUM < $PGVER_10 ) { + $pg_last_wal_receive_lsn = 'pg_last_xlog_replay_location()'; + $pg_wal_lsn_diff = 'pg_xlog_location_diff'; + } + + if ( $dec ) { + $query = "SELECT $pg_wal_lsn_diff( $pg_last_wal_replay_lsn, '0/0' )"; + } + else { + $query = "SELECT $pg_last_wal_replay_lsn"; + } + + $rc = _query( $query, \@rs ); + + return $rs[0][0] if $rc == 0 and $rs[0][0]; + + ocf_log( 'err', 'Could not query last replayed LSN (%s)', $rc ) if $rc != 0; + ocf_log( 'err', 'No values for last replayed LSN' ) + if $rc == 0 and not $rs[0][0]; + + return undef; +} + # Get the master score for each connected standby # Returns directly the result set of the query or exit with an error. # Exits with OCF_ERR_GENERIC if the query failed @@ -2060,6 +2095,24 @@ sub pgsql_notify_pre_promote { _query( q{ CHECKPOINT }, {} ); %cdata = _get_controldata(); $node_lsn = _get_last_received_lsn( 'in decimal' ); + # Checking if the current LSN are accurate + # In most cases, the pg_last_wal_receive_lsn() can accurately retrieve the last received LSN location + # When Postgres is started as a standby, the value obtained by this function is the starting point of the last + # WAL segment in the local pg_wal folder. + # After executing stream replication from primary, this value will be correctly updated. + my $WAL_SEGMENT_START = '000000'; + # Convert LSN to hexadecimal + my $node_lsn_hexadecimal = sprintf("%X", $node_lsn); + # If the last three bytes (or six hexadecimal digits) of the LSN are zeros, + # It means that the LSN is the starting point of the last WAL segment in the local pg_wal folder. + if ($node_lsn_hexadecimal =~ /$WAL_SEGMENT_START$/) { + ocf_log( 'info', 'the LSN "%s" is not accurate', $node_lsn_hexadecimal); + $node_last_replayed_lsn = _get_last_replay_lsn( 'in decimal' ); + if ($node_last_replayed_lsn > $node_lsn) { + ocf_log( 'info', 'Using the last replayed LSN "%s" as the value for LSN location.', $node_last_replayed_lsn); + $node_lsn = $node_last_replayed_lsn; + } + } unless ( defined $node_lsn ) { ocf_log( 'warning', 'Unknown current node LSN' ); From 686a375943ef897acc915a60f8831663c0905a86 Mon Sep 17 00:00:00 2001 From: Gavin Date: Mon, 8 Apr 2024 16:14:50 +0800 Subject: [PATCH 2/4] Update pgsqlms --- script/pgsqlms | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/pgsqlms b/script/pgsqlms index e13237c..962bc01 100755 --- a/script/pgsqlms +++ b/script/pgsqlms @@ -2107,7 +2107,7 @@ sub pgsql_notify_pre_promote { # It means that the LSN is the starting point of the last WAL segment in the local pg_wal folder. if ($node_lsn_hexadecimal =~ /$WAL_SEGMENT_START$/) { ocf_log( 'info', 'the LSN "%s" is not accurate', $node_lsn_hexadecimal); - $node_last_replayed_lsn = _get_last_replay_lsn( 'in decimal' ); + my $node_last_replayed_lsn = _get_last_replay_lsn( 'in decimal' ); if ($node_last_replayed_lsn > $node_lsn) { ocf_log( 'info', 'Using the last replayed LSN "%s" as the value for LSN location.', $node_last_replayed_lsn); $node_lsn = $node_last_replayed_lsn; From d4d3f57b1f1da4f98649b97765e8ece9f7c68d31 Mon Sep 17 00:00:00 2001 From: Gavin Date: Sun, 28 Sep 2025 12:18:31 +0800 Subject: [PATCH 3/4] Update README.md --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index b7cbaed..9fa5d83 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,27 @@ can set: For a demonstration about how to setup a cluster, see [http://clusterlabs.github.io/PAF/documentation.html](http://clusterlabs.github.io/PAF/documentation.html). +SELECT + pid, + status, + receive_start_lsn, + flushed_lsn, + latest_end_lsn, + pg_wal_lsn_diff(latest_end_lsn, flushed_lsn) AS byte_lag, + last_msg_send_time, + last_msg_receipt_time, + now() - last_msg_receipt_time AS last_msg_delay, + pg_last_wal_replay_lsn() AS last_replay_lsn, + pg_last_xact_replay_timestamp() AS last_replay_ts, + now() - pg_last_xact_replay_timestamp() AS replay_delay, + CASE + WHEN status = 'streaming' + AND pg_last_wal_replay_lsn() IS NOT NULL + AND pg_last_xact_replay_timestamp() IS NOT NULL + AND (now() - last_msg_receipt_time) < interval '30 seconds' + AND pg_wal_lsn_diff(latest_end_lsn, flushed_lsn) < 16*1024*1024 -- 16 MB threshold + AND (now() - pg_last_xact_replay_timestamp()) < interval '30 seconds' + THEN 'HEALTHY' + ELSE 'UNHEALTHY' + END AS standby_health +FROM pg_stat_wal_receiver; From 67aef99670150a3e23516393220301f959b3542c Mon Sep 17 00:00:00 2001 From: Gavin Date: Sun, 28 Sep 2025 12:23:44 +0800 Subject: [PATCH 4/4] Update README.md --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 9fa5d83..617ed34 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,30 @@ SELECT ELSE 'UNHEALTHY' END AS standby_health FROM pg_stat_wal_receiver; + +``` +SELECT + pid, + status, + receive_start_lsn, + flushed_lsn, + latest_end_lsn, + pg_wal_lsn_diff(latest_end_lsn, flushed_lsn) AS byte_lag, + last_msg_send_time, + last_msg_receipt_time, + now() - last_msg_receipt_time AS last_msg_delay, + pg_last_wal_replay_lsn() AS last_replay_lsn, + pg_last_xact_replay_timestamp() AS last_replay_ts, + now() - pg_last_xact_replay_timestamp() AS replay_delay, + CASE + WHEN status = 'streaming' + AND pg_last_wal_replay_lsn() IS NOT NULL + AND pg_last_xact_replay_timestamp() IS NOT NULL + AND (now() - last_msg_receipt_time) < interval '30 seconds' + AND pg_wal_lsn_diff(latest_end_lsn, flushed_lsn) < 16*1024*1024 -- 16 MB threshold + AND (now() - pg_last_xact_replay_timestamp()) < interval '30 seconds' + THEN 'HEALTHY' + ELSE 'UNHEALTHY' + END AS standby_health +FROM pg_stat_wal_receiver; +```