[Zope] Asynchronous DTML

kapil thangavelu k_vertigo@yahoo.com
Thu, 4 Oct 2001 14:48:00 -0700


On Thursday 04 October 2001 07:32 pm, W. Robert Kellock wrote:
> Thanks Chris,
>
> Anybody else able to shed some light on my problem?

there are some cases where blocking a thread can be problematic, talking for 
long periods of times to external servers can limit your throughput.  if 
you're blocking for 20-30s waiting on an external server and you have 4 
threads, and you have 3hits/sec, than most of your users will never see your 
site. 

i've done some experiments with using allowing async calls to be initiated to 
external servers from dtml, its possible, its not pretty and has some caveats 
that means its not for the faint of heart.

also for most of the use cases there might well be an alternate way to 
structure the system that will be better. indeed if you expect alot of this 
type of thing bumping up your thread count and using zeo would be an easy way 
out.

the biggest caveat with using async in zope that gets initiated from a web 
request is you basically lose your transaction (not in the sense of an abort) 
because zope transactions are request associated (well publishing more 
specifically) and thread specific. while your conn to the external server is 
still in asyncore loop, the original web request and publisher will have 
commited the transaction, even if you stopped that, when you enter asyncore 
the thread doing the select poll is a different thread and since transactions 
are keyed to threads you'd get a different transaction. (and its also 
probably not a good idea to do any long post result processing from here as 
holding the medusa thread messes with the dispatching of i/o and publishing 
requests back to zope.) so if you're don't really care about transactions and 
you're not doing any heavy post request processing than sure its doable...

btw. if you want to learn more about (async) network programming i can 
recommend some resources

www.kegel.com/c10k.html
www.nightmare.com # the home of asyncore /medusa
richard stevens network programming # the bible of a network programmer
patterns of software architecture vol II patterns for network and concurrent 
programs

if this hasn't scared you away... well than its probably possible to do the 
whole thing transactionally. jon heinz (sp?) posted  a transaction 
replacement that he was using for integrating with corba that allows more 
explicit transaction control. never said it would be easy...

> If the promises of .NET are realised we should be doing a lot more calling
> out to external servers like this from Zope.

if you're interested in this you should check out the web-services project on 
dev.zope.org, and bug certain people to keep it up to date:)

cheers

kapil thangavelu

> ----- Original Message -----
> From: "Chris McDonough" <chrism@digicool.com>
> To: "W. Robert Kellock" <sales@creditscore.co.nz>
> Sent: Friday, 5 October 2001 14:32
> Subject: Re: [Zope] Asynchronous DTML
>
> > Hmm... not exactly a socket programming guru here.. would you mind
> > sending this to the list?  I wonder if there's a system call somewhere
> > in here that locks a mutex.  I'm not sure but someone else might know.
> >
> > Just for a sanity check, you might want to write an external method like:
> >
> > import thread, time, zLOG
> >
> > def test(self):
> >      ident = thread.get_ident()
> >      zLOG.LOG('test', 0, "starting in %s at %s" % (ident, time.time()))
> >      time.sleep(10)
> >      zLOG.LOG('test', 0, "  ending in %s at %s" % (ident, time.time()))
> >
> > Then fire off a few requests to it to prove to yourself that it's not
> > serializing the calls (if no time periods in the log overlap, it's
> > serializing).  Youc an see the log by starting Zope up like this:
> >
> > ./start STUPID_LOG_FILE=debug.log
> >
> > - C
> >
> > W. Robert Kellock wrote:
> > > The following external method definitely blocks on my RedHat 7.1 Linux
>
> box.
>
> > > Any ideas?
> > >
> > > from socket import *
> > > import time
> > > import string
> > >
> > > def GetBureau(self,host,port,request,terminator):
> > >       s = socket(AF_INET,SOCK_STREAM)
> > >       s.connect((host,port))
> > >       s.setblocking(0) #non-blocking to allow us to time out
> > >       s.send(request)
> > >       start  = time.time()
> > >       data   = ''
> > >       buffer = ''
> > >       cont = 1
> > >       while cont:
> > >        cont = 0
> > >         try:
> > >             buffer = s.recv(256)
> > >        except:
> > >            pass
> > >        if len(buffer):
> > >             cont = 1
> > >             last = string.count(buffer,terminator)
> > >             if (last > 0):
> > >                 cont = 0
> > >                 buffer = buffer[:string.find(buffer,terminator) +
> > > len(terminator)]
> > >             data = data + buffer
> > >             buffer = ''
> > >        else:
> > >            if (start + 60 > time.time()):# time out if no response
> > >                cont = 1
> > >             else:
> > >                if (len(data) > 0):
> > >                    pass
> > >                else:
> > >                    data = ''
> > >       s.close()
> > >       return data
> >
> > -------------------------------------------------------------------------
> >-