Lately, I’ve been undertaking a large-scale, back-office project to support our growing operations teams at Plastiq. The application started experiencing some hanging, which ended up being caused by native PHP session handling. I was able to fix the issue by swapping in AWS’s DynamoDB for session management and storage, and I thought it’d be helpful to document the steps I took.
More after the jump.
So – the internal app I’ve been building uses LAMP on AWS EC2, and PHP sessions are used to manage state (e.g., authentication). For a while, everything worked pretty decently, but after a bit of time I started receiving reports of hanging requests and interminable page loads. The application uses a decent number of AJAX requests and complex SQL queries, so I optimized those up a bit, expecting to see some performance gains. While those changes helped, we’d still occasionally see some serious loading issues. Over the next couple months, I continued to check EC2 memory and CPU usage, and nothing looked out of sorts.. the first clue that it was PHP sessions-related was that the web app appeared to lock up more frequently when multiple tabs were being opened (this is common in our ops workflow).
One day, I sat down to finally get to the bottom of things, and I was able to isolate the loading issue to a
session_start() function that is called during initialization of the server response. Some quick research revealed a decently well-documented diagnosis that PHP sessions can cause websites to hang. It’s a potentially multi-faceted issue, so rather than pull my hair out with configs and functions, I decided to “outsource” my session handling to AWS’s DynamoDB.
AWS had written up a quick guide for running PHP Sessions with AWS DynamoDB (SDK v3), but I found it a little lacking in specifics, and there was surprisingly little documentation elsewhere on the web. It ended up taking a couple hours to swap in DynamoDB for my PHP session management / storage, and I thought it might be useful to outline the steps I took.
This tutorial assumes the following:
- You have an AWS account
Step 1) Create an AWS IAM User
Identity and Access Management (IAM) is AWS’s way of managing your access to AWS services. We’ll need to create an IAM user and provision their credentials so that you can access your DynamoDB instance from the PHP application.
Upon logging into your AWS Console, start by typing “IAM” into the search bar, and select the choice as shown below.
This will take you to your IAM dashboard. Select “Users” from the left panel, and click “Add User” on the next page.
Step 1a) Set User Details
Step 1b) Set User Permissions
Click “Attach existing policies directly” and start typing “dynamodb” into the filter box below. Then select the Policy called “AmazonDynamoDBFullAccess“. This will give your new user full access to all of DynamoDB’s facilities.
Step 1c) Review and Submit
The next page should look something like this. Just press the “Create User” button.
Step 1d) Record Your Credentials
The confirmation page will list two important details: your Access Key ID and your Secret Access Key. Save these details somewhere – this is the only time you will be able to access your Secret Access Key. But don’t worry too much if you lose it, you can re-provision a new Secret Access Key later (though this will invalidate your previously provisioned Secret Access Key).
Step 2) Create a DynamoDB
Next, navigate to the DynamoDB service in AWS by typing “dynamo” into the services search bar.
Click the “Create Table” button on the next screen. Then, set a Table name (this can be whatever you want) and name your Primary key. The Primary key can simply be named “id”, and should be a “String” type.
At this point, you can define your table settings. You can go with default settings, but one reason you might not want to initially is to enable auto scaling for your table. Auto scaling will automatically increase (and decrease) your read and write capacity as demand warrants. My preference is to have AWS auto scale as many things as possible to reduce my monitoring and management work load.
This guide from AWS has a good description of how you can ensure that your new DynamoDB table has auto scaling enabled. Basically, if your AWS account doesn’t yet have the DynamoDBAutoscaleRole, it will need to create it. The steps are simple – de-select the “Use default settings” checkbox, and under IAM Role in the advanced settings that appear, make sure that you select “New role: DynamoDBAutoscaleRole”.
That’s it! We’re now ready to use our new DynamoDB table.
Step 3) Install the AWS SDK
AWS has documented some instructions for installing the AWS SDK for PHP. My preference is using Composer, which makes downloading the latest stable version of the AWS SDK as simple as the following line
[syntax type=”html|php|js|css”]> php composer.phar require aws/aws-sdk-php[/syntax]
Then, you’ll want to require Composer’s autoloader, which will make any Composer-included packages available to your codebase.
Step 4) Register a New PHP Session Handler
Once the AWS SDK is available, setting up PHP to use DynamoDB for its session handling is as simple as the following code. The placeholders below should be filled as follows:
- <ACCESS_KEY_ID> = the Access Key ID we obtained earlier during IAM user provisionining.
- <SECRET_ACCESS_KEY> = the Secret Access Key we obtained earlier during IAM user provisionining.
- <AWS_REGION> = the AWS region where the DynamoDB we expect to use is located (e.g., “us-west-2”)
- <AWS_SDK_VERSION> = the API version we intend to be using (e.g., “2012-08-10”)
- <TABLE_NAME> = the name of the DynamoDB table that we just created
// Put the DynamoDbClient into local scope use Aws\DynamoDb\DynamoDbClient; // Create a DynamoDB client $dynamoDb = DynamoDbClient::factory(array( 'credentials' => array( 'key' => '<ACCESS_KEY_ID>', 'secret' => '<SECRET_ACCESS_KEY>' ), 'region' => '<AWS_REGION>', 'version' => '<AWS_SDK_VERSION>' )); // Register DynamoDB with a new PHP session handler $sessionHandler = $dynamoDb->registerSessionHandler(array( 'table_name' => '<TABLE_NAME>', 'session_locking' => false )); // Start the session session_start();
You may also notice that we’ve set
session_locking to “false” above, which will further reduce the chance of page-load hanging due to session locks taking a long time to be acquired / released. This does potentially introduce some danger to session updates, but for my purposes at least, I was willing to accept the risk.