Serverless session handling in PHP

Christian Burger
Five Squared
Published in
4 min readAug 20, 2019
Photo by Hans Vivek on Unsplash

Session handling is one of the first aspects to consider when designing web applications to run in multi-server, load-balanced, high availability environments.

The problem

As soon as an application has multiple instances running simultaneously (as is the case for applications behind load balancers), the state of the sessions created need to be managed between all the instances. While the client (the browser) remains constant during every request, the responding server may differ for every request. However, the browser expects its session to be honoured during every single request.

A simple use case that illustrates the problem is user login sessions… Once a user is authenticated and a session is established by Server 1, the user’s session will go missing for any subsequent requests passed to Server 2 and 3 in the cluster. Thus resulting in prompts to sign in again.

The fact that sessions are stored on disk poses a problem in this scenario as each server’s sessions files are siloed and inaccessible to the other servers in the cluster.

The same problem occurs when a session is changed, only one server will be aware of that and the session will be out of date on all the other servers.

Sticky sessions

One solution to this problem is called “Sticky sessions”. With this approach, a load balancer will remember a user’s server and keep directing the user’s requests back to the same server.

However, the problem with this approach is when a server suddenly becomes unavailable to the cluster. All users “assigned” to it will immediately lose their session data and will have to start a new session to continue, i.e. sign in again.

This could be especially costly in shopping applications, as the user’s basket will be lost and will have to add all his/her items again.

Shared sessions

The better approach would be one where all active sessions are accessible by all servers in the cluster. There are various tools that could provide this, i.e. Redis, Memcache, MySQL, NFS, etc. But the one we’ll discuss today is DynamoDB.

DynamoDB is one of the simplest ways to set up and maintain a shared session store. Especially for small teams already on AWS and looking to keep infrastructure and maintenance as simple as possible…

With DynamoDB, one could have a highly available session store up running in minutes. It’s serverless so virtually zero maintenance is required to keep it running and it is highly scalable.

Getting set up

We need to create a DynamoDB table. The required structure is very simple with just a partition key of ‘id’ needed.

Note: For production environments, it’s important to use the AWS Management Console to create the table. This is to ensure that the correct billing and throughput settings are selected.

Tip: to make DynamoDB handle the garbage collection for you, simply set your table’s TTL attribute to ‘expires’.

For dev environments using a local instance of the DynamoDB engine, the following script should create the table for you (assuming DynamoDB is running on your localhost on port 8000):

use Aws\DynamoDb;$client = new DynamoDb\DynamoDbClient([
'version' => '2012-08-10',
'region' => 'eu-west-1',
'credentials' => [
'key' => 'test',
'secret' => 'test',
],
'endpoint' => 'http://localhost:8000'
]);
$client->createTable([
'TableName' => 'My-Sessions-Table',
'AttributeDefinitions' => [
[
'AttributeName' => 'id',
'AttributeType' => 'S',
],
],
'KeySchema' => [
[
'AttributeName' => 'id',
'KeyType' => 'HASH',
],
],
'ProvisionedThroughput' => [
'ReadCapacityUnits' => 1,
'WriteCapacityUnits' => 1,
]
]);

Alternatively, with the AWS CLI tools installed you can create the table from the console like this:

aws dynamodb create-table \
--table-name My-Sessions-Table \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--endpoint-url http://localhost:8000 \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5

Allowing the session handler access to the table requires the following IAM permissions:

{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:GetItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Scan",
"dynamodb:BatchWriteItem"
],
"Effect": "Allow",
"Resource": "arn:aws:dynamodb:<region>:<account-id>:table/<table-name>"
}
]
}

Implementation

Now that we have a table for our session store, all that’s left to do is implement the session handler in our application.

For vanilla PHP projects

We need to pull in the AWS SDK into our project. We can do this using the composer package provided by AWS:

composer require aws/aws-sdk-php

The AWS SDK makes it super easy to register the session handler, below is an example:

use Aws\DynamoDb;$client = new DynamoDb\DynamoDbClient($config['aws']);$handler = DynamoDb\SessionHandler::fromClient($client, [
'table_name' => 'My-Sessions-Table',
'hash_key' => 'id',
'session_lifetime' => 86400, // 24 hours
]);
$handler->register();session_start();

The above needs to be part of our application’s bootstrap and should be executed before the session is started.

Once the session handler is registered, we can assign session variables as we normally would:

$_SESSION['name'] = 'Jack Sparrow';

Now when you perform a scan on your table, you should see one record corresponding to the session you have just initiated.

For Laravel projects

I created a small package called Jam that makes implementing DyanamoDb sessions in Laravel really quick and easy. Here is a separate post entirely dedicated to setting up your Laravel environments for serverless session handling using the Jam package.

From Laravel 5.8 onward it has been possible to piggyback on Laravel’s cache driver to create a session store in DynamoDB. Read more about that in this follow up post titled: Serverless session handling in Laravel 7

Fine-tuning and best practices

For more config options and fine tuning see the AWS PHP SDK documentation.

Wrapping up

In a nutshell, the solution proposed here offers a highly available, scalable and centralised store for session data that allows multiple instances of a web application running simultaneously from multiple servers access to realtime session data.

--

--