Skip to content

Latest commit

 

History

History
81 lines (68 loc) · 3.96 KB

v2.0.0_breaking_change.md

File metadata and controls

81 lines (68 loc) · 3.96 KB

v2.0.0 Breaking Change

Background

In previous versions of the Microsoft.Web.RedisSessionStateProvider NuGet package, session data was stored in Redis using keys using the following pattern:

DataKey = $"{applicationName}_{id}_Data";
LockKey = $"{applicationName}_{id}_Write_Lock";
InternalKey = $"{applicationName}_{id}_Internal";

With Redis clustering, there is no guarantee that these three keys would all land on the same shard (instance) of Redis. However, it is important that the data is on the same shard. When you add brackets ({ and }) to the key, only the part of the key that is inside the brackets is used by Redis when hashing the key to particular shard. So now all three keys are in same shard. The new key format looks like this:

DataKey = $"{{{applicationName}_{id}}}_Data";
LockKey = $"{{{applicationName}_{id}}}_Write_Lock";
InternalKey = $"{{{applicationName}_{id}}}_Internal";

Migrating Existing Session Data

Unfortunately, changing the key format means that existing session data won't be found. In your application, you can add code like the sample below to your Global.asax.cs to migrate session data from old format to new format when any request comes (session will be converted only when a request to access that session comes to the app). This migration only needs to happen once. After all keys have been updated to the new format, you can remove this code:

// KEYS[] = data-id, internal-id, lock-id, new-data-id, new-internal-id, new-lock-id
readonly string renameSessionScript = (@" 
        if redis.call('EXISTS', KEYS[1]) ~= 0 then
            local expiretime = redis.call('TTL',KEYS[1]) 
            redis.call('RENAME',KEYS[1],KEYS[4]) 
            redis.call('EXPIRE',KEYS[1], expiretime) 
        end
        if redis.call('EXISTS', KEYS[2]) ~= 0 then
            local expiretime = redis.call('TTL',KEYS[2]) 
            redis.call('RENAME',KEYS[2],KEYS[5]) 
            redis.call('EXPIRE',KEYS[2], expiretime) 
        end
        if redis.call('EXISTS', KEYS[3]) ~= 0 then
            local expiretime = redis.call('TTL',KEYS[3]) 
            redis.call('RENAME',KEYS[3],KEYS[6]) 
            redis.call('EXPIRE',KEYS[3], expiretime) 
        end
        return 1");

private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
    //Make sure to put cache name and password in bellow line.
    return ConnectionMultiplexer.Connect("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
});

public static ConnectionMultiplexer Connection
{
    get
    {
        return lazyConnection.Value;
    }
}

void Application_AuthorizeRequest(object sender, EventArgs e)
{
    string sessionId = this.Request.Params.Get("ASP.NET_SessionId");
    if(!string.IsNullOrEmpty(sessionId))
    {
        IDatabase connection = Connection.GetDatabase();
        string applicationName = "/"; // application name that you might have specified in web.config if not than default is '/'.
        string appIdAndSessionId = applicationName + "_" + sessionId;

        RedisKey[] keys = new RedisKey[6];
        keys[0] = appIdAndSessionId + "_Data";
        keys[1] = appIdAndSessionId + "_Write_Lock";
        keys[2] = appIdAndSessionId + "_Internal";
        keys[3] = "{" + appIdAndSessionId + "}_Data";
        keys[4] = "{" + appIdAndSessionId + "}_Write_Lock";
        keys[5] = "{" + appIdAndSessionId + "}_Internal";
        connection.ScriptEvaluate(renameSessionScript, keys, new RedisValue[0]);
    }
}

Server Farms

In a server farm scenario, you will need to update all farm instances at the same time. If only one server is updated with new session state provider, then you can run into a situation where one request comes to server with new session state which converts session data from old format to new format. Now, some other request can come to a server instance running the old session state provider code which will now fail to find the session as it was converted earlier.