I have a problem with an Azure project with one WebRole but multiple instances that uses cookieless sessions. The application doesn't need Session storage, so it's not using any session storage provider, but I need to track the SessionID. Apparently, the SessionID should be the same accross the WebRole instances, but it changes suddently w/o explanation. We are using the SessionID to track some data, so it's very important.
In order to reproduce the issue:
Create a Cloud Project.
Add a ASP.NET Web Role. The code already in it will do.
Open
Default.aspx
Add a control to see the current
SessionID
and a button to cause a postback<p><%= Session.SessionID %></p> <asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
Add a event handler for button that will delay the response a bit:
protected void Button1_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(150); }
Open
Web.Config
Enable cookieless sessions:
<system.web> <sessionState cookieless="true" /> </system.web>
Run the project, and hit fast and repeteadly the "PostBack" button for a while giving attention to the session id in the address bar. Nothing happens, the session id is always the same :). Stop it.
Open
ServiceConfiguration.csfg
Enable four instances:
<Instances count="4" />
Ensure that in the Web.config there is a line related with the machine key that has been added automatically by Visual Studio. (at the end of system.web).
- Rerun the project, hit fast and repeteadly the "Postback" button for a while and give attention to the session id in the address bar. You'll see how the
SessionID
changes after a while.
Why is this happening? As far as I know, if all machines share the machineKey
, the session should be the same across them. With cookies there are no problems, the issue apparently is just when cookieless sessions are used.
My best guess, is that something wrong is happening when there are several instances, when the SessionID
generated in one WebRole
goes to another, is rejected and regenerated. That doesn't make sense, as all the WebRole
s have the same machineKey
.
In order to find out the problem, and see it more clearly, I created my own SessionIDManager
:
public class MySessionIDManager : SessionIDManager
{
public override string CreateSessionID(HttpContext context)
{
if (context.Items.Contains("AspCookielessSession"))
{
String formerSessionID = context.Items["AspCookielessSession"].ToString();
// if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
// Debugger.Break();
return formerSessionID;
}
else
{
return base.CreateSessionID(context);
}
}
}
And to use it change this line in the WebConfig:
<sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />
Now you can see that the SessionID
doesn't change, no matter how fast and for how long you hit. If you uncomment those two lines, you will see how ASP.NET is creating a new sessionID even when there is already one.
In order to force ASP.NET to create a new session, just a redirect to an absolute URL in your site:
Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));
Why is this thing happening with cookieless sessions?
How reliable is my solution in MySessionIDManager
?
Kind regards.
UPDATE:
I've tried this workaround: User-Specified Machine Keys Overwritten by Site-Level Auto Configuration, but the problem still stands.
public override bool OnStart() { // For information on handling configuration changes // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357. using (var server = new ServerManager()) { try { // get the site's web configuration var siteNameFromServiceModel = "Web"; // update this site name for your site. var siteName = string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel); var siteConfig = server.Sites[siteName].GetWebConfiguration(); // get the appSettings section var appSettings = siteConfig.GetSection("appSettings").GetCollection() .ToDictionary(e => (string)e["key"], e => (string)e["value"]); // reconfigure the machine key var machineKeySection = siteConfig.GetSection("system.web/machineKey"); machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]); machineKeySection.SetAttributeValue("validation", appSettings["validation"]); machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]); machineKeySection.SetAttributeValue("decryption", appSettings["decryption"]); server.CommitChanges(); _init = true; } catch { } } return base.OnStart(); }
I've also tried this about put a session start handler and add some data, but no luck.
void Session_Start(object sender, EventArgs e) { Session.Add("dummyObject", "dummy"); }
Bounty up!