Asynchronous Updating of Session

63 views
Skip to first unread message

Matthew Henderson

unread,
Jul 19, 2013, 2:12:15 PM7/19/13
to we...@googlegroups.com
Hi,

In my application I would like AJAX invoked GETs and POSTs to edit the state of the application by editing attributes in the session.  I'm having trouble though when the requests overlap, meaning not all changes to the state are saved.

Below is a minimal example to reproduce the problem. First visit /, then /status to see that only one of the session attributes is updated, not both. /status shows for me "state1 = 1 state2 = 0"

Thanks in advance for any help,

Matt

import web, time

urls = (
    '/', 'index',
    '/1', 'one',
    '/2', 'two',
    '/status', 'status',
)
web.config.debug = False

app = web.application(urls, globals())
session = web.session.Session(app, web.session.DiskStore('sessions'))



class one(object):
    def GET(self):
        session.state1 = 1
        time.sleep(1)
        return "done"
    
class two(object):
    def GET(self):
        session.state2 = 1
        return "done"
    

class index(object):
    def GET(self):
        session["state1"] = 0
        session["state2"] = 0
        return """
                <iframe src="/1"></iframe><iframe src="/2"></iframe><a href="/status">status</a>
            """
        
class status(object):
    def GET(self):
        out = ""
        if "state1" in session.keys() :
            out += "state1 = " + str(session["state1"])
        else :
            out += "state1 not set"
        if "state2" in session.keys() :
            out += " state2 = " + str(session["state2"])
        else :
            out += "state2 not set"
        return out
        
    


if __name__ == '__main__':    
    app.run()

Matthew Henderson

unread,
Jul 19, 2013, 8:27:42 PM7/19/13
to we...@googlegroups.com
One way to get the behaviour I want is to add to the top if index's GET:

        if "uid" not in session.keys() :
            session["uid"] = uuid.uuid4().hex
        states[session["uid"]] = {}

then use states[session["uid"]]["state1"] and states[session["uid"]]["state2"]  instead of session["state1"] and session["state2"]

I suppose this means the state is always held in memory, and it won't be cleared out when the session expires.
Any better ideas?

Matthew Henderson

unread,
Jul 20, 2013, 8:06:18 AM7/20/13
to we...@googlegroups.com
Another solution I've found is to implement a DiskStore which gets a lock on the pickled object before reading it, and releases the lock when saving:



class LockingDiskStore(web.session.Store):
    """
    Store which uses lock files
    """
    def __init__(self, root):
        # if the storage root doesn't exists, create it.
        if not os.path.exists(root):
            os.makedirs(
                    os.path.abspath(root)
                    )
        self.root = root

    def _get_path(self, key):
        if os.path.sep in key: 
            raise ValueError, "Bad key: %s" % repr(key)
        return os.path.join(self.root, key)
    
    def __contains__(self, key):
        path = self._get_path(key)
        return os.path.exists(path)

    def __getitem__(self, key):
        print "read ", key
        lockfile = self._lockfile(key)
        dt = 0
        while os.path.exists(lockfile) and dt < 30: # 30 second time out
            print "waiting for lock to release"
            time.sleep(1)
            dt += 1
        f = open(lockfile, "w")
        f.write("lock")
        f.close()
        path = self._get_path(key)
        if os.path.exists(path): 
            pickled = open(path).read()
            return self.decode(pickled)
        else:
            raise KeyError, key

    def __setitem__(self, key, value):
        print "write ", key
        lockfile = self._lockfile(key)
        if os.path.exists(lockfile) :
            print "deleting lockfile"
            os.remove(lockfile)
        path = self._get_path(key)
        pickled = self.encode(value)    
        try:
            f = open(path, 'w')
            try:
                f.write(pickled)
            finally: 
                f.close()
        except IOError:
            pass

    def __delitem__(self, key):
        path = self._get_path(key)
        if os.path.exists(path):
            os.remove(path)
    
    def _lockfile(self, key):
        return self._get_path(key) + ".lock"
    
    
    def cleanup(self, timeout):
        now = time.time()
        for f in os.listdir(self.root):
            path = self._get_path(f)
            atime = os.stat(path).st_atime
            if now - atime > timeout :
                os.remove(path)



--
You received this message because you are subscribed to a topic in the Google Groups "web.py" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/webpy/w-0iD-NyJVg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to webpy+un...@googlegroups.com.
To post to this group, send email to we...@googlegroups.com.
Visit this group at http://groups.google.com/group/webpy.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Reply all
Reply to author
Forward
0 new messages