static void processCancelRequest(Port *port, void *pkt);
static int initMasks(fd_set *rmask);
static void report_fork_failure_to_client(Port *port, int errnum);
-static CAC_state canAcceptConnections(void);
+static CAC_state canAcceptConnections(int backend_type);
static long PostmasterRandom(void);
static void RandomSalt(char *md5Salt);
static void signal_child(pid_t pid, int signal);
}
/*
- * canAcceptConnections --- check to see if database state allows connections.
+ * canAcceptConnections --- check to see if database state allows connections
+ * of the specified type. backend_type can be BACKEND_TYPE_NORMAL,
+ * BACKEND_TYPE_AUTOVAC, or BACKEND_TYPE_BGWORKER. (Note that we don't yet
+ * know whether a NORMAL connection might turn into a walsender.)
*/
static CAC_state
-canAcceptConnections(void)
+canAcceptConnections(int backend_type)
{
CAC_state result = CAC_OK;
/*
* Can't start backends when in startup/shutdown/inconsistent recovery
- * state.
+ * state. We treat autovac workers the same as user backends for this
+ * purpose. However, bgworkers are excluded from this test; we expect
+ * bgworker_should_start_now() decided whether the DB state allows them.
*
* In state PM_WAIT_BACKUP only superusers can connect (this must be
* allowed so that a superuser can end online backup mode); we return
* that neither CAC_OK nor CAC_WAITBACKUP can safely be returned until we
* have checked for too many children.
*/
- if (pmState != PM_RUN)
+ if (pmState != PM_RUN &&
+ backend_type != BACKEND_TYPE_BGWORKER)
{
if (pmState == PM_WAIT_BACKUP)
result = CAC_WAITBACKUP; /* allow superusers only */
/*
* Don't start too many children.
*
- * We allow more connections than we can have backends here because some
+ * We allow more connections here than we can have backends because some
* might still be authenticating; they might fail auth, or some existing
- * backend might exit before the auth cycle is completed. The exact
+ * backend might exit before the auth cycle is completed. The exact
* MaxBackends limit is enforced when a new backend tries to join the
* shared-inval backend array.
*
bn->cancel_key = MyCancelKey;
/* Pass down canAcceptConnections state */
- port->canAcceptConnections = canAcceptConnections();
+ port->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
bn->dead_end = (port->canAcceptConnections != CAC_OK &&
port->canAcceptConnections != CAC_WAITBACKUP);
* we have to check to avoid race-condition problems during DB state
* changes.
*/
- if (canAcceptConnections() == CAC_OK)
+ if (canAcceptConnections(BACKEND_TYPE_AUTOVAC) == CAC_OK)
{
bn = (Backend *) malloc(sizeof(Backend));
if (bn)
/*
* Allocate and assign the Backend element. Note we must do this before
- * forking, so that we can handle out of memory properly.
+ * forking, so that we can handle failures (out of memory or child-process
+ * slots) cleanly.
*
* Treat failure as though the worker had crashed. That way, the
- * postmaster will wait a bit before attempting to start it again; if it
- * tried again right away, most likely it'd find itself repeating the
- * out-of-memory or fork failure condition.
+ * postmaster will wait a bit before attempting to start it again; if we
+ * tried again right away, most likely we'd find ourselves hitting the
+ * same resource-exhaustion condition.
*/
if (!assign_backendlist_entry(rw))
{
static bool
assign_backendlist_entry(RegisteredBgWorker *rw)
{
- Backend *bn = malloc(sizeof(Backend));
+ Backend *bn;
+
+ /*
+ * Check that database state allows another connection. Currently the
+ * only possible failure is CAC_TOOMANY, so we just log an error message
+ * based on that rather than checking the error code precisely.
+ */
+ if (canAcceptConnections(BACKEND_TYPE_BGWORKER) != CAC_OK)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+ errmsg("no slot available for new worker process")));
+ return false;
+ }
+ bn = malloc(sizeof(Backend));
if (bn == NULL)
{
ereport(LOG,