Am 16.06.2005 um 22:13 schrieb Stephen Deasey:


It was at one point implemented with mmap:

http://cvs.sourceforge.net/viewcvs.py/aolserver/aolserver/nsd/ driver.c?rev=1.34&view=markup


Aha! But it was taken out for some reason...
It would be good to know why?


Spooling large requests to disk is clearly necessary.  Almost always,
a large request needs to be stored in a file server-side anyway, so it
might as well go there from the start.

The only problem I see is that the calls to open a file and write
content to disk are blocking, and they happen in the context of the
driver thread which is busy multiplexing many non-blocking operations
on sockets.  Should one of the calls used to spool to disk block,
everything comes to a stop.

Oh yes. This is very right.


The version 3.x model was to read-ahead up to the end of headers in
the driver thread, then pass the conn to a conn thread.  As far as I
remember, the conn thread would then spool all content to disk, but
this could have been on-demand and it might have been only file-upload
data. A programmer could then access the spooled data using
Ns_ConnRead(), Ns_ConnReadLine() etc., or they would be called for you
by ns_getform etc.  The thing to note however is that all the blocking
calls happened in separate conn threads.

Yes. I remember this because the multipart data was parsed by a
Tcl script. Not very speedy but it worked surprisingly stable.


In early version 4.0 the model was changed so that the driver thread
would read-ahead all data (up to Content-Length bytes) before the conn
was passed to a conn thread.  In a point release a limit was
introduced to avoid the obvious DOS attack.  This allowed an easier
interface to the data for programmers: Ns_ConnContent() which returns
a pointer to an array of bytes.  Ns_ConnReadLine() etc. are no longer
used and are currently broken.

But this brought the memory bloat as a side-effect
(you can make all happy) :-)


Version 4.1 work seems to be trying to tackle the problem of what
happens when large files are uploaded.  The version 4.0 model work
extremely well for HTTP POST data up to a few K in size, but as you've
noticed it really bloats memory when multi-megabyte files are
uploaded.  This version also introduces limits, which are a
URL-specific way of pre-declaring the maxupload size and some other
parameters.

But still, just spools the data into temp-file at the very sensitive
point (in the driver.c) as you already pointed-out above.



So anyway, here's my idea of how it should work:

I'm all ears :-)


There's a maxreadahead parameter which is <= maxinput.  When a request
arrives with Content-Length > 0 && < maxinput, maxreadahead bytes are
read into a buffer by the driver thread before the conn is passed to a
conn thread.

The conn thread runs the registered proc for that URL.  If that
procedure does not try to acces the content, then when the conn is
returned to the driver thread any content > maxreadahead is drained.

If the registered proc does try to access the content via e.g.
Ns_ConnContent() (and I think this would be the main method, used
underneath by all others) and the content is <= maxreadahead, then a
pointer to the readahead buffer is returned.

If the content is accessed and it is > maxreadahead, a temp file is
mmaped, the readahead buffer is dumped to it, and the remaining bytes
are read from the socket, possibly blocking many times, and dumped to
the mmaped file, again possibly blocking.  A call to Ns_ConnContent()
returns a pointer to the mmaped bytes of the open file.

But this is now happening outside the driver and in the connection
thread, ok.


At any time before the registered proc asks for the content it can
check the content length header and decide whether it is too large to
accept.  You could imagine setting a low global maxinput, and a call
such as Ns_ConnSetMaxInput() which a registered proc could call to
increase the limit for that connection only.  The advantage over the
limits scheme in 4.1 is that the code which checks the size of the
content and processes it is kept together, rather than having to
pre-decalare maxinput sizes for arbitrary URLs in the config file.

Hm... this is the only part I do not understand.


This is similar to 4.1 except the task of overflowing to disk is moved
to the conn thread and is lazy.  The mental model is also slightly
different, the assumption is that content goes to disk, but theres a
cache for read-ahead which may be enough to hold everything.  In 4.1
it's everything is read into memory, unless it's large in which case
it overflows to disk.


How does that sound?


Summary (my understanding):

This is a kind of marriage between the 3.0 and 4.0 strategies as I see.
Up to maxreadahead, it is behaving like 4.0 (loads all in memory) and
above maxreadahead it behaves like 3.0 (spools to disk).
The most significant fact is that potentially blocking calls while spooling
to disk are taken out of the driver and done in the connection thread
(where it does not matter that much) and that the process is lazy
(done only when content is actually requested and not in advance).

Yes, this sound fine, indeed. The only think I'd have yet to
understand is the twiddling with the maxinput value on per-connection
basis. I do not know *when* this would happen. Can you give a simple
practical example?


Zoran


Reply via email to