[Twisted-Python] WSGI Thread-management strategy

The strategy used by twisted WSGI, as I understand it, doesn't meet our needs. Currently, a thread is created for each request. The total number of threads is throttled, I gather using a general Twisted thread limit. WSGI applications are called as soon as input headers have been received completely. An application may be called before all body input is received. We need application calls to be delayed until all request input has been received, In an application like Zope, application threads are expensive. There are application resources, such as database connections and caches needed by the application threads. To limit resource consumption, we have to limit the number of application threads. Because the number of threads is limited, application threads are scarce and thus valuable resources that need to be used very efficiently. In particular, we don't want to tie up an application thread waiting for request body input. I propose that the default thread-management strategy should be to delay calling an application until all request input has been received. If this isn't the default, then there should at least be an option to get this behavior. (Of course, the buffering strategy needs to be clever enough to switch to a file when the input gets over some size.) Thoughts? Jim -- Jim Fulton mailto:jim@zope.com Python Powered! CTO (540) 361-1714 http://www.python.org Zope Corporation http://www.zope.com http://www.zope.org

Jim Fulton wrote: ...
In an application like Zope, application threads are expensive.
Of course, threads are expensive for all applications, to some degree. Typically, they require at least a meg of stack space. No big deal of you only have a few, but quire a problem if you had to have one for each acrive request on a busy server. But, of course, y'all know this. That's why you use Twisted. :) Jim -- Jim Fulton mailto:jim@zope.com Python Powered! CTO (540) 361-1714 http://www.python.org Zope Corporation http://www.zope.com http://www.zope.org

(BTW, the correct mailing list for twisted webbish stuff is twisted- web@twistedmatrix.com) On Dec 15, 2005, at 4:22 PM, Jim Fulton wrote:
Sounds sensible, and is doable external to the WSGI wrapper. Here's a little bit I whipped up. (works on the 2.1.x branch and head). Could be smarter, by starting out the buffer in memory and switching to a file if necessary. Also shows off a couple of minor bugs I need to fix. :) def simple_wsgi_app(environ, start_response): print "Starting wsgi app" start_response("200 OK", [('Content-type','text/html; charset=ISO-8859-1')]) data = environ['wsgi.input'].read() return ['<pre>', data, '</pre>'] class Prebuffer(resource.WrapperResource): def hook(self, ctx): req = iweb.IRequest(ctx) temp = tempfile.TemporaryFile() def done(_): temp.seek(0) # Replace the request's stream object with the tempfile req.stream = stream.FileStream(temp) # Hm, this shouldn't be required: req.stream.doStartReading = None return stream.readStream(req.stream, temp.write).addCallback (done) # Oops, fix missing () in lambda in WrapperResource def locateChild(self, ctx, segments): x = self.hook(ctx) if x is not None: return x.addCallback(lambda data: (self.res, segments)) return self.res, segments if __name__ == '__builtin__': from twisted.application import service, strports from twisted.web2 import server, channel res = Prebuffer(wsgi.WSGIResource(simple_wsgi_app)) site = server.Site(res) application = service.Application("demo") s = strports.service('tcp:8080', channel.HTTPFactory(site)) s.setServiceParent(application)

James Y Knight wrote:
(BTW, the correct mailing list for twisted webbish stuff is twisted- web@twistedmatrix.com)
Oops. OK, I'll reply there. :) Jim -- Jim Fulton mailto:jim@zope.com Python Powered! CTO (540) 361-1714 http://www.python.org Zope Corporation http://www.zope.com http://www.zope.org

Jim Fulton wrote: ...
In an application like Zope, application threads are expensive.
Of course, threads are expensive for all applications, to some degree. Typically, they require at least a meg of stack space. No big deal of you only have a few, but quire a problem if you had to have one for each acrive request on a busy server. But, of course, y'all know this. That's why you use Twisted. :) Jim -- Jim Fulton mailto:jim@zope.com Python Powered! CTO (540) 361-1714 http://www.python.org Zope Corporation http://www.zope.com http://www.zope.org

(BTW, the correct mailing list for twisted webbish stuff is twisted- web@twistedmatrix.com) On Dec 15, 2005, at 4:22 PM, Jim Fulton wrote:
Sounds sensible, and is doable external to the WSGI wrapper. Here's a little bit I whipped up. (works on the 2.1.x branch and head). Could be smarter, by starting out the buffer in memory and switching to a file if necessary. Also shows off a couple of minor bugs I need to fix. :) def simple_wsgi_app(environ, start_response): print "Starting wsgi app" start_response("200 OK", [('Content-type','text/html; charset=ISO-8859-1')]) data = environ['wsgi.input'].read() return ['<pre>', data, '</pre>'] class Prebuffer(resource.WrapperResource): def hook(self, ctx): req = iweb.IRequest(ctx) temp = tempfile.TemporaryFile() def done(_): temp.seek(0) # Replace the request's stream object with the tempfile req.stream = stream.FileStream(temp) # Hm, this shouldn't be required: req.stream.doStartReading = None return stream.readStream(req.stream, temp.write).addCallback (done) # Oops, fix missing () in lambda in WrapperResource def locateChild(self, ctx, segments): x = self.hook(ctx) if x is not None: return x.addCallback(lambda data: (self.res, segments)) return self.res, segments if __name__ == '__builtin__': from twisted.application import service, strports from twisted.web2 import server, channel res = Prebuffer(wsgi.WSGIResource(simple_wsgi_app)) site = server.Site(res) application = service.Application("demo") s = strports.service('tcp:8080', channel.HTTPFactory(site)) s.setServiceParent(application)

James Y Knight wrote:
(BTW, the correct mailing list for twisted webbish stuff is twisted- web@twistedmatrix.com)
Oops. OK, I'll reply there. :) Jim -- Jim Fulton mailto:jim@zope.com Python Powered! CTO (540) 361-1714 http://www.python.org Zope Corporation http://www.zope.com http://www.zope.org
participants (2)
-
James Y Knight
-
Jim Fulton