server bootstrapping upon connection (WARNING: LONG)

Fortepianissimo fortepianissimo at yahoo.com.tw
Tue Feb 10 11:54:36 EST 2004


Here is the situation: I want my server started up upon connection.
When the first connection comes in, the server is not running. The
client realizes the fact, and then starts up the server and tries to
connect again. This of course all happens on the same machine (local
connection only).

The connections can come in as fast as 30+/sec, so the server is
threaded (using SocketServer.ThreadingTCPServer). Further, the server
initialization is threaded into two: one is to do the real, lengthy
setup, the other is start lisenting ASAP.

The problem: I need to prevent multiple copies of the server being
started. I did this by using file locking (fcntl.lockf()). However,
not every time the code successfully prevented the server being
started up more than one time. Here is the relevant code:

---- CLIENT CODE SNIPPET STARTS ----
from fooConsts import *

import socket,fcntl,os,sys,time,logging

FOO_CLIENT_DEBUG=1

POLL_SERVER_INTVL=0.5

if FOO_CLIENT_DEBUG:
    myPID=os.getpid()
    logger = logging.getLogger('FOO Client')
    hdlr = logging.FileHandler(TMP_PATH+'fooClient.log')
    formatter = logging.Formatter('%(asctime)s %(message)s')
    hdlr.setFormatter(formatter)
    logger.addHandler(hdlr) 
    logger.setLevel(logging.INFO)

    def log (info):
        logger.info('%d: %s'%(myPID,info))


class FooClient:
    def __init__ (self, startServer=True):
        """Connects to FooServer if it exists, otherwise starts it and
connects to it"""
        self.connected=True
        if self.connect(): return
        elif not startServer:
            if FOO_CLIENT_DEBUG: log('connection failed 1')
            self.connected=False
            return

        # try to obtain the right to start a server; if we can't,
someone else
        # must be doing it - try to reconnect
        try:
            if FOO_CLIENT_DEBUG: log('try to get right to start
server')
            serverStartLock=open(TMP_PATH+'serverStart.lock','w')
            fcntl.lockf(serverStartLock.fileno(),fcntl.LOCK_EX|fcntl.LOCK_NB)
        except:
            if FOO_CLIENT_DEBUG: log('someone else is doing it; wait
2')
            while 1:
                time.sleep(POLL_SERVER_INTVL)
                if self.connect(): return

        # safe to start a server and connect to it
        if FOO_CLIENT_DEBUG: log('start server')
        exitCode=os.system('python -OO "%sfooServer.py" &'%FOO_PATH)
        if exitCode==0:
            if FOO_CLIENT_DEBUG: log('server is being started; wait
3')
            while 1:
                time.sleep(POLL_SERVER_INTVL)
                if self.connect(): return
        else:
            if FOO_CLIENT_DEBUG: log('sever bootstrapping failed')
            self.connected=False
            raise "Cannot start FOOServer"
    
    def connect (self):
        """Attempts to connect to FOOServer from PORT_MIN to
PORT_MAX"""
        if FOO_CLIENT_DEBUG: log('connection attempt')
        port=PORT_MIN
        while port<=PORT_MAX:
            try:
                self.socket=socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
                self.socket.connect(('',port))
                break
            except:
                self.socket.close()
                port+=1

        if port>PORT_MAX:
            if FOO_CLIENT_DEBUG: log('connection failed 2')
            return False
        if FOO_CLIENT_DEBUG: log('connection succeeded at port
%d'%port)
        return True
...

FooClient()
---- CLIENT CODE SNIPPET ENDS ----

>From the log (when problem occurred) I see even *AFTER* the server was
started and accepted connections (several connections came and went
happily), a connection would come in and hit the "connection failed 1"
log line. This shouldn't have happened as the default value of
startServer for FooClient.__init__() is True. In the very same
incident, FooServer was started twice, but in the log there's no trace
of two "start server" lines, but only the "connection failed 1" line
in the very end of the log.

I realize this is a rather complex question to ask for help, but I'm
really at wits end. Feel free to skip the detailed description, and
just throw me some general tips and hints. Thank you VERY MUCH!



More information about the Python-list mailing list