Graceful degrading on bad Beaker session

131 views
Skip to first unread message

Theron Luhn

unread,
Feb 15, 2014, 2:34:03 PM2/15/14
to pylons-...@googlegroups.com
I'm using file-based Beaker sessions.  If something happens to the session file, (for example, if I delete the directory on my development machine), I get a 500 error:

  File "[dir]/lib/python2.7/site-packages/beaker/session.py", line 659, in __getattr__
    return getattr(self._session(), attr)
  File "[dir]/lib/python2.7/site-packages/beaker/session.py", line 655, in _session
    **params)
  File "[dir]/lib/python2.7/site-packages/beaker/session.py", line 162, in __init__
    self.load()
  File "[dir]/lib/python2.7/site-packages/beaker/session.py", line 329, in load
    self.namespace.acquire_read_lock()
  File "[dir]/lib/python2.7/site-packages/beaker/container.py", line 211, in acquire_read_lock
    self.access_lock.acquire_read_lock()
  File "[dir]/lib/python2.7/site-packages/beaker/synchronization.py", line 154, in acquire_read_lock
    x = self.do_acquire_read_lock(wait)
  File "[dir]/lib/python2.7/site-packages/beaker/synchronization.py", line 241, in do_acquire_read_lock
    filedescriptor = self._open(os.O_CREAT | os.O_RDONLY)
  File "[dir]/lib/python2.7/site-packages/beaker/synchronization.py", line 236, in _open
    filedescriptor = os.open(self.filename, mode)
OSError: [Errno 2] No such file or directory: '[dir]/data/sessions/lock/e/e9/e953f617010ebeee3990cce9eb345c584e5fcb96.lock'

This will be a problem in production:  I'll have a standby webserver waiting to take over in case of failure on the primary server.  If the standby were to take over, it won't have the session data and all users with sessions will be getting 500 errors.  How can I have it fail more gracefully, and just create a new, empty session?

Mike Orr

unread,
Feb 16, 2014, 1:06:08 PM2/16/14
to pylons-...@googlegroups.com
On Sat, Feb 15, 2014 at 11:34 AM, Theron Luhn <the...@luhn.com> wrote:
> I'm using file-based Beaker sessions. If something happens to the session
> file, (for example, if I delete the directory on my development machine), I
> get a 500 error:
>
> '[dir]/data/sessions/lock/e/e9/e953f617010ebeee3990cce9eb345c584e5fcb96.lock'

I suspect the problem is missing parent directories rather than the
session files themselves. Beaker probably creates the parent
directories only on startup, and doesn't expect them to be deleted
while the application is running. I generally have to make the 'data'
directory myself or the application won't start. So the thing to do
would be to either have your failover routine (re)start the
application on the standby server, or modify the source to use
os.makedirs instead of os.mkdir.

Beaker is not maintained anymore. The development version of Pyramid
is recommending pyramid_redis_sessions instead, which is the only
fully-maintained backend at this time. I was unhappy with this because
I was about to put an application into production that's using Beaker
file-based sessions (which I've never had problems with). But later we
decided we might use Redis on a wider scope, for caching and usage
statistics, so pyramid_redis_sessions would be a good way to start
gaining experience with it. I installed it last week, using a
self-compiled Redis, and it worked flawlessly. I'm going to put it in
production next week. I'm still using PostgreSQL as the primary
database.

Longer-term, somebody needs to write a replacement for Beaker, or
'pyramid_*' packages for the various backends. I've started looking
into it but I can't commit to it right now. The development version of
Pyramid has gotten more building blocks to write such backends with.

We also need some HOWTOs on setting up non-backend sessions; e.g.,
with regular database tables. I started trying that but got bogged
down in creating and managing the session ID, which Beaker does but
Pyramid doesn't support internally. I needed session IDs not only to
link the data to the user, but also to log with the request to count
the sessions per day, their length and entry/exit pages. It was enough
extra work that I went back to Beaker.

The fundamental problem with Beaker is these lock files. It's using an
obsolete protocol borrowed from Perl::Mason that's more hassle than
it's worth. Beaker's successor, Dogpile, doesn't use this kind of lock
file. But Dogpile's author is not recommending it for sessions, which
I didn't expect. In any case, there's no Pyramid adapter yet for
Dogpile, that does the backend-neutral configuration like
pyramid_beaker does, so it's not an option without that.

Theron Luhn

unread,
Feb 16, 2014, 1:36:51 PM2/16/14
to pylons-...@googlegroups.com
Thanks for letting me know.  I'll look into pyramid_redis_sessions.


— Theron



--
You received this message because you are subscribed to a topic in the Google Groups "pylons-discuss" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/pylons-discuss/9zDnLK15Wks/unsubscribe.
To unsubscribe from this group and all its topics, send an email to pylons-discus...@googlegroups.com.
To post to this group, send email to pylons-...@googlegroups.com.
Visit this group at http://groups.google.com/group/pylons-discuss.
For more options, visit https://groups.google.com/groups/opt_out.

Jeff Dairiki

unread,
Feb 16, 2014, 6:35:53 PM2/16/14
to Theron Luhn, pylons-...@googlegroups.com
On Sun, Feb 16, 2014 at 12:36:51PM -0600, Theron Luhn wrote:
> Thanks for letting me know. I'll look into pyramid_redis_sessions.

Not meaning to fan a smoldering thread here, but, if cookie-based
session storage (where all the data goes in the cookie and there is
no server-side storage of session data) will work in your use case,
that is probably the most painless way to go.

Pyramid provides for cookie-based session storage natively. Pyramid
1.5 provides SignedCookieSessionFactory for this.

http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/sessions.html

Older pyramids provide UnencryptedCookieSessionFactoryConfig:

http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/narr/sessions.html

No redis required. When your server fails-over, not only will there
(hopefully) be no exceptions thrown, but there will be no session
data lost, since its all stored client-side.

More context here:

https://groups.google.com/forum/#!searchin/pylons-discuss/session|sort:date/pylons-discuss/RQjev5QXBqc/Xf1pUHXl1-0J

Jeff

Bert JW Regeer

unread,
Feb 16, 2014, 6:37:25 PM2/16/14
to pylons-...@googlegroups.com, Theron Luhn
The biggest thing to remember is to keep the secret used the same across both instances, so that when the failover occurs the other server can correctly verify that the data has not been tampered with.

Bert

Jonathan Vanasco

unread,
Feb 17, 2014, 10:52:23 AM2/17/14
to pylons-...@googlegroups.com
When did Mike recommend against using Dogpile for sessions ?

It was my understanding that he simply didn't believe in integrating Cookie + Cache , as it created a lot of design issues.  In the release notes (http://techspot.zzzeek.org/2011/10/01/thoughts-on-beaker/) he doesn't like the idea of "classic" server side sessions of a Key accessing a blog , and recommends an encrypted cookie to key into a database model or caching system , but puts it out there that someone else can feel free to build a caching system on top of beaker.


Mike Orr

unread,
Feb 17, 2014, 12:19:40 PM2/17/14
to pylons-...@googlegroups.com
On Mon, Feb 17, 2014 at 7:52 AM, Jonathan Vanasco <jona...@findmeon.com> wrote:
> When did Mike recommend against using Dogpile for sessions ?

That was my interpretation of the comment you cited a month or so ago.

http://techspot.zzzeek.org/2012/04/19/using-beaker-for-caching-why-you-ll-want-to-switch-to-dogpile.cache/#comment-532502543

Jonathan Vanasco

unread,
Feb 17, 2014, 6:05:07 PM2/17/14
to pylons-...@googlegroups.com
Ah.  I didn't interpret any of his stuff like that.

Dogpile is just the locking / storage mechanism.    It could safely be used to manage a session backend.  

The question is, would you rather:

a- The Session ID is the key to some abstract blob on the server ( which is what Beaker does, what PHP and most other frameworks handle )
b- The Session ID is an encrypted key to some datastore.

I use A, because I'm lazy.  I'd prefer B -- and have a shared session for users across all devices -- except I often need "A" style functionality ( ie, "flash" messages and short status ) 

Mike Orr

unread,
Feb 17, 2014, 6:46:28 PM2/17/14
to pylons-...@googlegroups.com
"B" just needs some cookbook examples. And I think Pyramid's session
infrastructure needs to support session IDs. Not as a requirement for
all backends, but as support for the backends that want it so they
don't all have to reinvent the wheel. I'd like the 'session.id'
attribute to be standard, although that may be too much. (Currently,
my code uses 'getattr(session, "id", None)' to determine the session
ID and whether the backend supports that kind of ID. Perhaps it should
be 'session["id"]'; I'm not sure, but Beaker uses 'session.id' and I
think pyramid_redis_sessions does (?).

The two problems I had with implementing B was, one, figuring out how
Beaker manages the session ID, and two, the number of subtables I'd
have to add. Session data tends to me more hierarchical than regular
model data in my experience. For searches I have to store the criteria
(multiple fields), the result IDs (a list), flash messages (a list or
type:list queues). So my two-table application would explode to five
or six tables, plus configuring all the relationship attributes. I
started to do it but it got so complicated I went back to Beaker
because it's working (and I'm now heading toward
pyramid_redis_sessions).

Jonathan Vanasco

unread,
Feb 18, 2014, 2:00:46 PM2/18/14
to pylons-...@googlegroups.com


"B" just needs some cookbook examples. And I think Pyramid's session
infrastructure needs to support session IDs. Not as a requirement for
all backends, but as support for the backends that want it so they
don't all have to reinvent the wheel. I'd like the 'session.id'
attribute to be standard, although that may be too much. (Currently,
my code uses 'getattr(session, "id", None)' to determine the session
ID and whether the backend supports that kind of ID. Perhaps it should
be 'session["id"]'; I'm not sure, but Beaker uses 'session.id' and I
think pyramid_redis_sessions does (?).

+100

I created an issue 12 months ago, which was rejected and closed -- https://github.com/Pylons/pyramid/issues/895

ISession needs to support proxying the session id from backends that support it.

The two problems I had with implementing B was, one, figuring out how
Beaker manages the session ID, and two, the number of subtables I'd
have to add. Session data tends to me more hierarchical than regular
model data in my experience. For searches I have to store the criteria
(multiple fields), the result IDs (a list), flash messages (a list or
type:list queues). So my two-table application would explode to five
or six tables, plus configuring all the relationship attributes. I
started to do it but it got so complicated I went back to Beaker
because it's working (and I'm now heading toward
pyramid_redis_sessions).

Well another strategy would be to do everything in a single table.  Personally I wouldn't want to have so many tables.

Imagine if you had an encrypted cookie that contained this (and had a timestamp to time out after 30 minutes )

     user_id : INT for authenticated user, NULL for no user
     session_id : random hash

 (pseudocode):

    class SessionDataUser(sqlalchemy):
           session_id INT
           session_data TEXT

    class SessionDataFlow(sqlalchemy):
           session_id INT
           session_data TEXT

If you have a user_id , you pickle/unpickle "SessionDataUser", which can be used across devices.  this lets you precalculate expensive data only once per user and persist info across browsers.

For both users, you pickle/unpickle "SessionDataFlow" , which just has flash messages or search data, etc ; stuff that is relevant to the current flow




 

Mike Orr

unread,
Feb 19, 2014, 9:55:27 AM2/19/14
to pylons-...@googlegroups.com
On Tue, Feb 18, 2014 at 11:00 AM, Jonathan Vanasco
<jona...@findmeon.com> wrote:
>
>
>> "B" just needs some cookbook examples. And I think Pyramid's session
>> infrastructure needs to support session IDs. Not as a requirement for
>> all backends, but as support for the backends that want it so they
>> don't all have to reinvent the wheel. I'd like the 'session.id'
>> attribute to be standard, although that may be too much. (Currently,
>> my code uses 'getattr(session, "id", None)' to determine the session
>> ID and whether the backend supports that kind of ID. Perhaps it should
>> be 'session["id"]'; I'm not sure, but Beaker uses 'session.id' and I
>> think pyramid_redis_sessions does (?).
>
>
> +100
>
> I created an issue 12 months ago, which was rejected and closed --
> https://github.com/Pylons/pyramid/issues/895
>
> ISession needs to support proxying the session id from backends that support
> it.

Pyramid needs *something*. I'm not sure what yet. I think it needs a
function to generate a session ID, and instrunctions about how to
manage the ID. I'm not sure if that needs to be in ISession itself.
'signed_serialize()' is just a function, There could also be an
optional method on BaseCookieSessionFactory or
SignedCookieSessionFactory, because you can't really have sessions
without the ID in a cookie.

Although it would be nice to have an .id attribute on ISession; then
backends that don't support ID's could just set it to None. Then
callers would know definitively that there's no session ID.

Jonathan Vanasco

unread,
Feb 19, 2014, 3:54:04 PM2/19/14
to pylons-...@googlegroups.com
I think you'd need to have an `id` in ISession as an attribute.

You'd want it to default to 'None', then explicitly provide it in backends as class attribute set on __init__ or a property method.  this way all pyramid session objects have an 'id' attribute, but it can map to different values (if any) in the session storage

Client side cookies would return `None` as the id.  Server-side cookies would return the session value.  

problem is that i don't know how /if interfaces can have defaults , or what happens if you don't provide one.

some initial ideas i had were around this...

pyramid/interfaces.py:

    class ISession(IDict):
        ...
        id = Attribute('String id.  If not ``None``, the session has an id.')
        ...

pyramid/session.py -- and various backends --

    def __init__(self):
       self.id = foo

    or

        @property
        def id(self):
            return self['_id']


Jeff Dairiki

unread,
Feb 19, 2014, 4:43:00 PM2/19/14
to pylons-...@googlegroups.com
On Wed, Feb 19, 2014 at 12:54:04PM -0800, Jonathan Vanasco wrote:
> I think you'd need to have an `id` in ISession as an attribute.

If I understand your motives — it's quite possible that I don't — you
want access to the session id in order to access extra "externally
stored session data" (e.g. from SQL) which is not handled
automatically by whatever implementation of ISession you are using.

In that case, why does your external storage key have to be the same
as the ISession implementation's internal id (if indeed there is one)?
Why not just generate your own key for the external storage, and save
that in the session as a normal session variable?

Jonathan Vanasco

unread,
Feb 19, 2014, 5:12:14 PM2/19/14
to pylons-...@googlegroups.com
ISession doesn't know or care about "ids", as Pyramid officially only has "Client-Side Sessions", which are id-less.

If you're using a server-side ID, the id is the full value of the cookie. The ways to find that out are:

- inspect the cookies
- inspect the session dict, hope that it has the "session id" in it -- however this can be stored differently depending on the sessioning provider, so you can have a mess of code if you have different providers on production/staging/deployment.

You absolutely don't need the session_id to handle this.  It's largely a convenience method.  

But, and large 'but' here -- this is really needed during unit/integrated-testing and troubleshooting of server-side sessions.  In my experience, it is really messy/ugly code to get around this limitation.  

I don't think there would be a use case for having a ServerSide session (which has an id) and needing to query that id outside of an automatic iSession implementation. I think anything storing data based on that ID would be handled within the __init__ ; and anything stored in other 'tables' would be keyed to an identifier within a client or server session.

For me, this has really always been about testing.  Server Side sessions are essentially a "black box". I'd love to be able to access the corresponding cookie values to see the id or encrypted/signed payload easily.  it's a complete pain in the ass to do so otherwise.

Jonathan Vanasco

unread,
Feb 19, 2014, 6:01:24 PM2/19/14
to pylons-...@googlegroups.com
here's a gist of the idea i had ; it works for pyramid_beaker 


I'm not sure how to handle the client-side cookies, as they have two cookie value states:

   cookie_value_in
   cookie_value_out
 
for server-side cookies, the cookie_value_in and out are always the same as the id. ( though if you change ids, you have a new session object -- do you need to know about the old one ? )

for client-side cookies, your cookie_out will be different than cookie_in because of edits and timestamps.

in any event, being about to inspect a Session object and be able to know the Cookie Name, the "session id" and the various values is really useful and often necessary during testing.  it would be great if these were just properties of the Session objects.

Mike Orr

unread,
Feb 20, 2014, 10:36:23 PM2/20/14
to pylons-...@googlegroups.com
On Wed, Feb 19, 2014 at 1:43 PM, Jeff Dairiki <dai...@dairiki.org> wrote:
> On Wed, Feb 19, 2014 at 12:54:04PM -0800, Jonathan Vanasco wrote:
>> I think you'd need to have an `id` in ISession as an attribute.
>
> If I understand your motives -- it's quite possible that I don't -- you
> want access to the session id in order to access extra "externally
> stored session data" (e.g. from SQL) which is not handled
> automatically by whatever implementation of ISession you are using.
>
> In that case, why does your external storage key have to be the same
> as the ISession implementation's internal id (if indeed there is one)?
> Why not just generate your own key for the external storage, and save
> that in the session as a normal session variable?

That could work, actually. I need something in my access log to
indicate which requests form a session, and I was using the session ID
because that's what it's for. But I could generate a different ID if
my key isn't present, and that could have some advantages. I could
make it int and autoincrement which would me more convenient than a
random string, and it would also give the user an extra level of
privacy. So thanks for the idea.
Reply all
Reply to author
Forward
0 new messages