On Fri, Jun 05, 2015 at 05:15:09PM -0500, Matthew Martin wrote:
> >Synopsis:    Serving large files with httpd eats fscktons of memory.
> >Category:    system
> >Environment:
>       System      : OpenBSD 5.7
>       Details     : OpenBSD 5.7 (GENERIC) #0: Thu Apr 30 22:01:01 CEST 2015
>                        
> r...@stable-57-amd64.mtier.org:/binpatchng/work-binpatch57-amd64/src/sys/arch/amd64/compile/GENERIC
> 
>       Architecture: OpenBSD.amd64
>       Machine     : amd64
> >Description:
> A couple of people concurrently downloading iso sized files can drag
> the server into using all of swap (there's only a gig) in a
> frighteningly short time.
> 
> Revision 1.30 of
> http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.sbin/httpd/server_file.c
> had message "Adjust the read/write watermarks according to the TCP
> send buffer. This fixes sending of large files. Previously, httpd was
> reading the input file too quickly and could run out of memory when
> filling the input buffer."
> 
> That pretty well describes the situation I'm seeing now with 5.7
> (-RELEASE + m:tier binpatches). It also happens with -CURRENT (as of
> Jun 5) httpd compiled and slapped in its place.
> 
> >How-To-Repeat:
> Something along the lines of
> hostA# dd if=/dev/zero of=/var/www/htdocs/test_512.dat bs=1M count=512
> hostB$ ftp http://hostA/test_512.dat
> 
> >Fix:
> I've attempted various settings of 'tcp socket buffer' and shotgunning
> around the lines modified in 1.30 to no avail. Trying to limit its
> consumption via login.conf just makes it die when it hits the limit.

the problem is that we have to bufferevents.  clt->clt_bev which is
handleing the accept(2)ed socket and clt->clt_srvbev which is
handleing the fd of the served file.

it goes like this:
clt_bev: Here I am, brain the size of a planet, and they ask me to serve
         a file.
clt_srvbev: Whee, I can read clt_sndbufsiz bytes from the file. Hello
            clt_bev, here, have some data.
clt_bev: Ok, I can't send it right now, but I have a buffer, I'll put
         the data at the end.
[last message repeated n times]
clt_bev: Phew, I can send some data to the client, hopfully clt_bev
         stops to send me data soon.

and so on... In the end the whole iso ends up in clt_bev.

I'm not sure yet about the 32 and 256 magic numbers, they seem to it
some kind of sweet spot on *my* system. Make them to small and
everything slows down, make them to large and you waste memory.

Please try this:

diff --git server.c server.c
index ca67a47..22b47b8 100644
--- server.c
+++ server.c
@@ -704,7 +704,7 @@ server_input(struct client *clt)
 
        /* Adjust write watermark to the socket buffer output size */
        bufferevent_setwatermark(clt->clt_bev, EV_WRITE,
-           clt->clt_sndbufsiz, 0);
+           32 * clt->clt_sndbufsiz, 0);
        /* Read at most amount of data that fits in one fcgi record. */
        bufferevent_setwatermark(clt->clt_bev, EV_READ, 0, FCGI_CONTENT_SIZE);
 
@@ -729,6 +729,10 @@ server_write(struct bufferevent *bev, void *arg)
                goto done;
 
        bufferevent_enable(bev, EV_READ);
+
+       if (clt->clt_srvbev && !(clt->clt_srvbev->enabled & EV_READ))
+               bufferevent_enable(clt->clt_srvbev, EV_READ);
+
        return;
  done:
        (*bev->errorcb)(bev, EVBUFFER_WRITE|EVBUFFER_EOF, bev->cbarg);
@@ -769,6 +773,11 @@ server_read(struct bufferevent *bev, void *arg)
                goto fail;
        if (clt->clt_done)
                goto done;
+
+       if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(clt->clt_bev)) > 256 *
+           clt->clt_sndbufsiz)
+               bufferevent_disable(bev, EV_READ);
+
        return;
  done:
        (*bev->errorcb)(bev, EVBUFFER_READ|EVBUFFER_EOF, bev->cbarg);


-- 
I'm not entirely sure you are real.

Reply via email to