1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Do a direct probe during postmaster startup to determine the maximum

number of openable files and the number already opened.  This eliminates
depending on sysconf(_SC_OPEN_MAX), and allows much saner behavior on
platforms where open-file slots are used up by semaphores.
This commit is contained in:
Tom Lane
2004-02-23 20:45:59 +00:00
parent b4c8dcb294
commit f83356c7f5
5 changed files with 165 additions and 109 deletions

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.106 2004/01/26 22:35:32 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.107 2004/02/23 20:45:59 tgl Exp $
*
* NOTES:
*
@ -54,41 +54,50 @@
/*
* Problem: Postgres does a system(ld...) to do dynamic loading.
* This will open several extra files in addition to those used by
* Postgres. We need to guarantee that there are file descriptors free
* for ld to use.
* We must leave some file descriptors free for system(), the dynamic loader,
* and other code that tries to open files without consulting fd.c. This
* is the number left free. (While we can be pretty sure we won't get
* EMFILE, there's never any guarantee that we won't get ENFILE due to
* other processes chewing up FDs. So it's a bad idea to try to open files
* without consulting fd.c. Nonetheless we cannot control all code.)
*
* The current solution is to limit the number of file descriptors
* that this code will allocate at one time: it leaves RESERVE_FOR_LD free.
*
* (Even though most dynamic loaders now use dlopen(3) or the
* equivalent, the OS must still open several files to perform the
* dynamic loading. And stdin/stdout/stderr count too. Keep this here.)
* Because this is just a fixed setting, we are effectively assuming that
* no such code will leave FDs open over the long term; otherwise the slop
* is likely to be insufficient. Note in particular that we expect that
* loading a shared library does not result in any permanent increase in
* the number of open files. (This appears to be true on most if not
* all platforms as of Feb 2004.)
*/
#ifndef RESERVE_FOR_LD
#define RESERVE_FOR_LD 10
#endif
#define NUM_RESERVED_FDS 10
/*
* We need to ensure that we have at least some file descriptors
* available to postgreSQL after we've reserved the ones for LD,
* so we set that value here.
*
* I think 10 is an appropriate value so that's what it'll be
* for now.
* If we have fewer than this many usable FDs after allowing for the reserved
* ones, choke.
*/
#ifndef FD_MINFREE
#define FD_MINFREE 10
#endif
#define FD_MINFREE 10
/*
* A number of platforms return values for sysconf(_SC_OPEN_MAX) that are
* far beyond what they can really support. This GUC parameter limits what
* we will believe.
* A number of platforms allow individual processes to open many more files
* than they can really support when *many* processes do the same thing.
* This GUC parameter lets the DBA limit max_safe_fds to something less than
* what the postmaster's initial probe suggests will work.
*/
int max_files_per_process = 1000;
/*
* Maximum number of file descriptors to open for either VFD entries or
* AllocateFile files. This is initialized to a conservative value, and
* remains that way indefinitely in bootstrap or standalone-backend cases.
* In normal postmaster operation, the postmaster calls set_max_safe_fds()
* late in initialization to update the value, and that value is then
* inherited by forked subprocesses.
*
* Note: the value of max_files_per_process is taken into account while
* setting this variable, and so need not be tested separately.
*/
static int max_safe_fds = 32; /* default if not changed */
/* Debugging.... */
@ -199,7 +208,6 @@ static void FreeVfd(File file);
static int FileAccess(File file);
static File fileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
static char *filepath(const char *filename);
static long pg_nofile(void);
static void AtProcExit_Files(int code, Datum arg);
static void CleanupTempFiles(bool isProcExit);
@ -236,6 +244,105 @@ pg_fdatasync(int fd)
return 0;
}
/*
* count_usable_fds --- count how many FDs the system will let us open,
* and estimate how many are already open.
*
* We assume stdin (FD 0) is available for dup'ing
*/
static void
count_usable_fds(int *usable_fds, int *already_open)
{
int *fd;
int size;
int used = 0;
int highestfd = 0;
int j;
size = 1024;
fd = (int *) palloc(size * sizeof(int));
/* dup until failure ... */
for (;;)
{
int thisfd;
thisfd = dup(0);
if (thisfd < 0)
{
/* Expect EMFILE or ENFILE, else it's fishy */
if (errno != EMFILE && errno != ENFILE)
elog(WARNING, "dup(0) failed after %d successes: %m", used);
break;
}
if (used >= size)
{
size *= 2;
fd = (int *) repalloc(fd, size * sizeof(int));
}
fd[used++] = thisfd;
if (highestfd < thisfd)
highestfd = thisfd;
}
/* release the files we opened */
for (j = 0; j < used; j++)
close(fd[j]);
pfree(fd);
/*
* Return results. usable_fds is just the number of successful dups.
* We assume that the system limit is highestfd+1 (remember 0 is a legal
* FD number) and so already_open is highestfd+1 - usable_fds.
*/
*usable_fds = used;
*already_open = highestfd+1 - used;
}
/*
* set_max_safe_fds
* Determine number of filedescriptors that fd.c is allowed to use
*/
void
set_max_safe_fds(void)
{
int usable_fds;
int already_open;
/*
* We want to set max_safe_fds to
* MIN(usable_fds, max_files_per_process - already_open)
* less the slop factor for files that are opened without consulting
* fd.c. This ensures that we won't exceed either max_files_per_process
* or the experimentally-determined EMFILE limit.
*/
count_usable_fds(&usable_fds, &already_open);
max_safe_fds = Min(usable_fds, max_files_per_process - already_open);
/*
* Take off the FDs reserved for system() etc.
*/
max_safe_fds -= NUM_RESERVED_FDS;
/*
* Make sure we still have enough to get by.
*/
if (max_safe_fds < FD_MINFREE)
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("insufficient file descriptors available to start server process"),
errdetail("System allows %d, we need at least %d.",
max_safe_fds + NUM_RESERVED_FDS,
FD_MINFREE + NUM_RESERVED_FDS)));
elog(DEBUG2, "max_safe_fds = %d, usable_fds = %d, already_open = %d",
max_safe_fds, usable_fds, already_open);
}
/*
* BasicOpenFile --- same as open(2) except can free other FDs if needed
*
@ -279,63 +386,6 @@ tryAgain:
return -1; /* failure */
}
/*
* pg_nofile: determine number of filedescriptors that fd.c is allowed to use
*/
static long
pg_nofile(void)
{
static long no_files = 0;
/* need do this calculation only once */
if (no_files == 0)
{
/*
* Ask the system what its files-per-process limit is.
*/
#ifdef HAVE_SYSCONF
no_files = sysconf(_SC_OPEN_MAX);
if (no_files <= 0)
{
#ifdef NOFILE
no_files = (long) NOFILE;
#else
no_files = (long) max_files_per_process;
#endif
elog(LOG, "sysconf(_SC_OPEN_MAX) failed; using %ld",
no_files);
}
#else /* !HAVE_SYSCONF */
#ifdef NOFILE
no_files = (long) NOFILE;
#else
no_files = (long) max_files_per_process;
#endif
#endif /* HAVE_SYSCONF */
/*
* Some platforms return hopelessly optimistic values. Apply a
* configurable upper limit.
*/
if (no_files > (long) max_files_per_process)
no_files = (long) max_files_per_process;
/*
* Make sure we have enough to get by after reserving some for LD.
*/
if ((no_files - RESERVE_FOR_LD) < FD_MINFREE)
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("insufficient file descriptors available to start server process"),
errdetail("System allows %ld, we need at least %d.",
no_files, RESERVE_FOR_LD + FD_MINFREE)));
no_files -= RESERVE_FOR_LD;
}
return no_files;
}
#if defined(FDDEBUG)
static void
@ -439,7 +489,7 @@ LruInsert(File file)
if (FileIsNotOpen(file))
{
while (nfile + numAllocatedFiles >= pg_nofile())
while (nfile + numAllocatedFiles >= max_safe_fds)
{
if (!ReleaseLruFile())
break;
@ -698,7 +748,7 @@ fileNameOpenFile(FileName fileName,
file = AllocateVfd();
vfdP = &VfdCache[file];
while (nfile + numAllocatedFiles >= pg_nofile())
while (nfile + numAllocatedFiles >= max_safe_fds)
{
if (!ReleaseLruFile())
break;
@ -1042,7 +1092,14 @@ AllocateFile(char *name, char *mode)
DO_DB(elog(LOG, "AllocateFile: Allocated %d", numAllocatedFiles));
if (numAllocatedFiles >= MAX_ALLOCATED_FILES)
/*
* The test against MAX_ALLOCATED_FILES prevents us from overflowing
* allocatedFiles[]; the test against max_safe_fds prevents AllocateFile
* from hogging every one of the available FDs, which'd lead to infinite
* looping.
*/
if (numAllocatedFiles >= MAX_ALLOCATED_FILES ||
numAllocatedFiles >= max_safe_fds - 1)
elog(ERROR, "too many private FDs demanded");
TryAgain: