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 1kdsLr-0004Dj-Vu for pgsql-hackers@arkaria.postgresql.org; Sat, 14 Nov 2020 10:00:12 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1kdsLo-0003nt-Uk for pgsql-hackers@arkaria.postgresql.org; Sat, 14 Nov 2020 10:00:08 +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 1kdsLo-0003nm-KH for pgsql-hackers@lists.postgresql.org; Sat, 14 Nov 2020 10:00:08 +0000 Received: from mail-lj1-x232.google.com ([2a00:1450:4864:20::232]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1kdsLk-0007I3-7L for pgsql-hackers@postgresql.org; Sat, 14 Nov 2020 10:00:07 +0000 Received: by mail-lj1-x232.google.com with SMTP id p12so13944313ljc.9 for ; Sat, 14 Nov 2020 02:00:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:subject:to:message-id:date:user-agent:mime-version :content-language; bh=xOB2SPnBoXsknTFYC3XzMclHANN66n0pVaPr2eUCP7g=; b=LabEhi9gfO8mpTZxoL7+MC3aKmU1QJ7Ie0P3W+1CmcxUQiuTqIBOFS7y+iK6pH6iQG VdnX6ngx05Q6fshLv4jeT1dQweTTHLYfEM8vMzKcC3gYwbCf5yCJqcupvP2Yde+K8CHS ErhBPqAa+J8RKdNkGfsZMjUfZn/y0wPD9cx7BP5JOWXVySu5qFcyvP66Do8zskpRik/n 74KO7QNTFYaeEGVmB7XGx08r3zLyfTDRFwpDYQeaTAiX1J7G9ncRT0+qJcS8M2+nFBNv WdsdfR4fsVzywG7R2sBUgR56gTYgpGKSHAnpE0+0ludc4sk4Dysy+9sqjzQkDqCDMRXw ZYFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:subject:to:message-id:date:user-agent :mime-version:content-language; bh=xOB2SPnBoXsknTFYC3XzMclHANN66n0pVaPr2eUCP7g=; b=Nk4mGJIIapcs9cf1/M+cV63OxWOHR7AJjq/zDZe0dQhRV/aLGFZA8rWd1HrpdE7ND3 njaa6zIlgs4Efi1Qc5GMp6Fw9FTs9xX72Y9nBGWh+eEsg49mXAQIoNmTaIAlD1KbiMqV T0aByUPwADjUL4b3RyxkLMcCpNd/SZszIiRyRWwnHx3oajxikNnoHB90rHSGQAVJEzI3 GqkP7/ZY1OVjaNhv/W5EMq+JU8dNwv/zcOo4MDBgK86HK2NEL25F5PcD0zCpopD9TPkL DWfyho+Y77PN3eayp3S4FALuw2aJqw9mJC8mzZI4LMKZ9rZpHouMQa9JwAy5oKBrRL8u tUng== X-Gm-Message-State: AOAM530HovpveWqXbUXD4BltVrrESzDMcpvyCwRf9Krtek6dtgPsID4u BXg+qKicVI16o/w/zQk07X0AzEEk1JMIrA== X-Google-Smtp-Source: ABdhPJyspUc+La+Gncb/TgNgfHy6SYxSw5YSyeVm60RllJ9r8oCFF7K5776TqzmWb9JcJac5xLhAKQ== X-Received: by 2002:a2e:9d8d:: with SMTP id c13mr2712992ljj.160.1605348001787; Sat, 14 Nov 2020 02:00:01 -0800 (PST) Received: from [1.0.0.7] ([178.155.6.8]) by smtp.gmail.com with ESMTPSA id j4sm1881716lfk.275.2020.11.14.02.00.00 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 14 Nov 2020 02:00:01 -0800 (PST) From: Alexander Lakhin Subject: More time spending with "delete pending" To: pgsql-hackers Message-ID: Date: Sat, 14 Nov 2020 13:00:00 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.10.0 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------31154500D5416223148F03B6" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------31154500D5416223148F03B6 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Hello hackers, After fixing bug #16161 (pg_ctl inability to open just deleted postmaster.pid) there are still some errors related to the same Windows-only issue. Namely, pg_upgradeCheck failures seen on https://buildfarm.postgresql.org/cgi-bin/show_history.pl?nm=fairywren&br=REL_13_STABLE Here pg_ls_dir_files couldn't handle the "delete pending" state too. I've made a simple test to reproduce this behavior with pg_ls_waldir, that fails with: error running SQL: 'psql::1: ERROR:  could not stat file "pg_wal/dummy": Permission denied' As noted in [1], a sensible solution would be putting the same "retry on ERROR_ACCESS_DENIED" action in a wrapper for stat(). And bed90759f brought in master the _pgstat64() function, where such error handling should be placed. So please look at the patch (based on the previous one made to fix bug#16161), that makes the attached test pass. And I have a few questions. For now genfile.c needs to include "port.h" explicitly to override definitions in "sys/stat.h", that override "stat" redefinition for Windows included implicitly above via "postgres.h". Without it the pg_ls_dir_files() function just uses system' stat(). So I wonder, is there a way to make all stat-calling code use the improved stat()? And if so, maybe the stat() call in pgwin32_open should be replaced with microsoft_native_stat(). And regarding released versions, are there any reasons to not backport bed90759f (with the proposed fix or alike)? [1] https://www.postgresql.org/message-id/396837.1605298573%40sss.pgh.pa.us Best regards, Alexander --------------31154500D5416223148F03B6 Content-Type: application/x-perl; name="001_delete_pending.pl" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="001_delete_pending.pl" use strict; use warnings; use PostgresNode; use TestLib; use Test::More tests => 1; my $duration = 60; # Initialize a node my $node = get_new_node('node'); $node->init; $node->start; my $waldir = $node->data_dir . "/pg_wal"; my ($stdout, $stderr); my $cdproc = IPC::Run::start( [ 'perl', '-e', "for (;;) { my \$filename='$waldir/dummy'; open(\$FH, '>', \$filename); close(\$FH); unlink(\$filename);}" ], '<', \undef, '>', \$stdout, '2>', \$stderr, IPC::Run::timeout($duration)); my $end = time() + $duration; my $result = 1; while (time() < $end && $result) { $result = $node->safe_psql('postgres', "select count(*) > 0 as ok from pg_ls_waldir();" ); } $cdproc->signal('KILL'); sleep(1); unlink("$waldir/dummy"); ok($result, 'No errors occured'); --------------31154500D5416223148F03B6 Content-Type: text/x-patch; charset=UTF-8; name="fix_pg_ls_dir.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="fix_pg_ls_dir.patch" diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index d34182a7b0..922df49125 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -28,6 +28,7 @@ #include "funcapi.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "port.h" #include "postmaster/syslogger.h" #include "storage/fd.h" #include "utils/acl.h" diff --git a/src/port/win32stat.c b/src/port/win32stat.c index 4351aa4d08..c2b55c7fa6 100644 --- a/src/port/win32stat.c +++ b/src/port/win32stat.c @@ -170,6 +170,7 @@ _pgstat64(const char *name, struct stat *buf) SECURITY_ATTRIBUTES sa; HANDLE hFile; int ret; + int loops = 0; #if _WIN32_WINNT < 0x0600 IO_STATUS_BLOCK ioStatus; FILE_STANDARD_INFORMATION standardInfo; @@ -182,31 +183,60 @@ _pgstat64(const char *name, struct stat *buf) errno = EINVAL; return -1; } - /* fast not-exists check */ if (GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES) { - _dosmaperr(GetLastError()); - return -1; + DWORD err = GetLastError(); + + if (err != ERROR_ACCESS_DENIED) { + _dosmaperr(err); + return -1; + } } /* get a file handle as lightweight as we can */ sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; - hFile = CreateFile(name, - GENERIC_READ, - (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), - &sa, - OPEN_EXISTING, - (FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS | - FILE_FLAG_OVERLAPPED), - NULL); - if (hFile == INVALID_HANDLE_VALUE) + while ((hFile = CreateFile(name, + GENERIC_READ, + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + &sa, + OPEN_EXISTING, + (FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OVERLAPPED), + NULL)) == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); - CloseHandle(hFile); + /* + * ERROR_ACCESS_DENIED is returned if the file is deleted but not yet + * gone (Windows NT status code is STATUS_DELETE_PENDING). In that + * case we want to wait a bit and try again, giving up after 1 second + * (since this condition should never persist very long). However, + * there are other commonly-hit cases that return ERROR_ACCESS_DENIED, + * so care is needed. In particular that happens if we try to open a + * directory, or of course if there's an actual file-permissions + * problem. To distinguish these cases, try a stat(). In the + * delete-pending case, it will either also get STATUS_DELETE_PENDING, + * or it will see the file as gone and fail with ENOENT. In other + * cases it will usually succeed. The only somewhat-likely case where + * this coding will uselessly wait is if there's a permissions problem + * with a containing directory, which we hope will never happen in any + * performance-critical code paths. + */ + if (err == ERROR_ACCESS_DENIED) + { + struct microsoft_native_stat st; + + if (microsoft_native_stat(name, &st) != 0) + { + pg_usleep(100000); + loops++; + continue; + } + } + _dosmaperr(err); return -1; } --------------31154500D5416223148F03B6--