rlucas has asked for the wisdom of the Perl Monks concerning the following question:

CGI::Session is very handy, but when running under mod_perl, possible clobbering of data can occur, because you can have many different in-memory copies (in different interpreters) that all reference the same session ID on disk, but aren't in sync. This is especially true in race-type conditions. The DESTROY method of the objects performs a flush to disk which can be confounding, and which I would like to prevent.

For example:

  1. Bob comes to mod_perlish site and gets a cookie with his session ID.
  2. Bob tries to view a users-only page, but is negged because his CGI::Session does not list him as authorized.
  3. Bob logs in, and his CGI::Session now lists him as authorized.
  4. Bob is confused because at some point in the future he seems arbitrarily to be kicked out of the site.
What happened to Bob? The CGI::Session object from his earlier, unauthorized page view, got DESTROY'ed. When it was destroyed, the information in it was flushed to disk, clobbering the disk version of his session and therefore showing him as unauthorized.

What the programmer should do is make sure that as the last part of each request, the CGI::Session object is flushed and closed. However, 1. anomalous things happen that can sometimes prevent normal cleanup / teardown in applications, and 2. really long request times (think major db calls) might result in a second request before the first one is done and cleanup run. Plus, programmers are Lazy.

So, what I believe would help this is to be able to override the DESTROY method for existing objects without changing the source -- essentially, prevent them from running DESTROY, so that they don't flush to disk without my explicit approval.

  • Comment on Judiciously avoiding DESTROY method for CGI::Session clobber prevention

Replies are listed 'Best First'.
Re: Judiciously avoiding DESTROY method for CGI::Session clobber prevention
by jasonk (Parson) on Jun 07, 2005 at 21:35 UTC

    So, what I believe would help this is to be able to override the DESTROY method for existing objects without changing the source -- essentially, prevent them from running DESTROY, so that they don't flush to disk without my explicit approval.

    Ok, so what happened when you tried that? There isn't anything stopping you from overriding the DESTROY method using somethin like use CGI::Session; sub CGI::Session::DESTROY { }. Of course a better solution would be to switch to something that is intended to work with mod_perl, such as Apache::Session.


    We're not surrounded, we're in a target-rich environment!
      That's certainly doable. But I'd like to be able to take $instance and vanquish $instance without having to change the class code; for example, I'd like to avoid the possibility of nonorthogonality to other possible uses of the CGI::Session code in my mod_perl instance.

      I have the inkling there may be something like this in the guts of Perl's OO code.

        There is, if you are sure it's what you want.
        my $instance; ... if (want to disable destroy) { package My::Private::DESTROYless::CGI::Session; sub DESTROY { } @ISA = 'CGI::Session'; bless $instance; }
Re: Judiciously avoiding DESTROY method for CGI::Session clobber prevention
by perrin (Chancellor) on Jun 08, 2005 at 01:58 UTC
    You've got it backwards -- if you don't let the session object get destroyed, no other process will ever be able to see the changes you made. The session has to get destroyed in order for the data to be saved.

    If you truly need exclusive locking (not very likely, but some session uses need it), you should look at Apache::Session instead, which provides that.

      Hmm, yes and no. CGI::Session has a manual flush() method, and I have the habit of calling a flush to disk after a set of changes. Plus, my teardown methods in my CGI::Apps do normally close out and flush the session. So the DESTROY flush is bad here, because I'm already flushing when I want it, so DESTROY's flush is when I don't.

      I agree with you and jasonk that Apache::Session is generally better suited for concurrency, although I really like a lot of things about CGI::Session (specifically, the ability to have Data::Dumper serialization and /tmp file storage -- very transparent for debugging).

      Furthermore, although locking would be the full solution here, my app does not require it, and I would view that as a significant negative (due to possible blocking). All of the "hard" transactional stuff I'm doing is done with real DB transactions -- the session stuff is fairly "soft."

        The right answer is to make sure it always gets destroyed, not to prevent it from getting destroyed. Otherwise, you can always have changes that have not been synced.

        If you really want to do things differently, you can subclass it and change the DESTROY method to not flush. You should make it check for unsaved changes and at least send a warning to the log though.