Building a Sitecore Redis Session State Provider

Learn how to build a custom Sitecore Redis Session State Provider that properly supports Session_End() using Redis Keyspace Notifications. This guide explains the limitations of the default Microsoft provider and walks through a complete solution for handling session expiration events in Sitecore.

Building a Sitecore Redis Session State Provider

With the release of Sitecore 7.5, the platform introduced significant architectural changes, including the adoption of MongoDB. While this improved the performance of Experience Analytics (formerly known as Sitecore Analytics), it also introduced new challenges related to session management.

In the updated architecture, user interactions are held in session memory until the session ends, at which point the data is written to MongoDB. However, many session state providers do not support the Session_End() event, as it is not a required part of the .NET session state lifecycle.

To address this, Sitecore provides its own session state providers for MongoDB and SQL, which use a timer-based mechanism to periodically check for and end expired sessions. As of now, Sitecore does not offer a Redis-compatible session state provider. Although Microsoft provides an officially supported Redis provider, it also lacks support for the Session_End() event.

Nick Hills has previously outlined the basics of creating a custom Sitecore Redis session state provider. This solution builds on that foundation, incorporating Redis Keyspace Notifications to handle session expirations more effectively.


High-Level Solution

  • Create a Custom Provider
    Implement a basic session state provider that proxies calls to Microsoft’s Redis Session State Provider.
  • Enhance Read/Write Operations
    Modify the session read and write methods so that each request also creates or updates a separate timeout key, using the format: ApplicationName_SessionId_Timeout. Set the TTL for this key to be approximately one minute less than the timeout for the _Data and _Internal keys.
  • Subscribe to Redis Keyspace Notifications
    Use ServiceStack.Redis to subscribe to key expiration notifications with the following pattern: keyspace@0:{ApplicationName}_*_Timeout
  • Implement an Expiry Notification Handler
    Create a handler to listen for expired timeout keys and trigger the Session_End() event when these keys expire.

This approach ensures that session expiration is handled accurately and efficiently within Redis, even without native support for the Session_End() event.


Creating a Basic Provider

Start by creating a session provider class that inherits from SessionStateStoreProviderBase. It is important to ensure that this provider is not declared as static. Under load, a static provider will cause intermittent failures due to shared state across requests.

Override the Initialize() and SetItemExpireCallback() methods.

  • Use Initialize() to configure instances of both the Microsoft and ServiceStack Redis providers.
  • Use SetItemExpireCallback() to notify .NET that the provider supports the Session_End() event.

Override the required methods from the base provider and proxy their calls to the existing Microsoft Redis provider instance, _redisSessionStateProviderContext.

Leave the Dispose() method empty, as Sitecore frequently calls it during the application lifecycle, which can lead to intermittent issues if it performs any cleanup.

For brevity, only a selection of method examples is provided below:

Enhancing Session Read/Write Methods

Update the session read and write methods to ensure that each request handled by the Microsoft provider also creates or updates an additional timeout key. This key should follow the format: ApplicationName_SessionId_Timeout.

Set the TTL for the timeout key to approximately one minute less than the expiration time of the _Data and _Internal keys. The following example demonstrates this approach:

Creating an Expiry Handler

Implement a method to handle expired messages, as shown in the example below. The handler must ensure it only processes events explicitly marked as expired (ignoring similar events such as expire) and triggers the Session_End() event when appropriate.

Subscribing to Redis Keyspace Notifications

Update the Initialize() method to subscribe to the appropriate Redis Keyspace Notifications. Ensure that each notification received triggers the Handle method to process the event.

Implementation

The solution is now ready for integration. After updating the Web.config and Sitecore.Analytics.Tracking.config files, session data will begin to appear in Redis and, upon Session_End(), will be written to MongoDB as expected.


Performance

The integrated solution delivers performance comparable to the ServiceStack provider and has successfully passed initial testing with approximately 15 concurrent users.

Considerations

Consider the following points when implementing this solution:

  • Redis does not guarantee that the expired event will be generated precisely when a key’s TTL reaches zero. There may be a delay before the event is triggered.
  • While the example uses Keyspace Notifications, switching to Keyevent Notifications can help reduce overhead traffic. Keyspace Notifications watch all keys by name, whereas Keyevent Notifications target specific keys for all events.
  • During the application lifecycle, multiple instances of the session state provider may be created and disposed of by Sitecore. This behavior can result in increased resource usage.
  • Each provider instance opens two Redis connections. If Microsoft’s Redis provider exposed its IConnectionMultiplexer, connection reuse would be possible, reducing the number of active connections.
  • Some required configuration values must be manually extracted from the NameValueCollection, as Microsoft’s implementation makes relevant classes private. At a minimum, the provider should support the following settings:
    • timeout
    • connectionTimeoutInMilliseconds
    • operationTimeoutInMilliseconds
    • retryTimeoutInMilliseconds
    • ssl
    • host
    • port
  • Both the Microsoft and ServiceStack Redis providers default to using database db0. If another database is required, ensure this is explicitly configured.