[Web-SIG] Bill's comments on WSGI draft 1.4
Phillip J. Eby
pje at telecommunity.com
Mon Sep 6 15:38:13 CEST 2004
At 01:36 PM 9/2/04 -0700, Robert Brewer wrote:
>Phillip J. Eby wrote:
> > > I'd like to at least hear the rationale behind
> > > favoring iterables so heavily over write().
> >
> > One important reason: the server can suspend an iterable's execution
> > without tying up a thread. It can therefore potentially use
> > a much smaller thread pool to handle a given number of connections,
> > because the threads are only tied up while they're executing an
> > iterator 'next()' call.
> >
> > By contrast, 'write()' occurs *within* the application execution,
> > so the only way to suspend execution is to suspend the thread (e.g.
> > waiting for a lock).
>
>Hmm. I still don't get it--why would the server not simply "suspend
>execution" of the framework within the write() call? In my naive
>estimation, it would be the difference between:
>
>for chunk in framework.data:
> output(chunk)
> do_out_of_band_stuff()
>
>..and:
>
>def write(chunk):
> output(chunk)
> do_out_of_band_stuff()
Because now you've moved the server code into the application thread; many
Python web servers (pretty much all of the async ones including Medusa,
Twisted, and ZServer) have a single thread for all I/O operations, distinct
from the threads that run application requests.
So, if you want to perform I/O from an app thread, you need lock
synchronization code that didn't exist before... and the design rapidly
becomes more complicated.
Anyway, such servers' write() methods will probably look more like:
def write(self,data):
self.output_queue.put(data)
and they'll then return to the caller. However, this has new issues of its
own: specifically, if a program transmits a large file, it will consume
lots of memory if it produces data faster than the client can accept
it. (Because the output queue will back up.)
Of course, one can throttle the output queue to some set maximum size, but
then you end up right where I began this discussion: the application thread
has to hang, tying up that thread's availability until the app's execution
is complete, and thus reducing the concurrent request throughput of the server.
If, however, the application is structured as an iterable, these problems
all go away. Application threads are only tied up for computation, not
waiting for I/O, output a client isn't going to receive is never produced,
large memory buffers aren't needed, and so on.
So, on purely technical grounds, the iterable approach is immensely
superior; it should be used wherever practical to do so.
>..and in fact, I see most existing servers having to do both when they
>grow WSGI interfaces, since both are allowed in the WSGI spec (even if
>one is deprecated).
Yes, servers will have to support both; but it should be understood that
for many important servers (especially ones written in Python) that
applications using 'write()' may have detrimental effects on the server's
overall throughput, even if the application seems to run quite well on say,
a local connection to an unloaded server.
So, that's why people should be discouraged from using 'write()' outside of
necessity.
>Maybe you could add a line or two of pseudocode to
>help me understand...? (Assuming you're not fleeing for your life from
>hurricanes, that is ;)
Hurricane's past me now; I just got power and 'net back this morning.
More information about the Web-SIG
mailing list