A PHP session handler with a Mongo DB backend.
- PHP version 5.2.17+
- PHP MongoDB driver version 1.3+.
- A sense of adventure.
Pretty simple, actually. First, include
MongoSession class like so:
Then you need to configure it. The most basic config looks like this:
MongoSession::config(array( 'connection' => 'mongodb://localhost:27017', 'db' => 'theDbName', 'cookie_domain' => '.mydomain.com' ));
.mydomain.com with your domain and change the connection string to any
valid MongoDB connection string as described here.
Now, when you're ready, just call:
You can pass
init(…) the first time it runs so that you create the indices, but that's it.
You can pass options to MongoClient() via
connection_opts in config.
Ex, connect to replicaSet and define timeout:
MongoSession::config(array( 'connection' => 'mongodb://mongo1:27017,mongo2:27017', 'connection_opts' => array( 'replicaSet' =>'rs1', 'connecttimeoutMS' =>5000, ), 'cookie_domain' => '.mydomain.com', 'db' => 'theDbName' );
Sessions are hard
Sessions are something we take for granted in PHP since they just work straight out of the box. You call
session_start() and things just work well and quickly. Done.
Ok, now that you're done developing on a single-machine architecture, let's briefly discuss multi-machine environments. You can no longer rely on the tried-and-true file-based session management from PHP. Time to use a centralized system.
So why not an existing session handler built on MySQL? Well, 95% of the features in MySQL aren't necessary for session handling. MongoDB gives you exactly what you need and at a significant performance boost. Less overhead is good, right?
The most important point I'd like to make after having dealt with a lot of pain of multi-server architectures is that there are many variables that affect how your application will now work.
PHP file-based sessions rely on file locks which are inherently released after a process completes,
regardless of whether or not the process crashed,
exited, or ran out of memory. This makes file-based
sessions very reliable.
Unlike file-based sessions, using a data store like MongoDB requires the application to deal with the
locks. If your script runs out of memory and PHP crashes without the application releasing the lock,
you could end up with a session that's completely locked out to the user. I've been able to partially
replicate this problem on AWS EC2 Micro instances with a memory hungry app. Occasional crashes would
occur under heavy load causing PHP not to finish the session with
close() which would
invoking the unlock mechanism.
You can't test this type of behavior in a unit test. You must test your entire application under load to ensure that you're running sufficient resources so that your application is running stable.
If you're going to try running session handling on a single MongoDB instance, you're most likely asking for trouble. Each application obviously has its own requirements and load so you need to analyze this. However, running a replica set with 3 or more machines is a good place to start.
By default this class will require acknowledged writes from the primary only. You can configure the write concern as desired using the 'write_concern' configuration option. For example, set 'write_concern' to 2 to require acknowledgment from your primary and one secondary.
You must not pass an integer 'write_concern' value as a string or it will be interpreted as a replica tag set.
By default this class does not require journaling before acknowleding the write. You can override this behavior and require writes are journaled before ack'd by setting the 'write_journal' boolean. When set to true, Mongo will flush the write to disk from memory before acknowledging the write.
Why, oh why?
If you've examined the code, I'm sure you have a few questions.
(Q) Why is this PHP 5.2 compatible? Nothing cool in PHP ever existed prior to 5.3
(A) You're preaching to the choir, but it's a legacy app thing. I'll make it 5.4 flavored eventually.
(Q) WHERE are your unit tests?
(This is a question @grmpyprogrammer is surely asking right about now…)
(A) I'm working on it, but PHP sessions are inherently difficult to test. You can't really mock PHP calling your session handler so I'm working on figuring out a test. Regardless, I'm using this in a production environment so I as find optimizations or bugs, I'll be sure to post these here in addition to the unit test I'm trying to wrap my head around.