Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lJRBB-0007kM-Ql for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Mar 2021 01:28:58 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1lJRB8-00033O-0y for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Mar 2021 01:28:54 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lJRB7-00033H-KW for pgsql-hackers@lists.postgresql.org; Tue, 09 Mar 2021 01:28:53 +0000 Received: from mail-ej1-x62d.google.com ([2a00:1450:4864:20::62d]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1lJRB3-00059D-EK for pgsql-hackers@lists.postgresql.org; Tue, 09 Mar 2021 01:28:52 +0000 Received: by mail-ej1-x62d.google.com with SMTP id jt13so24353345ejb.0 for ; Mon, 08 Mar 2021 17:28:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=enterprisedb-com.20150623.gappssmtp.com; s=20150623; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=JhyUG9ILMZ5rXIEEQ0fdUXGs0a8QCtU+zRNrKLisZrE=; b=1Hx/tMe6psUi0iijUMhgbqt8jxFRFN35Yi2HYAWmNqabWxh98RSae2xdLPigp1EqjC 1n27eoTq9k6D2aMKYwGsn83ex7n3HsV/CUKwa1+LBz28yA0fEq5tybGUfdVGkoI3EpSY ydnW+UlIWB8NkpfMagHNrPrWVlBnt4PfXK4SdkRK3Mz9GduyfO8Hrz8wd7iKR3jqqJI1 xj4thhS+fhKwiULWrSpXvGkeBrlYfGEVEkESjQoO09MVpu97EkUdVuWvmVnQ3Q3MK9pl MuhiYcdgRi70U4LFPcTsz8DpRQAo3OOpG+ouPakYO833q2EDsWzj5NmdEYB7JJMcYGfm vWpQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=JhyUG9ILMZ5rXIEEQ0fdUXGs0a8QCtU+zRNrKLisZrE=; b=Va4v/0YO1cbeiAbnsuHXZSeHeB5/qD2d9cDJnOcYyYFpypaVsNzw8fkMOv3aqE5tB7 VdNVzEBPQgDzYZl5rajBbLRwcUTzc62lx08lBJTHtFbtRMzbj4o5vMyPzsXXU+Emd+NC 9ay1Ifcz85l1KRcaq3BKhiWNYJl3Ezk+7+nhHws1zifFw4MHvI6XHZ77LAR6gfPy0nxM Vv1WUZuhwn53+cholmOdmG0MNw1YQwvbe2XyNEcevDbgq5i9NxeAJxTsXCPGHY0xqKe9 fVOrTqH8mmfyiHdZ/979/qVOlJ0vKXNW83PLoU8OdgbKX0YFVQBvQVk1v/aCue8vbt/q 1jXw== X-Gm-Message-State: AOAM531UdQb1Fdd5TKHFxFSsnXYnfmlg1KSJt7Tpe+FkLc5jMN0yPpZo CUlfpdkAC0ONagdTKMNK9TYszbMBYHmm20IBI8hJhY09nXuj35oWQfrvI3HGs01xQB2bjboeiXN 5Q9Dwf7zLxwijEmYKrzaeYZO+vqx4CZwNeQLm02fi/oHzs1uIZpmjbHJpQ/bhA1UJg/cQTQdzz6 PaaoMKoNvOll9mKumqM/e7aSKr7MXOjAoQf+yDwygbYucEFm1nmm/J X-Google-Smtp-Source: ABdhPJzYXNkMBxRBA4/x/HQGKb6OfRQrXns2JHZXNEMVFRS8d1X8ceUtR9z4u4D7YqsmFf41J6CoeQ== X-Received: by 2002:a17:906:f953:: with SMTP id ld19mr17116372ejb.164.1615253326729; Mon, 08 Mar 2021 17:28:46 -0800 (PST) Received: from [10.137.0.20] (ip-86-49-253-127.net.upcbroadband.cz. [86.49.253.127]) by smtp.gmail.com with ESMTPSA id ha5sm7376961ejb.39.2021.03.08.17.28.44 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 08 Mar 2021 17:28:45 -0800 (PST) Subject: Re: cleanup temporary files after crash To: Euler Taveira , Anastasia Lubennikova Cc: Michael Paquier , Tomas Vondra , PostgreSQL Hackers , Tom Lane References: <20201028184252.5vpoe65kcfebbmmf@development> <20201101012555.GC3000@paquier.xyz> <1982ee3c-54c3-280a-806a-fd28ad8f19e6@postgrespro.ru> From: Tomas Vondra Message-ID: <363cb85c-3a03-9a49-b830-8b645c1241d9@enterprisedb.com> Date: Tue, 9 Mar 2021 02:28:43 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.0 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------002C097FE66DA941AEB9F7C8" Content-Language: en-US X-CLOUD-SEC-AV-Info: enterprisedb,google_mail,monitor X-CLOUD-SEC-AV-Sent: true X-Gm-Spam: 0 X-Gm-Phishy: 0 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --------------002C097FE66DA941AEB9F7C8 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Hi, Let's move this patch forward. Based on the responses, I agree the default behavior should be to remove the temp files, and I think we should have the GUC (on the off chance that someone wants to preserve the temporary files for debugging or whatever other reason). I propose to rename the GUC to remove_temp_files_after_crash, I think "remove" is a bit clearer than "cleanup". I've also reworded the sgml docs a little bit. Attached is a patch with those changes. Barring objections, I'll get this committed in the next couple days. regards -- Tomas Vondra EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company --------------002C097FE66DA941AEB9F7C8 Content-Type: text/x-patch; charset=UTF-8; name="0001-Control-temporary-files-removal-after-crash-20210309.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Control-temporary-files-removal-after-crash-20210309.pa"; filename*1="tch" From 70cc3bf303585f1ffcecbace43d13d3d4f545f1b Mon Sep 17 00:00:00 2001 From: Euler Taveira Date: Mon, 25 May 2020 00:08:20 -0300 Subject: [PATCH] Control temporary files removal after crash A new GUC remove_temp_files_after_crash controls whether temporary files are removed after a crash. Successive crashes could result in useless storage usage until service is restarted. It could be the case on host with inadequate resources. This manual intervention for some environments is not desirable. This GUC is marked as SIGHUP hence you don't have to interrupt the service to change it. The current behavior introduces a backward incompatibility (minor one) which means Postgres will reclaim temporary file space after a crash. The main reason is that we favor service continuity over debugging. --- doc/src/sgml/config.sgml | 18 ++ src/backend/postmaster/postmaster.c | 5 + src/backend/storage/file/fd.c | 12 +- src/backend/utils/misc/guc.c | 9 + src/backend/utils/misc/postgresql.conf.sample | 2 + src/include/postmaster/postmaster.h | 1 + src/test/recovery/t/022_crash_temp_files.pl | 194 ++++++++++++++++++ 7 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 src/test/recovery/t/022_crash_temp_files.pl diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 967de73596..1b102960d6 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -9665,6 +9665,24 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' + + remove_temp_files_after_crash (boolean) + + remove_temp_files_after_crash configuration parameter + + + + + When set to on, PostgreSQL will automatically + remove temporary files after a backend crash. When disabled, the temporary + files are retained after a crash, which may be useful in some circumstances + (e.g. during debugging). It may however result in accumulation of many + useless files, or possibly even running out of disk space. + It defaults to on. + + + + data_sync_retry (boolean) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index edab95a19e..2338892c33 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -242,6 +242,7 @@ bool Db_user_namespace = false; bool enable_bonjour = false; char *bonjour_name; bool restart_after_crash = true; +bool remove_temp_files_after_crash = true; /* PIDs of special child processes; 0 when not running */ static pid_t StartupPID = 0, @@ -3975,6 +3976,10 @@ PostmasterStateMachine(void) ereport(LOG, (errmsg("all server processes terminated; reinitializing"))); + /* remove leftover temporary files after a crash */ + if (remove_temp_files_after_crash) + RemovePgTempFiles(); + /* allow background workers to immediately restart */ ResetBackgroundWorkerCrashTimes(); diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index b58502837a..dfc3c2fe3e 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -3024,11 +3024,13 @@ CleanupTempFiles(bool isCommit, bool isProcExit) * remove any leftover files created by OpenTemporaryFile and any leftover * temporary relation files created by mdcreate. * - * NOTE: we could, but don't, call this during a post-backend-crash restart - * cycle. The argument for not doing it is that someone might want to examine - * the temp files for debugging purposes. This does however mean that - * OpenTemporaryFile had better allow for collision with an existing temp - * file name. + * During post-backend-crash restart cycle, this routine could be called if + * remove_temp_files_after_crash GUC is enabled. Multiple crashes while + * queries are using temp files could result in useless storage usage that can + * only be reclaimed by a service restart. The argument against enabling it is + * that someone might want to examine the temp files for debugging purposes. + * This does however mean that OpenTemporaryFile had better allow for + * collision with an existing temp file name. * * NOTE: this function and its subroutines generally report syscall failures * with ereport(LOG) and keep going. Removing temp files is not so critical diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 3fd1a5fbe2..64ac48b2e5 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1361,6 +1361,15 @@ static struct config_bool ConfigureNamesBool[] = true, NULL, NULL, NULL }, + { + {"remove_temp_files_after_crash", PGC_SIGHUP, ERROR_HANDLING_OPTIONS, + gettext_noop("Remove temporary files after backend crash."), + NULL + }, + &remove_temp_files_after_crash, + true, + NULL, NULL, NULL + }, { {"log_duration", PGC_SUSET, LOGGING_WHAT, diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index ee06528bb0..25c1b89ffc 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -759,6 +759,8 @@ #exit_on_error = off # terminate session on any error? #restart_after_crash = on # reinitialize after backend crash? +#remove_temp_files_after_crash = on # remove temporary files after +# # backend crash? #data_sync_retry = off # retry or panic on failure to fsync # data? # (change requires restart) diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index cfa59c4dc0..0efdd7c232 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -29,6 +29,7 @@ extern bool log_hostname; extern bool enable_bonjour; extern char *bonjour_name; extern bool restart_after_crash; +extern bool remove_temp_files_after_crash; #ifdef WIN32 extern HANDLE PostmasterHandle; diff --git a/src/test/recovery/t/022_crash_temp_files.pl b/src/test/recovery/t/022_crash_temp_files.pl new file mode 100644 index 0000000000..45a70a368e --- /dev/null +++ b/src/test/recovery/t/022_crash_temp_files.pl @@ -0,0 +1,194 @@ +# +# Test cleanup of temporary files after a crash. +# +# This is a description that I will add later. +# +use strict; +use warnings; +use PostgresNode; +use TestLib; +use Test::More; +use Config; +use Time::HiRes qw(usleep); + +plan tests => 9; + + +# To avoid hanging while expecting some specific input from a psql +# instance being driven by us, add a timeout high enough that it +# should never trigger even on very slow machines, unless something +# is really wrong. +my $psql_timeout = IPC::Run::timer(60); + +my $node = get_new_node('node_crash'); +$node->init(); +$node->start(); + +# By default, PostgresNode doesn't restart after crash +# Reduce work_mem to generate temporary file with a few number of rows +$node->safe_psql( + 'postgres', + q[ALTER SYSTEM SET remove_temp_files_after_crash = on; + ALTER SYSTEM SET log_connections = 1; + ALTER SYSTEM SET work_mem = '64kB'; + ALTER SYSTEM SET restart_after_crash = on; + SELECT pg_reload_conf();]); + +# create table, insert rows +$node->safe_psql( + 'postgres', + q[CREATE TABLE tab_crash (a text); + INSERT INTO tab_crash (a) SELECT gen_random_uuid() FROM generate_series(1, 500);]); + +# +# Test cleanup temporary files after crash +# + +# Run psql, keeping session alive, so we have an alive backend to kill. +my ($killme_stdin, $killme_stdout, $killme_stderr) = ('', '', ''); +my $killme = IPC::Run::start( + [ + 'psql', '-X', '-qAt', '-v', 'ON_ERROR_STOP=1', '-f', '-', '-d', + $node->connstr('postgres') + ], + '<', + \$killme_stdin, + '>', + \$killme_stdout, + '2>', + \$killme_stderr, + $psql_timeout); + +# Get backend pid +$killme_stdin .= q[ +SELECT pg_backend_pid(); +]; +ok(pump_until($killme, \$killme_stdout, qr/[[:digit:]]+[\r\n]$/m), + 'acquired pid for SIGKILL'); +my $pid = $killme_stdout; +chomp($pid); +$killme_stdout = ''; +$killme_stderr = ''; + +# Run the query that generates a temporary file and that will be killed before +# it finishes. Since the query that generates the temporary file does not +# return before the connection is killed, use a SELECT before to trigger +# pump_until. +$killme_stdin .= q[ +BEGIN; +SELECT $$in-progress-before-sigkill$$; +WITH foo AS (SELECT a FROM tab_crash ORDER BY a) SELECT a, pg_sleep(1) FROM foo; +]; +ok(pump_until($killme, \$killme_stdout, qr/in-progress-before-sigkill/m), + 'select in-progress-before-sigkill'); +$killme_stdout = ''; +$killme_stderr = ''; + +# Kill with SIGKILL +my $ret = TestLib::system_log('pg_ctl', 'kill', 'KILL', $pid); +is($ret, 0, 'killed process with KILL'); + +# Close psql session +$killme->finish; + +# Wait till server restarts +$node->poll_query_until('postgres', 'SELECT 1', '1'); + +# Check for temporary files +is($node->safe_psql( + 'postgres', + 'SELECT COUNT(1) FROM pg_ls_dir($$base/pgsql_tmp$$)'), + qq(0), 'no temporary files'); + +# +# Test old behavior (doesn't cleanup temporary file after crash) +# +$node->safe_psql( + 'postgres', + q[ALTER SYSTEM SET remove_temp_files_after_crash = off; + SELECT pg_reload_conf();]); + +# Restart psql session +($killme_stdin, $killme_stdout, $killme_stderr) = ('', '', ''); +$killme->run(); + +# Get backend pid +$killme_stdin .= q[ +SELECT pg_backend_pid(); +]; +ok(pump_until($killme, \$killme_stdout, qr/[[:digit:]]+[\r\n]$/m), + 'acquired pid for SIGKILL'); +$pid = $killme_stdout; +chomp($pid); +$killme_stdout = ''; +$killme_stderr = ''; + +# Run the query that generates a temporary file and that will be killed before +# it finishes. Since the query that generates the temporary file does not +# return before the connection is killed, use a SELECT before to trigger +# pump_until. +$killme_stdin .= q[ +BEGIN; +SELECT $$in-progress-before-sigkill$$; +WITH foo AS (SELECT a FROM tab_crash ORDER BY a) SELECT a, $$in-progress-before-sigkill$$, pg_sleep(1) FROM foo; +]; +ok(pump_until($killme, \$killme_stdout, qr/in-progress-before-sigkill/m), + 'select in-progress-before-sigkill'); +$killme_stdout = ''; +$killme_stderr = ''; + +# Kill with SIGKILL +$ret = TestLib::system_log('pg_ctl', 'kill', 'KILL', $pid); +is($ret, 0, 'killed process with KILL'); + +# Close psql session +$killme->finish; + +# Wait till server restarts +$node->poll_query_until('postgres', 'SELECT 1', '1'); + +# Check for temporary files -- should be there +is($node->safe_psql( + 'postgres', + 'SELECT COUNT(1) FROM pg_ls_dir($$base/pgsql_tmp$$)'), + qq(1), 'one temporary file'); + +# Restart should remove the temporary files +$node->restart(); + +# Check the temporary files -- should be gone +is($node->safe_psql( + 'postgres', + 'SELECT COUNT(1) FROM pg_ls_dir($$base/pgsql_tmp$$)'), + qq(0), 'temporary file was removed'); + +$node->stop(); + +# Pump until string is matched, or timeout occurs +sub pump_until +{ + my ($proc, $stream, $untl) = @_; + $proc->pump_nb(); + while (1) + { + last if $$stream =~ /$untl/; + if ($psql_timeout->is_expired) + { + diag("aborting wait: program timed out"); + diag("stream contents: >>", $$stream, "<<"); + diag("pattern searched for: ", $untl); + + return 0; + } + if (not $proc->pumpable()) + { + diag("aborting wait: program died"); + diag("stream contents: >>", $$stream, "<<"); + diag("pattern searched for: ", $untl); + + return 0; + } + $proc->pump(); + } + return 1; +} -- 2.29.2 --------------002C097FE66DA941AEB9F7C8--