DynamoDB is a NoSQL database service provided by Amazon, offering features like easy scalability, high availability and data durability. These properties make it well suited to being used as a Magento session storage engine. The AWS PHP SDK already includes a PHP session handler, so in theory it should be a simple matter of including the SDK and configuring Magento to use it. In practice, it wasn't quite this straightforward.

Using a custom session handler

Since CE 1.7, Magento has allowed specifying a custom session handler. This is done by specifying 'user' as the value of the <session_save> node in local.xml, and a static function to call in the <session_save_path> node. For example:

<config>
 <global>
 ...
 <session_save><![CDATA[user]]></session_save>
 <session_save_path><![CDATA[My_Module_Model_Session::initHandler]]></session_save_path>
 ...
 </global>
</config>

Unfortunately this doesn't work out of the box. The value of the <session_save_path> node is retrieved using the getSessionSavePath() function in Mage_Core_Model_Session_Abstract. This function checks to see if there's a config value for the session save path and returns it. The issue is that it's returned as a Magento config node object, not as a string. Magento tries to pass this object as an argument to call_user_func(), which won't work. This means the session handler isn't initialised correctly and the session_start() call fails.

Our fix for this was to modify the getSessionSavePath() method in Mage_Core_Model_Session_Abstract to convert the return value to a string. This allows it to be successfully used as a call_user_func() argument.

Storing session data in DynamoDB

Once we got the session handler loaded, we found that sessions weren't being stored correctly on the frontend - the data was always blank. They did work correctly from the admin panel, however. After some digging, we found that the issue was the AWS SDK was failing to write the session data to DynamoDB because one of the arguments to the UpdateItem API call had no value. This was because the SDK uses json_encode() to escape API call parameters, and it was failing on the session data.

The session data string contained invalid UTF-8 characters in the server_addr and remote_addr values. These are added by the Mage_Log module and contain the output of the inet_pton() function: IP addresses as binary strings. As the output of this function isn't likely to be valid UTF-8, it causes json_encode() to fail when called on the session data. The relevant classes and methods are:

  • initByRequest() in Mage_Log_Model_Visitor
  • getServerAddr() and getRemoteAddr() in Mage_Core_Helper_Http

The issue didn't occur for the admin panel as the server_addr and remote_addr values are only added on the frontend. Admin sessions didn't contain the binary strings and could be successfully passed to json_encode(). This also only occurs for recent versions of Magento (such as CE 1.9.2.0). Previous versions used ip2long() to output IP addresses; this returns an integer that can be successfully JSON-encoded.

Our solution was to disable the Mage_Log module, as we didn't need it (or anything depending on it) for this project. We used the Zookal magento-mock module to let us disable the module without causing errors in other code that expects it to be present. Another possible solution would be to have the session handler encode/decode the session data to handle the invalid UTF-8 characters (i.e. using Base64).