From: Jehan-Guillaume de Rorthais <jgdr@dalibo.com>
To: Michael Paquier <michael@paquier.xyz>
Cc: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Cc: masao.fujii@oss.nttdata.com
Cc: pgsql-bugs@lists.postgresql.org
Subject: Re: [BUG] non archived WAL removed during production crash recovery
Date: Tue, 14 Apr 2020 18:03:19 +0200
Message-ID: <20200414180319.731f9593@firost> (raw)
In-Reply-To: <20200413071414.GF2169@paquier.xyz>
References: <20200408022345.GC1606@paquier.xyz>
<20200408135830.25a8e2ef@firost>
<20200409184622.390f9b7f@firost>
<20200410.111454.269125561493324950.horikyota.ntt@gmail.com>
<20200413071414.GF2169@paquier.xyz>
On Mon, 13 Apr 2020 16:14:14 +0900
Michael Paquier <michael@paquier.xyz> wrote:
[...]
> XLogCtl->XLogCacheBlck = XLOGbuffers - 1;
> - XLogCtl->SharedRecoveryInProgress = true;
> XLogCtl->SharedHotStandbyActive = false;
> XLogCtl->SharedPromoteIsTriggered = false;
> XLogCtl->WalWriterSleeping = false;
> [...]
> + switch (ControlFile->state)
> + {
> + case DB_SHUTDOWNED:
> + case DB_SHUTDOWNED_IN_RECOVERY:
> + XLogCtl->SharedRecoveryState = ARCHIVE_RECOVERING;
> + break;
> + default:
> + XLogCtl->SharedRecoveryState = CRASH_RECOVERING;
> + }
> It seems to me that the initial value of SharedRecoveryState should be
> CRASH_RECOVERING all the time no? StartupXLOG() is a code path taken
> even if starting cleanly, and the flag would be reset correctly at the
> end to NOT_IN_RECOVERY.
Previous version of the patch was setting CRASH_RECOVERING. Fujii-san reported
(the 4 Apr 2020 02:49:50 +0900) that it could be useful to expose a better value
until relevant code is reached in StartupXLOG() so GetRecoveryState() returns
a safer value for futur use.
As I answered upthread, I'm not sure who would need this information before the
WAL machinery is up though. Note that ARCHIVE_RECOVERING and CRASH_RECOVERING
are compatible with the previous behavior.
Maybe the solution would be to init with CRASH_RECOVERING and add some comment
in GetRecoveryState() to warn the state is "enforced" after the XLOG machinery
is started and is init'ed to RECOVERING in the meantime?
I initialized it to CRASH_RECOVERING in the new v5 patch and added a comment
to GetRecoveryState().
> +typedef enum RecoveryState
> +{
> + NOT_IN_RECOVERY = 0, /* currently in production */
> + CRASH_RECOVERING, /* recovering from a crash */
> + ARCHIVE_RECOVERING /* recovering archives as requested */
> +} RecoveryState;
> I also have some issues with the name of those variables, here is an
> idea for the three states:
> - RECOVERY_STATE_CRASH
> - RECOVERT_STATE_ARCHIVE
> - RECOVERY_STATE_NONE
> I would recommend to use the same suffix for those variables to ease
> grepping.
Sounds really good to me. Thanks!
> /*
> - * Local copy of SharedRecoveryInProgress variable. True actually means "not
> - * known, need to check the shared state".
> + * This is false when SharedRecoveryState is not ARCHIVE_RECOVERING.
> + * True actually means "not known, need to check the shared state".
> */
> A double negation sounds wrong to me. And actually this variable is
> false when the shared state is set to NOT_IN_RECOVERY, and true when
> the state is either CRASH_RECOVERING or ARCHIVE_RECOVERING because it
> means that recovery is running, be it archive recovery or crash
> recovery, so the comment is wrong.
Indeed, sorry. Fixed.
> + /* The file is always deletable if archive_mode is "off". */
> + if ( ! XLogArchivingActive() )
> + return true;
> [...]
> + if ( recoveryState == ARCHIVE_RECOVERING && !XLogArchivingAlways() )
> return true;
> Incorrect indentation.
Is it the spaces as reported by Horiguchi-san? I removed them in latest patch.
> UpdateControlFile();
> +
> + SpinLockAcquire(&XLogCtl->info_lck);
> + XLogCtl->SharedRecoveryState = ARCHIVE_RECOVERING;
> + SpinLockRelease(&XLogCtl->info_lck);
> +
> LWLockRelease(ControlFileLock);
> Here, the shared flag is updated while holding ControlFileLock to
> ensure a consistent state of things within shared memory, so it would
> be nice to add a comment about that.
Indeed. the original code had no such comment and I asked myself the same
question. Added.
> +RecoveryState
> +GetRecoveryState(void)
> +{
> + volatile XLogCtlData *xlogctl = XLogCtl;
> +
> + return xlogctl->SharedRecoveryState;
> +}
> Er, you need to acquire info_lck to look at the state here, no?
Yes, fixed.
> /*
> - * The file is always deletable if archive_mode is "off". On standbys
> - * archiving is disabled if archive_mode is "on", and enabled with
> - * "always". On a primary, archiving is enabled if archive_mode is "on"
> - * or "always".
> + * On standbys, the file is deletable if archive_mode is not
> + * "always".
> */
> It would be good to mention that while in crash recovery, files are
> not considered as deletable.
Well, in fact, I am still wondering about this. I was hesitant to add a
shortcut like:
/* no WAL segment cleanup during crash recovery */
if (recoveryState == RECOVERT_STATE_CRASH)
return false;
But, what if for example we crashed for lack of disk space during intensive
wirtes? During crash recovery, any WAL marked as .done could be removed and
allow the system to start again and maybe make even further WAL cleanup by
archiving some more WAL without competing with previous high write ratio.
When we recover a primary, this behavior seems conform with any value of
archive_mode, even if it has been changed after crash and before starting it.
On a standby, we might create .ready files, but they will be removed during the
first restartpoint if needed.
> I agree that having a separate .pl file for the tests of this thread
> is just cleaner. Here are more comments about these.
>
> +# temporary fail archive_command for futur tests
> +$node->safe_psql('postgres', q{
> + ALTER SYSTEM SET archive_command TO 'false';
> + SELECT pg_reload_conf();
> +});
> That's likely portable on Windows even if you skip the tests there,
> and I am not sure that it is a good idea to rely on it being in PATH.
> Depending on the system, the path of the command is also likely going
> to be different. As here the goal is to prevent the archiver to do
> its work, why not relying on the configuration where archive_mode is
> set but archive_command is not? This would cause the archiver to be a
> no-op process, and .ready files will remain around. You could then
> replace the lookup of pg_stat_archiver with poll_query_until() and a
> query that makes use of pg_stat_file() to make sure that the .ready
> exists when needed.
I needed a failure so I can test pg_stat_archiver reports it as well. In my
mind, using "false" would either trigger a failure because false returns 1 or...
a failure because the command is not found. In either way, the result is the
same.
Using poll_query_until+pg_stat_file, is a good idea, but not enough as
archiver reports a failure some moment after the .ready signal file appears.
> + ok( -f "$node_data/pg_wal/archive_status/000000010000000000000001.ready",
> + "WAL still ready to archive in archive_status");
> It would be good to mention in the description the check applies to a
> primary.
done
> +# test recovery without archive_mode=always does not keep .ready WALs
> +$standby1 = get_new_node('standby');
> +$standby1->init_from_backup($node, 'backup', has_restoring => 1);
> +$standby1_data = $standby1->data_dir;
> +$standby1->start;
> +$standby1->safe_psql('postgres', q{CHECKPOINT});
> For readability archive_mode = on should be added to the configuration
> file? Okay, this is inherited from the primary, still that would
> avoid any issues with this code is refactored in some way.
added
> "WAL waiting to be archived in backup removed with archive_mode=on
> on
> standby" ); That should be "WAL segment" or "WAL file", but not WAL.
updated everywhere.
> Regarding the tests on a standby, it seems to me that the following
> is necessary:
> 1) Test that with archive_mode = on, segments are not marked with
> .ready.
> 2) Test that with archive_mode = always, segments are marked with
> .ready during archive recovery.
> 3) Test that with archive_mode = always, segments are not removed
> during crash recovery.
> I can see tests for 1) and 2),
Not really. The current tests does not check that segments created *after* the
backup are marked or not with .ready file on standby. I added these tests plus
some various other ones.
> but not 3). Could you add a
> stop('immediate')+start() for $standby2 at the end of
> 020_archive_status.pl and check that the .ready file is still there
> after crash recovery?
done.
> The end of the tests actually relies on the
> fact that archive_command is set to "false" when the cold backup is
> taken, before resetting it. I think that it would be cleaner to
> enforce the configuration you want to test before starting each
> standby. It becomes easier to understand the flow of the test for the
> reader.
done as well.
Thank you for your review!
Attachments:download all (2 files, .tar.gz)0001-v5-Fix-WAL-retention-during-production-crash-recovery.patch (text/x-patch, 7.0K) download
From 6654b0cd3073c4bed6c0cbf7a3b0e9fc7a4b9e2d Mon Sep 17 00:00:00 2001
From: Jehan-Guillaume de Rorthais <jgdr@dalibo.com>
Date: Wed, 1 Apr 2020 16:36:06 +0200
Subject: [PATCH 1/2] Fix WAL retention during production crash recovery
During crash recovery of a production cluster with archive_mode=on,
XLogArchiveCheckDone() was considering the cluster as inRecovery
without archive_mode=always. Because of this non-arcived WAL and
related .ready files were recycled or removed.
---
src/backend/access/transam/xlog.c | 48 +++++++++++++++++++-----
src/backend/access/transam/xlogarchive.c | 21 +++++++----
src/include/access/xlog.h | 21 +++++++++++
3 files changed, 74 insertions(+), 16 deletions(-)
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.cindex 11e32733c4..26920c7f3e 100644--- a/src/backend/access/transam/xlog.c+++ b/src/backend/access/transam/xlog.c@@ -221,8 +221,8 @@ static TimeLineID receiveTLI = 0;
static bool lastFullPageWrites;
/*
- * Local copy of SharedRecoveryInProgress variable. True actually means "not- * known, need to check the shared state".+ * This is false when SharedRecoveryState is RECOVERT_STATE_NONE.+ * True actually means "not known, need to check the shared state".
*/
static bool LocalRecoveryInProgress = true;
@@ -653,10 +653,10 @@ typedef struct XLogCtlData
TimeLineID PrevTimeLineID;
/*
- * SharedRecoveryInProgress indicates if we're still in crash or archive+ * SharedRecoveryState indicates if we're either in crash or archive
* recovery. Protected by info_lck.
*/
- bool SharedRecoveryInProgress;+ RecoveryState SharedRecoveryState;
/*
* SharedHotStandbyActive indicates if we allow hot standby queries to be
@@ -4434,6 +4434,16 @@ ReadRecord(XLogReaderState *xlogreader, int emode,
updateMinRecoveryPoint = true;
UpdateControlFile();
++ /*+ * We update SharedRecoveryState while holding the lock on+ * ControlFileLock so both states are consistent in shared+ * memory.+ */+ SpinLockAcquire(&XLogCtl->info_lck);+ XLogCtl->SharedRecoveryState = RECOVERT_STATE_ARCHIVE;+ SpinLockRelease(&XLogCtl->info_lck);+
LWLockRelease(ControlFileLock);
CheckRecoveryConsistency();
@@ -5166,10 +5176,10 @@ XLOGShmemInit(void)
* in additional info.)
*/
XLogCtl->XLogCacheBlck = XLOGbuffers - 1;
- XLogCtl->SharedRecoveryInProgress = true;
XLogCtl->SharedHotStandbyActive = false;
XLogCtl->SharedPromoteIsTriggered = false;
XLogCtl->WalWriterSleeping = false;
+ XLogCtl->SharedRecoveryState = RECOVERY_STATE_CRASH;
SpinLockInit(&XLogCtl->Insert.insertpos_lck);
SpinLockInit(&XLogCtl->info_lck);
@@ -7911,7 +7921,7 @@ StartupXLOG(void)
ControlFile->time = (pg_time_t) time(NULL);
SpinLockAcquire(&XLogCtl->info_lck);
- XLogCtl->SharedRecoveryInProgress = false;+ XLogCtl->SharedRecoveryState = RECOVERY_STATE_NONE;
SpinLockRelease(&XLogCtl->info_lck);
UpdateControlFile();
@@ -8057,7 +8067,7 @@ RecoveryInProgress(void)
*/
volatile XLogCtlData *xlogctl = XLogCtl;
- LocalRecoveryInProgress = xlogctl->SharedRecoveryInProgress;+ LocalRecoveryInProgress = (xlogctl->SharedRecoveryState != RECOVERY_STATE_NONE);
/*
* Initialize TimeLineID and RedoRecPtr when we discover that recovery
@@ -8069,8 +8079,8 @@ RecoveryInProgress(void)
{
/*
* If we just exited recovery, make sure we read TimeLineID and
- * RedoRecPtr after SharedRecoveryInProgress (for machines with- * weak memory ordering).+ * RedoRecPtr after SharedRecoveryState (for machines with weak+ * memory ordering).
*/
pg_memory_barrier();
InitXLOGAccess();
@@ -8086,6 +8096,26 @@ RecoveryInProgress(void)
}
}
+/*+ * Returns current recovery state from shared memory.+ *+ * This returned state is accurate after StartupXLOG()+ * finished.+ *+ * See details about RecoveryState in xlog.h+ */+RecoveryState+GetRecoveryState(void)+{+ RecoveryState retval;++ SpinLockAcquire(&XLogCtl->info_lck);+ retval = XLogCtl->SharedRecoveryState;+ SpinLockRelease(&XLogCtl->info_lck);++ return retval;+}+
/*
* Is HotStandby active yet? This is only important in special backends
* since normal backends won't ever be able to connect until this returns
diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.cindex d62c12310a..d2ffc7e1a2 100644--- a/src/backend/access/transam/xlogarchive.c+++ b/src/backend/access/transam/xlogarchive.c@@ -572,18 +572,25 @@ XLogArchiveCheckDone(const char *xlog)
{
char archiveStatusPath[MAXPGPATH];
struct stat stat_buf;
- bool inRecovery = RecoveryInProgress();+ RecoveryState recoveryState = GetRecoveryState();++ /* The file is always deletable if archive_mode is "off". */+ if (!XLogArchivingActive())+ return true;
/*
- * The file is always deletable if archive_mode is "off". On standbys- * archiving is disabled if archive_mode is "on", and enabled with- * "always". On a primary, archiving is enabled if archive_mode is "on"- * or "always".+ * On standbys, the file is deletable if archive_mode is not+ * "always".
*/
- if (!((XLogArchivingActive() && !inRecovery) ||- (XLogArchivingAlways() && inRecovery)))+ if (recoveryState == RECOVERT_STATE_ARCHIVE && !XLogArchivingAlways())
return true;
+ /*+ * We are either:+ * * a primary with archive_mode set to "on" or "always"+ * * a standby with archive_mode set to "always".+ */+
/* First check for .done --- this means archiver is done with it */
StatusFilePath(archiveStatusPath, xlog, ".done");
if (stat(archiveStatusPath, &stat_buf) == 0)
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.hindex f60ed2d36c..157a3fb104 100644--- a/src/include/access/xlog.h+++ b/src/include/access/xlog.h@@ -166,6 +166,26 @@ typedef enum WalLevel
WAL_LEVEL_LOGICAL
} WalLevel;
+/*+ * Recovery state.+ *+ * There's two different reasons to recover WAL: when standby mode is requested+ * or after a crash to catchup with consistency.+ *+ * RECOVERY_STATE_CRASH is set when recovering after a crash.+ * RECOVERT_STATE_ARCHIVE is set when archive recovery or standby is requested.+ * RECOVERY_STATE_NONE means the cluster is in production.+ *+ * Note that even if archive recovery or standby is requested, recovery state+ * starts first with RECOVERY_STATE_CRASH before switching to ARCHIVE_RECOVERING+ */+typedef enum RecoveryState+{+ RECOVERY_STATE_NONE = 0, /* currently in production */+ RECOVERY_STATE_CRASH, /* recovering from a crash */+ RECOVERT_STATE_ARCHIVE /* recovering archives as requested */+} RecoveryState;+
extern PGDLLIMPORT int wal_level;
/* Is WAL archiving enabled (always or only while server is running normally)? */
@@ -291,6 +311,7 @@ extern const char *xlog_identify(uint8 info);
extern void issue_xlog_fsync(int fd, XLogSegNo segno);
extern bool RecoveryInProgress(void);
+extern RecoveryState GetRecoveryState(void);
extern bool HotStandbyActive(void);
extern bool HotStandbyActiveInReplay(void);
extern bool XLogInsertAllowed(void);
--
2.20.1
0002-v4-Add-tests-relative-to-WAL-archiving.patch (text/x-patch, 7.4K) download
From 14fab693010b1e034d0bd9e7c81a8ac48aa8f8d8 Mon Sep 17 00:00:00 2001
From: Jehan-Guillaume de Rorthais <jgdr@dalibo.com>
Date: Thu, 9 Apr 2020 18:38:50 +0200
Subject: [PATCH 2/2] Add tests relative to WAL archiving
---
src/test/recovery/t/020_archive_status.pl | 193 ++++++++++++++++++++++
1 file changed, 193 insertions(+)
create mode 100644 src/test/recovery/t/020_archive_status.pl
diff --git a/src/test/recovery/t/020_archive_status.pl b/src/test/recovery/t/020_archive_status.plnew file mode 100644index 0000000000..a5f4fd821a--- /dev/null+++ b/src/test/recovery/t/020_archive_status.pl@@ -0,0 +1,193 @@+#+# Tests relating to WAL archiving and cleanup+#+use strict;+use warnings;+use PostgresNode;+use TestLib;+use Test::More;+use Config;++my ($node, $standby1, $standby2);+my ($node_data, $standby1_data, $standby2_data);++if ($Config{osname} eq 'MSWin32')+{++ # some Windows Perls at least don't like IPC::Run's start/kill_kill regime.+ plan skip_all => "Test fails on Windows perl";+}+else+{+ plan tests => 16;+}++$node = get_new_node('master');+$node->init(+ has_archiving => 1,+ allows_streaming => 1+);+$node->start;+$node_data = $node->data_dir;++# temporary fail archive_command for futur tests+$node->safe_psql('postgres', q{+ ALTER SYSTEM SET archive_command TO 'false';+ SELECT pg_reload_conf();+});++$node->safe_psql('postgres', q{+ CREATE TABLE mine AS SELECT generate_series(1,10) AS x;+ SELECT pg_switch_wal();+ CHECKPOINT;+});++# wait for archive failure+$node->poll_query_until('postgres',+ q{SELECT failed_count > 0 FROM pg_stat_archiver},+ 't') or die "Timed out while waiting for archiver to fail";++ok( -f "$node_data/pg_wal/archive_status/000000010000000000000001.ready",+ "ready file exists for WAL waiting to be archived");++is($node->safe_psql('postgres', q{+ SELECT archived_count, last_failed_wal+ FROM pg_stat_archiver+ }),+ '0|000000010000000000000001', 'pg_stat_archiver reports archive failure');++# We need to crash the cluster because next test checks the crash+# recovery step do not removes non-archived WAL.+$node->stop('immediate');++# Standby recovery tests checks the recovery behavior when restoring a+# backup taken using eg. a snapshot with no pg_start/stop_backup.+# In this situation, the recovered standby should enter first crash+# recovery then switch to regular archive recovery.+$node->backup_fs_cold('backup');++$node->start;++ok( -f "$node_data/pg_wal/archive_status/000000010000000000000001.ready",+ "WAL segment still ready to archive after crash recovery on primary");++# Allow WAL archiving again+$node->safe_psql('postgres', q{+ ALTER SYSTEM RESET archive_command;+ SELECT pg_reload_conf();+});++# wait for archive success+$node->poll_query_until('postgres',+ q{SELECT archived_count FROM pg_stat_archiver},+ '1') or die "Timed out while waiting for archiver to success";++ok( ! -f "$node_data/pg_wal/archive_status/000000010000000000000001.ready",+ "ready file for archived WAL removed");++ok( -f "$node_data/pg_wal/archive_status/000000010000000000000001.done",+ "done file for archived WAL exists");++is($node->safe_psql('postgres',+ q{ SELECT last_archived_wal FROM pg_stat_archiver }),+ '000000010000000000000001',+ 'Archive success reported in pg_stat_archiver');++# create some wal activity and a new checkpoint so futur standby can create+# a restartpoint.+# As standby start in crash recovery because of the backup method, they need+# a clean restartpoint to deal with existing status files.+$node->safe_psql('postgres', q{+ INSERT INTO mine SELECT generate_series(10,20) AS x;+ SELECT pg_switch_wal();+ CHECKPOINT;+});++$node->poll_query_until('postgres',+ q{ SELECT last_archived_wal FROM pg_stat_archiver },+ '000000010000000000000002') or die "Timed out while waiting for archiver to succeed";++# tests standby with archive_mode=on+$standby1 = get_new_node('standby');+$standby1->init_from_backup($node, 'backup', has_restoring => 1);+$standby1->append_conf('postgresql.conf', "archive_mode = on");+$standby1_data = $standby1->data_dir;+$standby1->start;+$standby1->safe_psql('postgres', q{CHECKPOINT});++# recovery with archive_mode=on does not keep .ready signal files inherited from backup.+# 000000010000000000000001.ready existed in the backup.+ok( ! -f "$standby1_data/pg_wal/archive_status/000000010000000000000001.ready",+ ".ready signal file existing in backup removed with archive_mode=on on standby" );++# recovery with archive_mode=on should not create .ready signal files+# 000000010000000000000002.ready did not exist in the backup+ok( ! -f "$standby1_data/pg_wal/archive_status/000000010000000000000002.ready",+ "standby doesn't create .ready signal file when archive_mode=on" );++# recovery with archive_mode=on creates .done signal files+ok( -f "$standby1_data/pg_wal/archive_status/000000010000000000000002.done",+ "standby creates .done signal file when archive_mode=on" );++# test recovery with archive_mode=always keeps .ready WALs+$standby2 = get_new_node('standby2');+$standby2->init_from_backup($node, 'backup', has_restoring => 1);+$standby2->append_conf('postgresql.conf', 'archive_mode = always');+$standby1->append_conf('postgresql.auto.conf', "archive_command = false");+$standby2_data = $standby2->data_dir;+$standby2->start;++$standby2->safe_psql('postgres', q{CHECKPOINT});++ok( -f "$standby2_data/pg_wal/archive_status/000000010000000000000001.ready",+ ".ready signal file existing in backup are kept with archive_mode=always on standby" );++ok( -f "$standby2_data/pg_wal/archive_status/000000010000000000000002.ready",+ ".ready signal file are created with archive_mode=always on standby" );++# Allow WAL archiving again+$standby2->safe_psql('postgres', q{SELECT pg_stat_reset_shared('archiver')});++is($standby2->safe_psql('postgres',+ q{ SELECT failed_count, archived_count FROM pg_stat_archiver }),+ '0|0',+ 'statistics reset from pg_stat_archiver succeed');++# We need to crash the cluster because next test checks the crash+# recovery step do not removes non-archived WAL on a standby.+$standby2->stop('immediate');+$standby2->start;++ok( -f "$standby2_data/pg_wal/archive_status/000000010000000000000001.ready",+ "WAL segment still ready to archive after crash recovery on standby with archive_mode=always");++# Allow WAL archiving again+$standby2->safe_psql('postgres', q{+ ALTER SYSTEM RESET archive_command;+ SELECT pg_reload_conf();+});++# wait for archive success+$standby2->poll_query_until('postgres',+ q{SELECT last_archived_wal FROM pg_stat_archiver},+ '000000010000000000000002') or die "Timed out while waiting for archiver to success";++is($standby2->safe_psql('postgres',+ q{SELECT archived_count FROM pg_stat_archiver}),+ '2', 'WAL segment archived from standby');++ok( ! -f "$standby2_data/pg_wal/archive_status/000000010000000000000001.ready"+ && ! -f "$standby2_data/pg_wal/archive_status/000000010000000000000002.ready",+ ".ready signal file removed after archiving with archive_mode=always on standby" );++ok( -f "$standby2_data/pg_wal/archive_status/000000010000000000000001.done"+ and -f "$standby2_data/pg_wal/archive_status/000000010000000000000002.done",+ ".done signal file created after archiving with archive_mode=always on standby" );++#ok(0);+#+## wait for archive success+#$standby2->poll_query_until('postgres',+# q{SELECT 1},+# '0') or die "Timed out while waiting for archiver to success";+--
2.20.1
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: jgdr@dalibo.com, michael@paquier.xyz, horikyota.ntt@gmail.com, masao.fujii@oss.nttdata.com, pgsql-bugs@lists.postgresql.org
Subject: Re: [BUG] non archived WAL removed during production crash recovery
In-Reply-To: <20200414180319.731f9593@firost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox