You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Dean Gaudet <dg...@arctic.org> on 1997/07/20 06:35:42 UTC

[PATCH] exponential spawning, and child_main tweaks

You could call this optimizing for benchmarks.  Um, you'd be mostly
right... but it does give the user a little hint as to when their various
tunings might be wrong.  With the code that's there already plus this
patch, the server seems to be pretty good at coming up to speed fast, and
staying there, even if the settings are left at the defaults. 

The child_main tweak is to eliminate a system call, and possibly eliminate
another one on arches with scoreboard files (it moves one of the
generation comparisons around, the loop has almost identical semantics).

While debugging this one I found a slight problem in
perform_idle_server_maintenance, which I already checked in the fix for. 

Dean

Index: CHANGES
===================================================================
RCS file: /export/home/cvs/apache/src/CHANGES,v
retrieving revision 1.353
diff -u -r1.353 CHANGES
--- CHANGES	1997/07/19 22:26:55	1.353
+++ CHANGES	1997/07/20 04:28:25
@@ -1,4 +1,14 @@
 Changes with Apache 1.3
+
+  *) child_main avoids an uneeded call to select() when there is only one
+     listening socket.  [Dean Gaudet]
+  
+  *) In the event that the server is starved for idle servers it will
+     spawn 1, then 2, then 4, ..., then 32 servers each second,
+     doubling each second.  It'll also give a warning in the errorlog
+     since the most common reason for this is a poor StartServers
+     setting.  The define MAX_SPAWN_RATE can be used to raise/lower
+     the maximum.  [Dean Gaudet]
   
   *) added transport handle slot (t_handle) to the BUFF structure
      [Doug MacEachern]
Index: http_main.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_main.c,v
retrieving revision 1.182
diff -u -r1.182 http_main.c
--- http_main.c	1997/07/20 04:05:56	1.182
+++ http_main.c	1997/07/20 04:28:32
@@ -1094,7 +1094,7 @@
     
     sync_scoreboard_image();
     new_score_rec = scoreboard_image->servers[child_num];
-    new_score_rec.pid = getpid();
+    new_score_rec.x.pid = getpid();
     old_status = new_score_rec.status;
     new_score_rec.status = status;
 
@@ -1219,7 +1219,7 @@
     int i;
 
     for (i = 0; i < max_daemons_limit; ++i)
-	if (scoreboard_image->servers[i].pid == pid)
+	if (scoreboard_image->servers[i].x.pid == pid)
 	    return i;
 
     return -1;
@@ -1233,7 +1233,7 @@
 
     sync_scoreboard_image();
     for (i = 0; i < max_daemons_limit; ++i) {
-	int pid = scoreboard_image->servers[i].pid;
+	int pid = scoreboard_image->servers[i].x.pid;
 
 	if (pid != my_pid && pid != 0) { 
 	    int waitret = 0,
@@ -1304,7 +1304,7 @@
 
     for (n = 0; n < max_daemons_limit; ++n) {
 	if (scoreboard_image->servers[n].status != SERVER_DEAD
-		&& waitpid (scoreboard_image->servers[n].pid, &status, WNOHANG)
+		&& waitpid (scoreboard_image->servers[n].x.pid, &status, WNOHANG)
 		    == -1
 		&& errno == ECHILD) {
 	    sync_scoreboard_image ();
@@ -1337,7 +1337,7 @@
             if(scoreboard_image->servers[pi].status != SERVER_DEAD)
             {
                 e[hi] = pi;
-                h[hi++] = (HANDLE)scoreboard_image->servers[pi].pid;
+                h[hi++] = (HANDLE)scoreboard_image->servers[pi].x.pid;
             }
 
         }
@@ -1347,9 +1347,9 @@
             if(rv == -1)
                 err = GetLastError();
             if((WAIT_OBJECT_0 <= (unsigned int)rv) && ((unsigned int)rv < (WAIT_OBJECT_0 + hi)))
-                return(scoreboard_image->servers[e[rv - WAIT_OBJECT_0]].pid);
+                return(scoreboard_image->servers[e[rv - WAIT_OBJECT_0]].x.pid);
             else if((WAIT_ABANDONED_0 <= (unsigned int)rv) && ((unsigned int)rv < (WAIT_ABANDONED_0 + hi)))
-                return(scoreboard_image->servers[e[rv - WAIT_ABANDONED_0]].pid);
+                return(scoreboard_image->servers[e[rv - WAIT_ABANDONED_0]].x.pid);
 
         }
     }
@@ -2131,7 +2131,6 @@
 #endif    
 
     while (1) {
-	int errsave;
 	BUFF *conn_io;
 	request_rec *r;
       
@@ -2171,24 +2170,24 @@
         accept_mutex_on();  /* Lock around "accept", if necessary */
 
         for (;;) {
-            memcpy(&main_fds, &listenfds, sizeof(fd_set));
-            srv = ap_select(listenmaxfd+1, &main_fds, NULL, NULL, NULL);
-            errsave = errno;
-
-            sync_scoreboard_image();
-            if (scoreboard_image->global.exit_generation >= generation)
-                exit(0);
-
-            errno = errsave;
-            if (srv < 0 && errno != EINTR)
-                log_unixerr("select", "(listen)", NULL, server_conf);
-
-            if (srv <= 0)
-                continue;
-
-	    lr = find_ready_listener(&main_fds);
-	    if (lr == NULL) continue;
-	    sd = lr->fd;
+	    if (listeners->next != listeners) {
+		/* more than one socket */
+		memcpy(&main_fds, &listenfds, sizeof(fd_set));
+		srv = ap_select(listenmaxfd+1, &main_fds, NULL, NULL, NULL);
+
+		if (srv < 0 && errno != EINTR)
+		    log_unixerr("select", "(listen)", NULL, server_conf);
+
+		if (srv <= 0)
+		    continue;
+
+		lr = find_ready_listener(&main_fds);
+		if (lr == NULL) continue;
+		sd = lr->fd;
+	    } else {
+		/* there's only one socket, just pretend we the other stuff */
+		sd = listeners->fd;
+	    }
 
 	    /* if we accept() something we don't want to die, so we have to
 	     * defer the exit
@@ -2225,6 +2224,12 @@
 		/* ok maybe not, see ya later */
 		exit (0);
 	    }
+	    /* or maybe we missed a signal, you never know on systems
+	     * without reliable signals
+	     */
+	    sync_scoreboard_image();
+	    if (scoreboard_image->global.exit_generation >= generation)
+		exit(0);
         }
 
         accept_mutex_off(); /* unlock after "accept" */
@@ -2406,7 +2411,7 @@
      * to the same word.)
      * XXX: this needs to be sync'd to disk in the non shared memory stuff
      */
-    scoreboard_image->servers[child_num].pid = pid;
+    scoreboard_image->servers[child_num].x.pid = pid;
 
     return 0;
 }
@@ -2428,17 +2433,34 @@
     }
 }
 
+/*
+ * idle_spawn_rate is the number of children that will be spawned on the
+ * next maintenance cycle if there aren't enough idle servers.  It is
+ * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
+ * without the need to spawn.
+ */
+static int idle_spawn_rate = 1;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE	(32)
+#endif
 
 static void perform_idle_server_maintenance ()
 {
     int i;
     int to_kill;
-    int free_slot;
     int idle_count;
+    int free_head;
+    int *free_ptr;
+    int free_length;
+
+    /* initialize the free_list */
+    free_head = -1;
+    free_ptr = &free_head;
+    free_length = 0;
 
-    free_slot = -1;
     to_kill = -1;
     idle_count = 0;
+
     sync_scoreboard_image ();
     for (i = 0; i < daemons_limit; ++i) {
 	switch (scoreboard_image->servers[i].status) {
@@ -2454,8 +2476,10 @@
 	    break;
 	case SERVER_DEAD:
 	    /* try to keep children numbers as low as possible */
-	    if (free_slot == -1) {
-		free_slot = i;
+	    if (free_length < idle_spawn_rate) {
+		*free_ptr = i;
+		free_ptr = &scoreboard_image->servers[i].x.free_list;
+		++free_length;
 	    }
 	    break;
 	}
@@ -2465,9 +2489,12 @@
 	 * shut down gracefully, in case it happened to pick up a request
 	 * while we were counting
 	 */
-	kill (scoreboard_image->servers[to_kill].pid, SIGUSR1);
+	kill (scoreboard_image->servers[to_kill].x.pid, SIGUSR1);
+	idle_spawn_rate = 1;
     } else if (idle_count < daemons_min_free) {
-	if (free_slot == -1) {
+	/* terminate the free list */
+	*free_ptr = -1;
+	if (free_head == -1) {
 	    /* only report this condition once */
 	    static int reported = 0;
 
@@ -2478,8 +2505,28 @@
 		reported = 1;
 	    }
 	} else {
-	    make_child (server_conf, free_slot);
+	    if (idle_spawn_rate > 1) {
+		log_printf (server_conf,
+		    "server seems busy, spawning %d children (you may need "
+		    "to increase StartServers, or Min/MaxSpareServers)",
+		    idle_spawn_rate);
+	    }
+	    i = 0;
+	    while (i < idle_spawn_rate && free_head != -1) {
+		int slot = free_head;
+		free_head = scoreboard_image->servers[free_head].x.free_list;
+		make_child (server_conf, slot);
+		++i;
+	    }
+	    /* the next time around we want to spawn twice as many if this
+	     * wasn't good enough
+	     */
+	    if (idle_spawn_rate < MAX_SPAWN_RATE) {
+		idle_spawn_rate *= 2;
+	    }
 	}
+    } else {
+	idle_spawn_rate = 1;
     }
 }
 
Index: mod_status.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_status.c,v
retrieving revision 1.53
diff -u -r1.53 mod_status.c
--- mod_status.c	1997/07/17 22:27:41	1.53
+++ mod_status.c	1997/07/20 04:28:34
@@ -472,7 +472,7 @@
 			 i,(int)conn_lres,my_lres,lres);
 		    else
 			rprintf(r,"<b>Server %d</b> (%d): %d|%lu|%lu [",
-			 i,(int)score_record.pid,(int)conn_lres,my_lres,lres);
+			 i,(int)score_record.x.pid,(int)conn_lres,my_lres,lres);
 
 		    switch (score_record.status)
 		    {
@@ -537,7 +537,7 @@
 			 i,(int)conn_lres,my_lres,lres);
 		    else
 			rprintf(r,"<tr><td><b>%d</b><td>%d<td>%d/%lu/%lu",
-			 i,(int)score_record.pid,(int)conn_lres,my_lres,lres);
+			 i,(int)score_record.x.pid,(int)conn_lres,my_lres,lres);
 
 		    switch (score_record.status)
 		    {
Index: scoreboard.h
===================================================================
RCS file: /export/home/cvs/apache/src/scoreboard.h,v
retrieving revision 1.25
diff -u -r1.25 scoreboard.h
--- scoreboard.h	1997/07/15 21:39:58	1.25
+++ scoreboard.h	1997/07/20 04:28:35
@@ -76,8 +76,11 @@
 #define SERVER_GRACEFUL 8	/* server is gracefully finishing request */
 
 typedef struct {
-    pid_t pid;
-    char status;
+    union {
+	pid_t pid;		/* if it's not DEAD then this is the pid */
+	int free_list;		/* otherwise this is scratch space */
+    } x;
+    int status;
 #if defined(STATUS)
     unsigned long access_count;
     unsigned long bytes_served;