Faster, dependency free PHP sessions in dynamodb

Idealstack • May 10, 2019


Dependency-free DynamoDB sessions

One of the great features of the Idealstack platform is the way we automatically setup PHP to use DynamoDB to store sessions, rather than PHP's default file-based session storage.  It's a big part of how we can automatically cluster-enable generic PHP apps like wordpress. We started off using the official AWS SDK's session handler for this, but found some issues with that.  So we've created our own session handler and released it on github under an Apache 2.0 license (the same open source license as the AWS PHP SDK).

Why DynamoDB?

If you are going to run PHP in a clustered environment, PHP's default file-based session handler won't work - sessions aren't shared between the different servers that might be running the website, so users will lose their session every time the load balancer sends them to a different server.

There's a few well-established cluster-friendly ways to do sessions in PHP.   Popular solutions are to use an SQL database, or to use Memcached or Redis.  But all of these require an additional server and none of them scale as well as DynamoDB does.  With DynamoDB you can pay for the capacity you use but then scale up to any capacity you require.  We feel that if you are hosting PHP on AWS, dynamodb sessions are the best solution.

Why dependency-free?

The PHP SDK provides a session handler that uses DynamoDB for PHP sessions.  For a while we used this to handle sessions for our clients and it works well. There's obviously benefits to using the officially supported AWS codebase if you can.

The problem though is that the AWS SDK is a beast.  It has a number of dependencies (things like Guzzle) and a very elegent, abstracted code structure - but therefore also a complex one.  Using this for sessions means a large number of PHP files need to be included in every page load.  This is fine if you are using the PHP SDK in other places in your code - you need to load these files sooner or later anyway.  But if you are using a common CMS system like wordpress you may never use these libraries elsewhere, so you end out using up memory, opcache caching space and disk IO loading these files for nothing.

The other problem you face - which admittedly may be a little specific to the quirks of how we are running it - is that because we are injecting this session handler code into other random code, you can get conflicts between versions for the libraries it uses.  If the code you are running depends on a different version of Guzzle or the PHP SDK (Guzzle in particular has a lot of incompatible versions), then weird bugs may happen.  The SDK also requires an autoloader which can clash with your code's own autoloading.  So in general it's better if we can use a session handler that doesn't rely on external libraries or autoloading.

Dependency-free session handler for dynamodb

For these reasons we built our own session handler from scratch with no dependencies, and we are releasing it under an open source Apache2 license.  Grab the code from github

  • Essentially a drop-in replacement for the official session handler in the AWS SDK
  • Dependency-free - does not depend on any other composer packages. Only requires the core curl and json extensions be enabled in PHP
  • Does not require an autoloader (although will work fine with one, eg composer)
  • Supports most common AWS authentication methods (eg instance profiles, ECS task roles, .aws config files, environment variables)
  • Compatible with all major PHP versions (even PHP 5.6, for all you luddites out there)
  • As a nice bonus, it's also about 30% faster

How to use it

Configuration is identical to the session handler in the official SDK, so if you are already using that it's a drop-in replacement.

  1. Create a table in dynamodb to store your sessions

    • Set the primary key to 'id'
    • Once the table is created enable TTL.  Note the AWS docs don't tell you to do this (we've opened a github pull request  suggesting doc updates about this) but it's a good idea even with the official SDK session handler.  Otherwise you'd need to garbage collect sessions which consumes read/write capacity and slows things down (and costs)
      • Set the TTL attribute to 'expires'
    • Consider using the 'on demand' capacity mode.  By default dynamodb sets up provisioned capacity with autoscaling.  This may (or may not) be cheaper but it means your site will 'go slow' or possibly timeout as it nears the capacity limits until autoscaling kicks in.  'On demand' scales instantly and automatically.  It's not eligible for the free tier though if you care about that.
  2. Setup the session handler in your code

    • You can install the session handler using composer, but composer and it's autoloader is not required as they are with the SDK
    • Create AWS credentials with access to the table.  We recomend you create an IAM policy and associate it as an instance role or ECS task role, the AWS SDK docs tell you the minimum required permissions.  You can also save the credentials in ~/.aws/credentials or use something like PHP dotenv to store them.
    • Create the session handler object and call 'register' on it:

    ```php use Idealstack\DynamoDbSessionHandlerDependencyFree; // or if you don't want to use composer auto-loader, try: // require(DIR .'/vendor/idealstack/dynamodb-session-handler-dependency-free/src/DynamoDbSessionHandler.php');

(new Idealstack\DynamoDbSessionHandlerDependencyFree\DynamoDbSessionHandler( [ 'table_name' => 'your-session-table-name', // Credentials. In production we recomend you use an instance role so you do not need to hardcode these. // At least make sure you don't hardcode them and commit them to github! 'credentials' => [ 'key' => 'AAAAAAAAAAAAAAAAAAAAAA', 'secret' => 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' ], // These are all defaults. // // Base64 encode data when reading and writing. Avoids problems with binary data, Note this is // // not the behaviour of the AWS SDK, so set to false if you require compatibility with existing // // sessions created with the SDK // 'base64' => true, // 'hash_key' => 'id', // // // The lifetime of an inactive session before it should be garbage collected. If it isn't provided, // // the actual lifetime value that will be used is ini_get('session.gc_maxlifetime'). // 'session_lifetime' => 1440, // 24 minutes // 'consistent_reads' => true, //You almost certainly want this to be true // 'session_locking' => false, //True is not supported ]


3. ### Test that it's working * This code will create a session.   Refresh it a few times to confirm that the timestamp it outputs doesn't change.  You can also look in the dynamodb table to confirm it is creating new records with session data ```php if (session_module_name() != 'user') { throw new \Exception("not using session handler"); } session_start(); if (! array_key_exists("test", $_SESSION)) $_SESSION["test"] = microtime(); echo "OK ".$_SESSION["test"]
  1. Make sure your app actually uses native sessions

    • One thing to be aware of is that a lot of PHP apps and frameworks override PHP's native sessions with their own session handlers.  Most of these use files to store sessions anyway, which is what we are trying to avoid.  To make these apps use your sessions you'll need to find a way to make them use native sessions.  Typically there is a module available for this in common frameworks like Laravel or Symfony.  We've documented steps for many popular  frameworks, apps and CMS's in our app setup instructions 

Harness the power of AWS for hosting PHP-based websites and apps.

Idealstack is a powerful hosting console that creates a modern,  autoscaling, best-practice, AWS-native hosting cluster for PHP-based code in your AWS account


How it works     TRY IT Now For Free