RedisSessionStateProvider Elasticache deadlock - asp.net

we want to share ASP.NET Session state between our apps and services. We chose Elasticache/redis to achieve this. It was going well but we've run into a deadlock scenario.
Here's the deadlock sequence:
user navigates to page served by App 1
App 1 uses RedisSessionStateProvider, successfully fetches the Session in a few milliseconds
App 1 makes an HttpWebRequest to App 2, with the ASP.NET_SessionId cookie attached
App 2 also uses RedisSessionStateProvider, which attempts to fetch the Session from the same redis instance and times-out after ~ 2 minutes
Presumably App 1's RedisSessionStateProvider is holding a (write?) lock on the cache item containing the Session. As you can tell from my parlance, I'm no redis guru...
AFAICT Elasticache gives you no visibility onto situations like this, just performance-y graphs. And RedisSessionStateProvider is closed-source so I can't poke around there.
I also tried to get RedisSessionStateProvider to log (via the loggingClassName parameter) but nothing gets written by either App 1 or App 2 (my Log() method is called though).
To prove that it is the RedisSessionStateProvider deadlocking (rather than our own code deadlocking) I switched App 1 back to using InProc sessions and everything runs fine.
Does anyone have any suggestions? BTW our Session data is to all intents and purposes immutable, so there really is no need for it to be locked.
Many thanks,
Pete
EDIT: the sessionState config as requested. Note that the large operationTimeoutInMilliseconds value is so that we don't get exceptions whilst debugging the app. This will be changed to ~ 5000 in production.
<sessionState mode="Custom" customProvider="RedisSessionProvider">
<providers>
<add name="RedisSessionProvider"
type="Microsoft.Web.Redis.RedisSessionStateProvider"
host = "ec2-184-73-3-249.compute-1.amazonaws.com"
port = "6379"
ssl = "false"
throwOnError = "true"
retryTimeoutInMilliseconds = "2000"
applicationName = "PE"
connectionTimeoutInMilliseconds = "2000"
operationTimeoutInMilliseconds = "1800000"
</providers>
</sessionState>

This is not answer but it is not fitting in comment section.
At start of page asp.net page execution life cycle it calls GetItemExclusive which fetches session from store (in this case redis) and puts a lock on that session so other parallel request cannot modify session while this request is working. This lock has time out that is equivalent of request timeout that you can set using web.config like below.
<configuration>
<system.web>
<httpRuntime executionTimeout="10"/>
</system.web>
</configuration>
Now, page executes and depending on weather anything was modified or not modified in session it calls SetAndReleaseItemExclusive or ReleaseItemExclusive which releases the lock. If this request fails for some reason than it will retry depending on retryTimeoutInMilliseconds value. If retryTimeoutInMilliseconds is very less or same as operationTimeoutInMilliseconds then it might not retry at all. If SetAndReleaseItemExclusive or ReleaseItemExclusive is not completed successfully then basically your session will be locked for complete time of “executionTimeout” that you have set above which is in seconds. All other request will be blocked and won’t be able to access the session while it is locked. Lock will be released automatically when it reaches expiry.
Use web.config properties loggingClassName and loggingMethodName for configuring logging. You can find more details in web.config comments when you upgrade to above package. You can basically provide a public, static method which returns a TextWriter. Session state provider and StackExchange.Redis.StrongName both will use this TextWriter object to log details.
This will help us get more details about problem. Beware that enabling logging will reduce the performance.
Example of using logging:
namespace SSPWebAppLatest3
{
public static class Logger
{
public static TextWriter GetLogger()
{
return File.CreateText("C:\\Logger.txt");
}
}
}
Web.config:
<add name="MySessionStateStore" type="Microsoft.Web.Redis.RedisSessionStateProvider" host="127.0.0.1" accessKey="" ssl="false"
loggingClassName="Logger, SSPWebAppLatest3, Version=1.0.0.0, Culture=neutral ……."
loggingMethodName="GetLogger"/>
Please send me a reproducible test app with which I debug this further. You can also do the same as session state and output cache provider code is open source now. (https://github.com/Azure/aspnet-redis-providers)

Related

ASP.NET MVC - Erasing session data after fer minutes of inactivity

In my web application, i often can see, that when i am not doing anything for a few minutes, and then i come back, and refresh the page - i am still being logged in, but my session data is all gone!
On the login() action i am setting up few Session[] objects that are necessary for a page to work correctly. I have no idea why is it doing so, but i need it to log user out whenever it clears his session data.
I have read about setting <sessionState mode="InProc" timeout="20"/> but will this timeout refresh everytime i refresh the page? Or will it run out after 20 minutes from the time i logged in? What if i make this timer bigger than i have on keeping the user online?
Posting back to the server will keep the session alive for longer. It's a sliding expiration. There are two ways to handle from the client, which the client is not aware of this 20 minute timeout:
Create a timer using client javascript that redirects to the logout page when 20 minutes is hit
Whenever a postback happens, check if the session expired (which can be done in a variety of ways, such as checking Session.IsNewSession, see if your objects are lost, etc.) and then redirect to the logout handler before processing the request.
I assume you are using Forms Authentication. Is that correct? If so, you need to have your Forms Authentication ticket's timeout match the Session timeout.
The user stays logged in through a process that is more complicated than it first seems. A cookie is stored in the user's browser that is called the Forms Authentication Ticket. If the user stays idle past the session timeout limit, the server will discard the session. But on the next request, the Forms Authentication Ticket is passed back to the web server. The server validates the ticket, and if it is still valid, the user is logged back in.
As you can see, the user's session is not restored. If you want that behavior, you would have to detect that condition and restore the session yourself.
The solution is to set the Forms Authentication Ticket's timeout to be the same as the Session timeout. You accomplish that in your Web.config file, as explained here:
<system.web>
<authentication mode="Forms">
<forms timeout="20"/>
</authentication>
</system.web>
The timeout value is in minutes. Once the Forms Authentication Ticket's timeout is hit, the user will be logged out. This operates independent from the session's timeout, but if they are the same, they will expire at roughly the same time. If you want to be completely safe, set the Forms Authentication Ticket timeout to be a little shorter than the session timeout. The user will be logged out before their session times out. When they log in again, they will get a new session. The old session will eventually time out on its own.
Try checking this:
Q: In Proc mode, why do I lose all my session occasionally?
A: Please see the "Robustness" section in the "Understanding session
state modes" section of of this article.
Robustness
InProc - Session state will be lost if the worker process
(aspnet_wp.exe) recycles, or if the appdomain restarts. It's because
session state is stored in the memory space of an appdomain. The
restart can be caused by the modification of certain config files such
as web.config and machine.config, or any change in the \bin directory
(such as new DLL after you've recompiled the application using VS) For
details, see KB324772. In v1, there is also a bug that will cause
worker process to restart. It's fixed in SP2 and in v1.1. See
KB321792.
Source - http://forums.asp.net/t/7504.aspx/1

Session in asp.net expiration

I am using a session to pass a variable but on the server after logging in the session automatically expires after 2 -3 minutes, What could be the problem?
The webconfig file:
<sessionState timeout="1440" mode="InProc"></sessionState>
<authentication mode="Forms">
<forms name="School" loginUrl="Login.aspx" defaultUrl="default.aspx"
timeout="1440" slidingExpiration="true" protection="All" path="/" />
</authentication>
I changed the timeout but it does not work.
Enable and check the logs and performance counters if the application pool restarts for some (configurable) reason, and loses it's sessions. Examples include if it runs out of memory (more likely if you have a shared app pool), if you have too many errors per minute (possibly "hidden" errors, triggered by for example search engine spiders) or if you are making changes to observed files or in observed folders (like web.config or bin\).
Depending on your session "uptime" requirements, since restarting the application pool will drop ("expire") all of your in process sessions, you could "fix" the issue by using an out of process session state store, like ASP.NET/Windows State Service/Server or SQL Server.
If you feel it's an IIS configuration or server issue more than a code issue, you can always ask on ServerFault.
Your timeout settings for Session and Forms looks fine but there are still many things can go side ways which causes you to think session is timed out. I suggest you to investigate the issue as follows:
Network setup: if your servers are load balanced, make sure the configuration will work with session.
App Pool: Check your application pool refresh/reset rules on IIS. Make sure there is no setting to refresh the pool every 20 requests or the likes.
Task Manager: Look at task manager and see how the IIS worker process doing (w3wp.exe). Is it getting killed off by antivirus program? If so, session will be timed out for sure.
Event log: Lastly, take a look at windows event log. see if there are event entries related to time out.
Add this to your Global.asax.cs
protected void Session_Start(object sender, EventArgs e)
{
Session.Timeout = 240;
}

ASP.NET session has expired or could not be found -> Because the Session.SessionID changes (Reporting Services)

1.-I'm using reporting services and sometimes I get this error ASP.NET session has expired or could not be found when I try to load a report.
2.-I realized that I get this error when the Session.SessionID property changes even though the user is the same. If it does not change, the report is loaded. I mean, if I refresh the report a number of times, whenever the Session.SessionID is the same than the last one, the report is loaded.
3.-Microsoft Documentation says:
When using cookie-based session state, ASP.NET does not allocate
storage for session data until the Session object is used. As a
result, a new session ID is generated for each page request until the
session object is accessed. If your application requires a static
session ID for the entire session, you can either implement the
Session_Start method in the application's Global.asax file and store
data in the Session object to fix the session ID, or you can use code
in another part of your application to explicitly store data in the
Session object.
If your application uses cookieless session state, the
session ID is generated on the first page view and is maintained for
the entire session.
The point is that I can not use a cookieless session state because I need cookies.
What could I do to avoid this error? Or What could I do to avoid the Session.SessionID to change on every request?
You are probably storing your session InProcess. Try changing it to session state server. You can find more details here.
I'm using report viewer 11.0.0; in your web config on system.web section, put the next configuration:
<sessionState timeout ="120" mode="InProc" cookieless="false" />
When you are generating the report (C# code bellow) in the reportviewer object change the KeepSessionAlive property to false and the AsynkRendering property to false, and that's all
this.rvReporte.KeepSessionAlive = false;
this.rvReporte.AsyncRendering = false;
(rvReporte) is a ReportViewer control located on my asp.net Form
This solution work for me, i hope that work for other people.
Regards
<httpCookies httpOnlyCookies="false" requireSSL="false"/>
Solved the problem.
Thanks to :
http://www.c-sharpcorner.com/Blogs/8786/reportviewer-Asp-Net-session-has-expired.aspx
I had the same issue on report viewer page when the web site was accessed from outside intranet. hardvin's suggestion saved the day for me which is to set
this.rvReporte.KeepSessionAlive = false;
this.rvReporte.AsyncRendering = false;
I changed the property on the control itself. I am using report viewer on a user control which raises a custom event for supplying parameters programmatically at the host page instead of prompting the users.
I solved this issue by setting AsyncRendering to false on reportviewer server control
Having the reportviewer being displayed in iframe was giving us this error. If displayed outside of iframe it works nice.
The reportviewer object has this configuration, AsyncRendering = false and KeepSessionAlive = true.
The webapp that has the reportviewer and set the session cookie in the browser was compiled with .net framework 4.6.1. We upgrade to 4.8 and put this in web.config
<sessionState cookieSameSite="None" />
<httpCookies requireSSL="true"/>
Só the solution is from https://learn.microsoft.com/en-us/aspnet/samesite/system-web-samesite#:~:text=The%20updated%20standard%20is%20not%20backward%20compatible%20with,SameSite%3DStrict.%20See%20Supporting%20older%20browsers%20in%20this%20document.
The answer given by Alexsandar is just one of the solution to this problem.
This link clearly explains what is the root cause for this problem and possible solutions:
http://blogs.msdn.com/b/brianhartman/archive/2009/02/15/did-your-session-really-expire.aspx
In case of Brian, the way he has descrived the problem, if he had just a single IIS server, using a session object in his code would have solved the problem because in that case, the SessionID which is passed in the request from browser to the server will get mapped to a corresponding sessionID on the server and hence the session expiry message will not come.
Setting the mode may only work in case of a server cluster where Brian had multiple IIS servers handling the same request. In that case an out of process mode will help to retrieve the session object from the Session Store irrespective of the server hit.
So based on this observation, I would conclude that Brian's problem was not related to cookies but to a server cluster. The information provided by Brian in his question and the subsequent solution misled me and hence this clarification. Hope it helps anyone looking for a similar problem.
Thanks,
Vipul
I have added the below-mentioned line on the web config file and it is working fine for me.
<sessionState mode="InProc" cookieless="true" timeout="3000" />
<pages enableSessionState="false" />
<customErrors mode="Off" />
Try removing SessionState="somevalue" tag from the top of your calling ASPX page. I'm using a custom SessionState and refuse to use InProc since I have multiple instances on Azure. You can even use AsyncRendering=True if you desire. Let me know if this did the trick for you.
For me, it turned out to be having more than one worker process for the app pool.
For me Azure hosted web app turning ON - ARR Affinity fixed the issue.
ARR Affinity ON

Access Session in WCF service from WebHttpBinding

I'm using WCF service (via WebGet attribute).
I'm trying to access Session from WCF service, but HttpContext.Current is null
I added AspNetCompatibilityRequirements and edited web.config but I still cannot access session.
Is it possible to use WebGet and Session together?
Thank you!
Yes, it is possible. If you edit the web.config:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
and add the AspNetCompatiblityRequirements, the HttpContext.Current should be available.
Check everything once again, maybe you've put the attribute in the wrong place (the interface instead of the class?).
A RESTfull service with a session?
See excellent discussion here: Can you help me understand this? "Common REST Mistakes: Sessions are irrelevant"
http://javadialog.blogspot.co.uk/2009/06/common-rest-mistakes.html (point 6)
and
http://www.peej.co.uk/articles/no-sessions.html
Quote from Paul Prescod:
Sessions are irrelevant.
There should be no need for a client to "login" or "start a connection." HTTP authentication is done
automatically on every message. Client applications are consumers of
resources, not services. Therefore there is nothing to log in to!
Let's say that you are booking a flight on a REST web service. You
don't create a new "session" connection to the service. Rather you ask
the "itinerary creator object" to create you a new itinerary. You can
start filling in the blanks but then get some totally different
component elsewhere on the web to fill in some other blanks. There is
no session so there is no problem of migrating session state between
clients. There is also no issue of "session affinity" in the server
(though there are still load balancing issues to continue).

Why might my users be being logged out after a minute or so?

I have a Asp Mvc 2 site using forms authentication. When I run it locally I can log in and stay logged in indefinitely.
However when I put it on the server I seem to only stay logged in for a few minutes and then seems to be logged out. I have looked at the cookies and there are 2 which seem relevant:
.ASPXAUTH which is a session cookie
.ASPXANONYMOUS which expires in 3 months.
When I refresh the page the cookies stay the same until I get logged out, when I seem to get a new .ASPXANONYMOUS cookie, but the .ASPXAUTH seems to be the same.
It seems that I might be able to stay logged in until I do something after a certain amount of time. If I submit a form as soon as I am logged in then it works ok, but if I keep submitting data again and again then after a minute or so, one of the submits will happen as a logged out user and not as the user who was logged in, which all the other submits worked as.
What might cause this behaviour and how can I track down what is different & change it so that I can stay logged in indefinitely?
EDIT,
its a single server, but after some more investigation and searching the likely candidate seems to be that I am using more than 100mb on the server and the application pool is getting recycled. I suppose now i need to know
How can I check how much memory I'm using.
What advice there is to reduce that.
Could it be that the ASP.NET application is being re-cycled or shutdown (e.g. due to idle timeout, or newly built/changed assemblies)?
When an ASP.NET web application starts up it will, by default, generate encryption keys for view state and session cookies. This will invalidate any such data originally served from an earlier run of the application (or from a different system).
To have sessions survive ASP.NET application cycles (and multi-server farms) you can specify the keys in your web.config:
<system.web>
...
<machineKey
decryption="AES"
validation="SHA1"
decryptionKey="..."
validationKey="..."
/>
where decryptionKey and validationKey are hex strings of length depending on the algorithm (with AES: 64 digits and SHA1: 128, for other algorithms check MSDN).
These keys should be cryptographically generated, and .NET has the types to do this which can be used from PowerShell:
$rng = New-Object "System.Security.Cryptography.RNGCryptoServiceProvider"
$bytes = [Array]::CreateInstance([byte], 16)
$rng.GetBytes($bytes)
$bytes | ForEach-Object -begin { $s = "" } -process { $s = $s + ("{0:X2}" -f $_) } -end { $s}
For AES use the above array length, for SHA1 use a length of 64.
It is quite likely that Session Timeout on the web server is configured to a much smaller timespan than you have set in your Form Authentication configuration in web.config.
The default Session Timeout is 20 minutes for IIS6 and IIS7.
If you have access to the web server's admin interface, you can raise the timeout via the GUI, but it can also be set from the config file if your IIS7 using the <sessionState> and <sessionPageState> sections:
http://msdn.microsoft.com/en-us/library/cc725820(v=ws.10).aspx
Check the webconfig authentication section
<authentication mode="Forms">
<forms name="UniqueName" loginUrl="login.aspx" path="/" >
</forms>
</authentication>
Ensure that the authentication cookie name for each hosted site is unique.
Came here with a similar issue, following the suggestion by #Richard, I looked at the Application Pools' recycling settings. What I found was the settings were changed and the Regular time intervals (in minutes) value was set to 1 minute. This meant that the app pool was being recycled each minute.
To change that, Right-click on the application pool, select the Recycling option, change the value under Regular time intervals (in minutes). I set it to the same value as the other Application Pools were using.
This change fixed the issue, turns out it was set to a low value a while back while during some misguided troubleshooting with an expired SSL certificate.
If none of these work, check in the Application Pools and ensure that the Idle Timeout is set to 20+ minutes. Click on the application pool, select the Advanced Settings link to the right, find the Process Model section, and increase the Idle Timeout value there.

Resources