Frage

I have deployed a website on Google-App-Engine with Python.

Since GAE does not guarantee "keep-alive", I have implemented a stateless server:

  1. Upon every change in internal variables, they are immediately stored into GQL data-base.

  2. Whenever the process is started, all internal variables are loaded from GQL data-base.

I have a scenario which rarely throws an exception, and I have not been able to track it down:

  • Client sends a synchronous AJAX POST request.

  • Server creates a session and sends a unique session-ID in the response.

  • Client sends a synchronous AJAX GET request with the session-ID as an argument.

  • Server sends some text message in the response.

Since the client requests are synchronous, the entire sequence is also synchronous.

Here is the relevant mapping within my server:

from webapp2 import WSGIApplication
from Handler import MyRequestHandler

app = WSGIApplication([
    ('/request1'    ,MyRequestHandler), # post request
    ('/request2(.*)',MyRequestHandler), # get request
])

Here is the relevant request-handling within my server:

from webapp2 import RequestHandler
from Server  import MyServer

myServer = MyServer()

class MyRequestHandler(RequestHandler):
    def post(self):
        try:
            if self.request.path.startswith('/request1'):
                sessionId = myServer.GetNewSessionId()
                self.SendContent('text/plain',sessionId)
        except Exception,error:
            self.SendError(error)
    def get(self,sessionId):
        try:
            if self.request.path.startswith('/request2'):
                textMessage = myServer.GetMsg(sessionId)
                self.SendContent('text/plain',textMessage)
        except Exception,error:
            self.SendError(error)
    def SendContent(self,contentType,contentData):
        self.response.set_status(200)
        self.response.headers['content-type'] = contentType
        self.response.headers['cache-control'] = 'no-cache'
        self.response.write(contentData)
    def SendError(self,error):
        self.response.set_status(500)
        self.response.write(error.message)

Here is the internal implementation of my server:

class MyServer():
    def __init__(self):
        self.sessions = SessionsTable.ReadSessions()
    def GetNewSessionId(self):
        while True:
            sessionId = ... # a 16-digit random number
            if SessionsTable.ReserveSession(sessionId):
                self.sessions[sessionId] = ... # a text message
                SessionsTable.WriteSession(self.sessions,sessionId)
                return sessionId
    def GetMsg(self,sessionId):
        return self.sessions[sessionId]

And finally, here is the data-base maintenance within my server:

from google.appengine.ext import db

class SessionsTable(db.Model):
    message = db.TextProperty()
    @staticmethod
    def ReadSessions():
        sessions = {}
        for session in SessionsTable.all():
            sessions[session.key().name()] = session.message
        return sessions
    @staticmethod
    @db.transactional
    def ReserveSession(sessionId):
        if not SessionsTable.get_by_key_name(sessionId):
            SessionsTable(key_name=sessionId,message='').put()
            return True
        return False
    @staticmethod
    def WriteSession(sessions,sessionId):
        SessionsTable(key_name=sessionId,message=sessions[sessionId]).put()
    @staticmethod
    def EraseSession(sessionId):
        SessionsTable.get_by_key_name(sessionId).delete()

The exception itself indicates an illegal access to the sessions dictionary using the sessionId key. From my observation, it occurs only when the client-server sequence described at the beginning of this question is initiated after the server "has been sleeping" for a relatively long period of time (like a few days or so). It may provide some sort of clue to the source of this problem, though I am unable to see it.

My questions:

  1. Is there anything visibly wrong with my design?

  2. Has anyone experienced a similar problem on GAE?

  3. Does anyone see an obvious solution, or even a debugging method that might help to understand this problem?

Thanks

War es hilfreich?

Lösung

You are wrongly assuming that all requests are handled by the same instance. This isn't at all the case: GAE, like most hosting enviroments, makes no guarantee about which server process will handle any request.

Your implementation is using a module-level variable, myServer, which is a class instance with its own instance variables. But each server process will have its own instance of myServer, and they're not shared between processes: so a dictionary entry that's created in one request won't necessarily exist in a second.

You'll need to look at ways of persisting this data across instances. Really, that's what the datastore is for in first place; if you're worried about overhead, you should investigate using memcache.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top