Thread
Commits
-
Fix initial sync of slot parent directory when restoring status
- 113020627235 9.4.20 landed
- 02b1b01d8ed4 9.5.15 landed
- d8030c684207 9.6.11 landed
- 504f059f5a19 10.6 landed
- 680f89e5613c 11.0 landed
- caa0c6ceba1f 12.0 landed
-
Bug in slot.c and are replication slots ever used at Window?
Konstantin Knizhnik <k.knizhnik@postgrespro.ru> — 2018-08-30T08:00:43Z
Hi hackers, I am really confused. If my conclusions are correct, then nobody ever tried to use replication slots at Windows! The function RestoreSlotFromDisk in slot.c contains the following code: static void RestoreSlotFromDisk(const char *name) { ReplicationSlotOnDisk cp; int i; char path[MAXPGPATH + 22]; int fd; bool restored = false; int readBytes; pg_crc32c checksum; /* no need to lock here, no concurrent access allowed yet */ /* delete temp file if it exists */ sprintf(path, "pg_replslot/%s/state.tmp", name); if (unlink(path) < 0 && errno != ENOENT) ereport(PANIC, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", path))); sprintf(path, "pg_replslot/%s/state", name); elog(DEBUG1, "restoring replication slot from \"%s\"", path); fd = OpenTransientFile(path, O_RDWR | PG_BINARY); /* * We do not need to handle this as we are rename()ing the directory into * place only after we fsync()ed the state file. */ if (fd < 0) ereport(PANIC, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", path))); /* * Sync state file before we're reading from it. We might have crashed * while it wasn't synced yet and we shouldn't continue on that basis. */ pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_RESTORE_SYNC); if (pg_fsync(fd) != 0) { int save_errno = errno; CloseTransientFile(fd); errno = save_errno; ereport(PANIC, (errcode_for_file_access(), errmsg("could not fsync file \"%s\": %m", path))); } pgstat_report_wait_end(); /* Also sync the parent directory */ START_CRIT_SECTION(); fsync_fname(path, true); END_CRIT_SECTION(); ... Please notice that fsync_fname with comment "also sync parent directory" is called for path of the file! fsync_fname in turn does the following: /* * fsync_fname -- Try to fsync a file or directory * * Ignores errors trying to open unreadable files, or trying to fsync * directories on systems where that isn't allowed/required. Reports * other errors non-fatally. */ int fsync_fname(const char *fname, bool isdir, const char *progname) { int fd; int flags; int returncode; /* * Some OSs require directories to be opened read-only whereas other * systems don't allow us to fsync files opened read-only; so we need both * cases here. Using O_RDWR will cause us to fail to fsync files that are * not writable by our userid, but we assume that's OK. */ flags = PG_BINARY; if (!isdir) flags |= O_RDWR; else flags |= O_RDONLY; /* * Open the file, silently ignoring errors about unreadable files (or * unsupported operations, e.g. opening a directory under Windows), and * logging others. */ fd = open(fname, flags); if (fd < 0) { if (errno == EACCES || (isdir && errno == EISDIR)) return 0; fprintf(stderr, _("%s: could not open file \"%s\": %s\n"), progname, fname, strerror(errno)); return -1; } returncode = fsync(fd); So if "isdir" is true (and it is true in this case), it sets O_RDONLY flag. Then fsync_fname successfully opens slot file in readonly mode and calls fsync() which at windows is substituted with _commit() which in turn is wrapper for FlushFileBuffers. Finally FlushFileBuffers returns ERROR_ACCESS_DENINED which cause assertion failure in _commit: if ( !FlushFileBuffers((HANDLE)_get_osfhandle(filedes)) ) { retval = GetLastError(); } else { retval = 0; /* return success */ } /* map the OS return code to C errno value and return code */ if (retval == 0) goto good; _doserrno = retval; } errno = EBADF; retval = -1; _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0)); I think that the problem happen only with debug version of postgres. Release version will just return error in this case which is silently ignored by RestoreSlotFromDisk function. I think that bug fix is trivial: we just need to use fsync_parent_path instead of fsync_fname in RestoreSlotFromDisk. -- Konstantin Knizhnik Postgres Professional: http://www.postgrespro.com The Russian Postgres Company -
Re: Bug in slot.c and are replication slots ever used at Window?
Michael Paquier <michael@paquier.xyz> — 2018-08-30T18:27:31Z
On Thu, Aug 30, 2018 at 11:00:43AM +0300, Konstantin Knizhnik wrote: > So if "isdir" is true (and it is true in this case), it sets O_RDONLY > flag. Then fsync_fname successfully opens slot file in readonly mode > and calls fsync() which at windows is substituted with _commit() which > in turn is wrapper for FlushFileBuffers. It seems to me that you are right here, "path" points to pg_replslot/$SLOTNAME/state which is a file so the fsync is incorrect. I am not sure that we'd want to publish fsync_parent_path out of fd.c though, so perhaps we could just save the slot path in a different variable and use it? -- Michael
-
Re: Bug in slot.c and are replication slots ever used at Window?
Michael Paquier <michael@paquier.xyz> — 2018-08-31T18:46:47Z
Hi Konstantin, On Thu, Aug 30, 2018 at 11:27:31AM -0700, Michael Paquier wrote: > It seems to me that you are right here, "path" points to > pg_replslot/$SLOTNAME/state which is a file so the fsync is incorrect. > I am not sure that we'd want to publish fsync_parent_path out of fd.c > though, so perhaps we could just save the slot path in a different > variable and use it? I have spent more time on this bug, and the code path you have pointed at is the only one having such an issue. Attached is a patch to fix the problem. It includes the sanity checks I have used to check all code paths calling fsync_fname() for both the frontend and the backend code. The checks will not be included in the final fix, still they look useful so I am planning to start a new thread on the matter as perhaps other folks have more and/or better ideas. -- Michael
-
Re: Bug in slot.c and are replication slots ever used at Window?
Michael Paquier <michael@paquier.xyz> — 2018-09-02T19:44:31Z
On Fri, Aug 31, 2018 at 11:46:47AM -0700, Michael Paquier wrote: > The checks will not be included in the final fix, still they look useful > so I am planning to start a new thread on the matter as perhaps other > folks have more and/or better ideas. Pushed the fix down to 9.4, without the extra sanity checks. -- Michael