I check a session object and if it does exist then call another method which would use that object indirectly. Although the second method would access this object in a few nanoseconds I was thinking of a situation when the object exactly expires between two calls. Does Session object extends its lifetime on every read access from code for preventing such a problem ? If not how to solve the problem ?
If you are going to say why I don't pass the retrieved object from first method to second one, this is because I pass the ASP.NET Page object which carries many other parameters inside it to second method and if I try to pass each of them separately, there would be many parameters while I just pass one Page object now.
Don't worry, this won't happen
If I understand your situation it works sort of this way:
Access a certain page
If session is active it immediately redirects to the second page or executes a certain method on the first page.
Second page/method uses session
You're afraid that session will expire between execution of the first and second method/page.
Basically this is impossible since your session timer was reset when just before the first page starts processing. So if the first page had active session then your second page/method will have it as well (as long as processing finishes before 20 minutes - default session timeout duration).
How is Session processed
Session is processed by means of an HTTP Module that runs on every request and before page starts processing. This explains the behaviour. If you're not familiar with HTTP Modules, then I suggest you read a bit about IHttpModule interface.
It's quite difficult to understand your question, IMHO, but I will try.
From what I understand, you're doing something like:
string helloWorld = string.Empty;
if (this.Session["myObject"] == null)
{
// The object was removed from the session or the session expired.
helloWorld = this.CreateNewMyObject();
}
else
{
// Session still exists.
helloWorld = this.Session["myObject"].ToString(); // <- What if the session expired just now?
}
or
// What if the session existed here...
if (this.Session["myObject"] == null)
{
this.Session["myObject"] = this.CreateNewMyObject();
}
// ... but expired just there?
string helloWorld = this.Session["myObject"].ToString();
I thought that Session object is managed by the same thread as the page request, which would mean that it is safe to check if object exists, than use it without a try/catch.
I were wrong:
For Cache objects you have to be aware of the fact that you’re dealing essentially with an object accessed across multiple threads
Source: ASP.NET Cache and Session State Storage
I were also wrong about not reading to carefully the answer by Robert Koritnik, which, in fact, clearly answers the question.
In fact, you are warned about the fact that an object might be removed during page request. But since Session lifespan relies on page requests, it would mean that you must take in account the removal of session variables only if your request takes longer than the session timeout (see How is Session processed in the answer by Robert Koritnik).
Of course, such situation is very rare. But if in your case, you are pretty sure that the page request can take longer than 20 minutes (default session timeout), than yes, you must take in account that an object may be removed after you've checked if it exists, but before you really use it.
In this situation, you can obviously increment the session timeout, or use try/catch when accessing the session objects. But IMHO, if the page request takes dozens of minutes, you must consider other alternatives, as Windows services, to do the work.
I'm having difficulties understanding what the problem here is but let me try it again referring to thread safety.
Thread safety issue
If this is a thread safety issue, you can always issue a lock when creating a certain session object so other parallel requests won't run into a problem by double creating your object.
if (obj == null)
{
lock (objLock)
{
if (obj == null)
{
obj = GenerateYourObject();
}
}
}
Check lock documentation on MSDN if you've never used it before. And don't forget to check other web resources as well.
Related
I have an HttpSessionListener. Is there a way, inside its sessionDestroyed method to distinguish between the following cases:
the session was destroyed because the session-timeout configured in the web.xml was exceeded
the session was destroyed programmatically by the the application calling HttpSession#invalidate
My use case is that I have a Single Sign On (SSO) arrangement between a number of applications and I want a global single sign off when one of the applications participating in the SSO arrangement explicitly logs off but not when its session times out, hence the need to distinguish between the two cases. I guess a way would be for the application to set some flag in the session object just prior to calling HttpSession#invalidate. The HttpSessionListener would then examine the session object and if that flag is found it would know this was a programmatic logout. If not, it was a container logout. Would that make sense and / or is there a better way?
You can use HttpSession#getLastAccessedTime() to obtain the timestamp of the last request sent by the client associated with the session. Then you can just do the math with help of HttpSession#getMaxInactiveInterval() and the current timestamp.
long lastAccessedTime = session.getLastAccessedTime();
long timeoutInMillis = TimeUnit.SECONDS.toMillis(session.getMaxInactiveInterval());
long now = System.currentTimeMillis();
boolean sessionHasBeenTimedout = (now - timeoutInMillis > lastAccessedTime);
// ...
I have a function that parses an input file.
Private Function getSvSpelOdds(ByVal BombNo As Integer) As Boolean
Dim InputFileBase As String = HttpContext.Current.Application("InputFileBase")
strInputFile = InputFileBase & "PC_P7_D.TXT"
OddsReader = New StreamReader(strInputFile)
'some other code
End Function
If the file is not there (getSvSpelOdds returns False), I would like to retry after 30 seconds.
To achieve this I use a timer.
If Not getSvSpelOdds(y) Then
Timer1.Interval = 30000
End If
Private Sub Timer1_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed
getSvSpelOdds(y)
End Sub
Problem is that when timer fires the HttpContext.Current (used to get the value of gloal variable) is null.
Should I use some other approach to get this to work?
As already described HttpContext should be null as Timer_Elapsed is called in different thread. But you may use System.Web.HttpRuntime.Cache to pass filename, cache should be accessible from all threads.
HttpContext.Current only gives you the context you want when you call it on the thread that handles the incoming thread.
When calling it outside of such threads, you get null. That matches your case, as Timer1_Elapsed is executed on a new thread.
Should I use some other approach to get this to work?
Almost certainly, yes. 30 seconds is a long time to wait without giving any feedback to users.
It would probably be better to return a "no results are available yet, but we're still looking" page to the user. That page can be set to refresh automatically after 30 seconds, by adding a suitable meta-tag:
<META HTTP-EQUIV="refresh" CONTENT="30">
And you then get a fresh request/response cycle on the server. And haven't tied up server resources in the meantime.
Other answers seems to address the other part of your question (about why it doesn't work in the timer callback)
The Elapsed event on the Timer will run on a separate thread therefore its expected behaviour for the current context to be null.
You can only access it from the same thread.
Should I use some other approach to get this to work?
Yes, it's not generally a good idea to mix ASP.NET and threads given the complexity of how ASP.NET works. Like already mentioned its not a great UX to have no feedback for 30 seconds, its better to let the user know what's actually going on.
Also, you need to determine whether the timeout length is appropriate or whether a timeout is needed at all. I don't know the nature of your application but I assume there is some external means for the file to be generated and picked up by your site.
I was playing around with asynchronous features of .NET a little bit and came up with a situation that I couldn't really explain. When executing the following code inside a synchronous ASP.NET MVC controller
var t = Task.Factory.StartNew(()=>{
var ctx = System.Web.HttpContext.Current;
//ctx == null here
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()
);
t.Wait();
ctx is null within the delegate. Now to my understanding, the context should be restored when you use the TaskScheduler.FromCurrentSynchronizationContext() task scheduler. So why isn't it here? (I can, btw, see that the delegate gets executed synchronously on the same thread).
Also, from msdn, a TaskScheduler.FromCurrentSynchronizationContext() should behave as follows:
All Task instances queued to the returned scheduler will be executed
through a call to the Post method on that context.
However, when I use this code:
var wh = new AutoResetEvent(false);
SynchronizationContext.Current.Post(s=> {
var ctx = System.Web.HttpContext.Current;
//ctx is set here
wh.Set();
return;
},null);
wh.WaitOne();
The context is actually set.
I know that this example is little bit contrived, but I'd really like to understand what happens to increase my understanding of asynchronous programming on .NET.
Your observations seem to be correct, it is a bit puzzling.
You specify the scheduler as "TaskScheduler.FromCurrentSynchronizationContext()". This associates a new "SynchronizationContextTaskScheduler". Now if you look into this class it uses:
So if the task scheduler has access to the same "Synchronization
Context" and that should reference
"LegacyAspNetSychronizationContext". So surely it appears that
HttpContext.current should not be null.
In the second case, when you use a SychronizationContext (Refer:MSDN Article) the thread's context is shared with the task:
"Another aspect of SynchronizationContext is that every thread has a
“current” context. A thread’s context isn’t necessarily unique; its
context instance may be shared with other threads."
SynchronizationContext.Current is provided by LegacyAspNetSychronizationContext in this case and internally has a reference to HttpApplication.
When the Post method has to invoke registered callback, it calls HttpApplication.OnThreadEnter, which ultimately results in setting of the current thread's context as HttpCurrent.Context:
All the classes referenced here are defined as internal in the framework and is making it a bit difficult to investigate further.
PS: Illustrating that both SynchornizationContext objects in fact point to "LegacyAspNetSynchronizationContext":
I was googling for HTTPContext info some time ago. And I found this:
http://odetocode.com/articles/112.aspx
It's about threading and HTTPContext. There is good explanation:
The CallContext provides a service extremely similar to thread local storage (except CallContext can perform some additional magic during a remoting call). Thread local storage is a concept where each logical thread in an application domain has a unique data slot to keep data specific to itself. Threads do not share the data, and one thread cannot modify the data local to a different thread. ASP.NET, after selecting a thread to execute an incoming request, stores a reference to the current request context in the thread’s local storage. Now, no matter where the thread goes while executing (a business object, a data access object), the context is nearby and easily retrieved.
Knowing the above we can state the following: if, while processing a request, execution moves to a different thread (via QueueUserWorkItem, or an asynchronous delegate, as two examples), HttpContext.Current will not know how to retrieve the current context, and will return null. You might think one way around the problem would be to pass a reference to the worker thread
So you have to create reference to your HTTPContext.Current via some variable and this variable will be adressed from other threads you will create in your code.
Your results are odd - are you sure there's nothing else going on?
Your first example ( with Task ) only works because Task.Wait() can run the task body "inline".
If you put a breakpoint in the task lambda and look at the call stack, you will see that the lambda is being called from inside the Task.Wait() method - there is no concurrency. Since the task is being executed with just normal synchronous method calls, HttpContext.Current must return the same value as it would from anywhere else in your controller method.
Your second example ( with SynchronizationContext.Post ) will deadlock and your lambda will never run.
This is because you are using an AutoResetEvent, which doesn't "know" anything about your Post. The call to WaitOne() will block the thread until the AutoResetEvent is Set. At the same time, the SynchronizationContext is waiting for the thread to be free in order to run the lambda.
Since the thread is blocked in WaitOne, the posted lambda will never execute, which means the AutoResetEvent will never be set, which means the WaitOne will never be satisfied. This is a deadlock.
We have a data driven ASP.NET website which has been written using the standard pattern for data caching (adapted here from MSDN):
public DataTable GetData()
{
string key = "DataTable";
object item = Cache[key] as DataTable;
if((item == null)
{
item = GetDataFromSQL();
Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300), TimeSpan.Zero;
}
return (DataTable)item;
}
The trouble with this is that the call to GetDataFromSQL() is expensive and the use of the site is fairly high. So every five minutes, when the cache drops, the site becomes very 'sticky' while a lot of requests are waiting for the new data to be retrieved.
What we really want to happen is for the old data to remain current while new data is periodically reloaded in the background. (The fact that someone might therefore see data that is six minutes old isn't a big issue - the data isn't that time sensitive). This is something that I can write myself, but it would be useful to know if any alternative caching engines (I know names like Velocity, memcache) support this kind of scenario. Or am I missing some obvious trick with the standard ASP.NET data cache?
You should be able to use the CacheItemUpdateCallback delegate which is the 6th parameter which is the 4th overload for Insert using ASP.NET Cache:
Cache.Insert(key, value, dependancy, absoluteExpiration,
slidingExpiration, onUpdateCallback);
The following should work:
Cache.Insert(key, item, null, DateTime.Now.AddSeconds(300),
Cache.NoSlidingExpiration, itemUpdateCallback);
private void itemUpdateCallback(string key, CacheItemUpdateReason reason,
out object value, out CacheDependency dependency, out DateTime expiriation,
out TimeSpan slidingExpiration)
{
// do your SQL call here and store it in 'value'
expiriation = DateTime.Now.AddSeconds(300);
value = FunctionToGetYourData();
}
From MSDN:
When an object expires in the cache,
ASP.NET calls the
CacheItemUpdateCallback method with
the key for the cache item and the
reason you might want to update the
item. The remaining parameters of this
method are out parameters. You supply
the new cached item and optional
expiration and dependency values to
use when refreshing the cached item.
The update callback is not called if
the cached item is explicitly removed
by using a call to Remove().
If you want the cached item to be
removed from the cache, you must
return null in the expensiveObject
parameter. Otherwise, you return a
reference to the new cached data by
using the expensiveObject parameter.
If you do not specify expiration or
dependency values, the item will be
removed from the cache only when
memory is needed.
If the callback method throws an
exception, ASP.NET suppresses the
exception and removes the cached
value.
I haven't tested this so you might have to tinker with it a bit but it should give you the basic idea of what your trying to accomplish.
I can see that there's a potential solution to this using AppFabric (the cache formerly known as Velocity) in that it allows you to lock a cached item so it can be updated. While an item is locked, ordinary (non-locking) Get requests still work as normal and return the cache's current copy of the item.
Doing it this way would also allow you to separate out your GetDataFromSQL method to a different process, say a Windows Service, that runs every five minutes, which should alleviate your 'sticky' site.
Or...
Rather than just caching the data for five minutes at a time regardless, why not use a SqlCacheDependency object when you put the data into the cache, so that it'll only be refreshed when the data actually changes. That way you can cache the data for longer periods, so you get better performance, and you'll always be showing the up-to-date data.
(BTW, top tip for making your intention clearer when you're putting objects into the cache - the Cache has a NoSlidingExpiration (and a NoAbsoluteExpiration) constant available that's more readable than your Timespan.Zero)
First, put the date you actually need in a lean class (also known as POCO) instead of that DataTable hog.
Second, use cache and hash - so that when your time dependency expires you can spawn an async delegate to fetch new data but your old data is still safe in a separate hash table (not Dictionary - it's not safe for multi-reader single writer threading).
Depending on the kind of data and the time/budget to restructure SQL side you could potentially fetch only things that have LastWrite younger that your update window. you will need 2-step update (have to copy dats from the hash-kept opject into new object - stuff in hash is strictly read-only for any use or the hell will break loose).
Oh and SqlCacheDependency is notorious for being unreliable and can make your system break into mad updates.
What is the difference between destroying a session and removing its values? Can you please provide an example demonstrating this?
I searched for this question, but don't grasp total answer. Some answers are:
Session.Abandon() destroys the session
Session.Clear() just removes all values
A friend told me this:
Clearing the session will not unset
the session, it still exists with the
same ID for the user but with the
values simply cleared.
Abandon will destroy the session
completely, meaning that you need to
begin a new session before you can
store any more values in the session
for that user.
The below code works and doesn't throw any exceptions.
Session.Abandon();
Session["tempKey1"] = "tempValue1";
When you Abandon() a Session, you (or
rather the user) will get a new
SessionId
When I test Session, it doesn't makes any change when I Abandon the session.
I just find one difference:
session.Abandon() raises Session_End event
Clear - Removes all keys and values from the session-state collection.
Abandon - removes all the objects stored in a Session. If you do not call the Abandon method explicitly, the server removes these objects and destroys the session when the session times out.
It also raises events like Session_End.
Session.Clear can be compared to removing all books from the shelf, while Session.Abandon is more like throwing away the whole shelf.
You say:
When I test Session, it doesn't makes any change when I Abandon the session.
This is correct while you are doing it within one request only.
On the next request the session will be different. But the session ID can be reused so that the id will remain the same.
If you will use Session.Clear you will have the same session in many requests.
Generally, in most cases you need to use Session.Clear.
You can use Session.Abandon if you are sure the user is going to leave your site.
So back to the differences:
Abandon raises Session_End request.
Clear removes items immidiately, Abandon does not.
Abandon releases the SessionState object and its items so it can ba garbage collected to free the resources. Clear keeps SessionState and resources associated with it.
When you Abandon() a Session, you (or rather the user) will get a new SessionId (on the next request).
When you Clear() a Session, all stored values are removed, but the SessionId stays intact.
This is sort of covered by the various responses above, but the first time I read this article I missed an important fact, which led to a minor bug in my code...
Session.Clear() will CLEAR the values of all the keys but will NOT cause the session end event to fire.
Session.Abandon() will NOT clear the values on the current request. IF another page is requested, the values will be gone for that one. However, abandon WILL throw the event.
So, in my case (and perhaps in yours?), I needed Clear() followed by Abandon().
this code works and dont throw any exception:
Session.Abandon();
Session["tempKey1"] = "tempValue1";
It's because when the Abandon method is called, the current Session object is queued for deletion but is not actually deleted until all of the script commands on the current page have been processed. This means that you can access variables stored in the Session object on the same page as the call to the Abandon method but not in any subsequent Web pages.
For example, in the following script, the third line prints the value Mary. This is because the Session object is not destroyed until the server has finished processing the script.
<%
Session.Abandon
Session("MyName") = "Mary"
Reponse.Write(Session("MyName"))
%>
If you access the variable MyName on a subsequent Web page, it is empty. This is because MyName was destroyed with the previous Session object when the page containing the previous example finished processing.
from MSDN Session.Abandon
Session.Abandon()
will destroy/kill the entire session.
Session.Clear()
removes/clears the session data (i.e. the keys and values from the current session) but the session will be alive.
Compare to Session.Abandon() method, Session.Clear() doesn't create the new session, it just make all variables in the session to NULL.
Session ID will remain same in both the cases, as long as the browser is not closed.
Session.RemoveAll()
It removes all keys and values from the session-state collection.
Session.Remove()
It deletes an item from the session-state collection.
Session.RemoveAt()
It deletes an item at a specified index from the session-state collection.
Session.TimeOut()
This property specifies the time-out period assigned to the Session object for the application. (the time will be specified in minutes).
If the user does not refresh or request a page within the time-out period, then the session ends.
Clearing a session removes the values that were stored there, but you still can add new ones there. After destroying the session you cannot add new values there.
clear-its remove key or values from session state collection..
abandon-its remove or deleted session objects from session..
Existence of sessionid can cause the session fixation attack that is one of the point in PCI compliance. To remove the sessionid and overcome the session fixation attack, read this solution - How to avoid the Session fixation vulnerability in ASP.NET?.
I think it would be handy to use Session.Clear() rather than using Session.Abandon().
Because the values still exist in session after calling later but are removed after calling the former.
this code works and dont throw any exception:
Session.Abandon();
Session["tempKey1"] = "tempValue1";
One thing to note here that Session.Clear remove items immediately but Session.Abandon marks the session to be abandoned at the end of the current request. That simply means that suppose you tried to access value in code just after the session.abandon command was executed, it will be still there. So do not get confused if your code is just not working even after issuing session.abandon command and immediately doing some logic with the session.