Replacing ASP.Net's session entirely
Asked Answered
C

1

48

ASP.Net session appear perfect for a traditional WebForms app, but they do some things that are a serious problem for a modern AJAX and MVC application.

Specifically there are only 3 ways to access the ASP.Net provider:

  1. Locking read & write (default) - the session is locked from AcquireRequestState firing until ReleaseRequestState fires. If 3 requests occur from the browser at once they'll queue up on the server. This is the only option in MVC 2, but MVC 3 allows...

  2. Non-locking read only - the session isn't locked, but can't be saved to. This appears to be unreliable though, as some reads appear to lock the session again.

  3. Session disabled - any attempt to read or write to the session throws an exception.

However with a modern MVC app I have lots of AJAX events happening at once - I don't want them the queue on the server but I do want them to be able to write to the session.

What I want is a fourth mode: Dirty read, last write wins

I think (happy to be corrected) that the only way to do this is to completely replace ASP.Net's sessions. I can write my own provider, but ASP will still call it with one of the 3 patters it supports. Is there any way to make ASP.Net support optimistic concurrency?

This leaves me replacing all calls to the session with a new class that basically does the same thing, but doesn't lock - which is a pain.

I want to keep as much of the current session stuff as I can (most significantly session IDs in various logs) with the minimum amount of code replacement. Is there any way to do this? Ideally I'd want HttpContext.Current.Session to point to my new class but without ASP.Net locking any requests.

Has anyone already done something like this? It seems odd that with all the AJAXey MVC apps out there this is a new problem with ASP.

Cerebrate answered 24/1, 2012 at 15:43 Comment(10)
If you write your own provider. Who is forcing you to lock even if ASP.NET asks you to lock the session? Just ignore the lock hint and do dirty reads as you please.Trypsin
@HasanKhan - I think (I'd like to know for sure) that SetAndReleaseItemExclusive (the method that saves values) only gets called during the ReleaseRequestState event if the locking has been applied. If there is some way to spoof that I'd like to know from someone that's done it; I can't be the first person to need optimisticly concurrent sessions and I'd rather not reinvent the wheel.Cerebrate
"I don't want them the queue on the server but I do want them to be able to write to the session." No, no you don't. If they need to write to the session they need to take turns to avoid inconsistent writes. Get as many requests as possible using option 3 to reduce blocking as much as you can, and leave it at that.Stiff
@Stiff - I don't care about inconsistent writes. As I say in the question I want last-write-wins. For my app (and I suspect many more) sessions hold user options (page size, current sort field, basket, etc) or cached values for optimisation - in either case, for my app, a last-write-wins model is what I want. I'm sure there are plenty of apps out there where the current locking model is perfect, but I can't be the only developer looking for optimistic concurrency in sessions.Cerebrate
@Keith, My suggestion is to get as many requests on option 3 as you can. Then see if you still have a problem.Stiff
@Cerebrate After all search that I have done up to now I think that this lock is on the very hard of asp.net processing and its up to microsoft to make this option works. The solution is to make a totally custom session keep variables. From the part of MS I think that is correct lock it, because the last write wins is not so simple - in sessions all variables are on the same struct, the last write wins can destroy all the times all variables again and again.Aldas
@Stiff I'm already doing that everywhere I can.Cerebrate
@Aldas Like I say at the beginning, the lock exclusive is ideal for lots of situations. I'm not arguing that optimistic concurrency should be the default, I'm asking if anyone else has already done it or found a better way to implement it.Cerebrate
@Cerebrate I am with you 100% and I have face this situation all ready, and your question is target very well on the problem. I tell you that, asp.net in this versions can not make everything, or think everything. Maybe on the future they solve it, or maybe some one light us with a solution. In my program I face similar problem with the cache - so I have made a totally custom cache to solve my problems. The same way with session, I am thinking to make a custom session from the start to handle the way I like the data. In the moment I have made a session like idea where I save the user last optionsAldas
@Cerebrate I have made this question some time now #2772036 because if you solve this one you can make a simple class that work on background with session data.Aldas
A
11

First of all I say that MS asp.net have lock in the core of "asp.net processing a page" the session, somewhere in the "webengine4.dll" dll for asp.net 4

And I say that from the view point of MS asp.net act correct and lock the session this way because asp.net can not know what type of information we keep on session so can make a correct "last write wins".

Also correct is lock for the session the full page because that way is gives your a very important synchronizations of your program, that from experience now I say you need it for most of your actions. After I have replace the ms session with mine, on all of my actions I need to make global lock, or else I have problems with double inserts, double actions, etc.

I give an example of the issue that I recognize here. Session data are saved on SessionSateItemCollection that is a list of keys. When session reads or write this collection is do it all of them together. So let see this case.

we have tree variables on session, "VAR1", "VAR2", "VAR3"

Page 1.
getsession (actually get all data and place them on list)
session[VAR1] = "data1";
savesession()
result is VAR1=data1, VAR2=(last data of var2), VAR3=(last data of var3)

Page 2.
getsession (actually get all data and place them on list)
session[VAR2] = "data2";
savesession()
result is VAR1=(last data of var1), VAR2=data2, VAR3=(last data of var3)

Page 3.
getsession (actually get all data and place them on list)
session[VAR3] = "data3";
savesession()
result is VAR1=(last data of var1), VAR2=(last data of var2) VAR3="data3"

Note here that each page have a clone of the data, as he read them from the session medium (eg as they last readed from database)

If we let this 3 pages run with out locking, we do not have actually dirty read, nether "last write wins" - What we have here is "the last write destroy the others", because if you think it again, when VAR1 change, why the VAR2 and VAR3 stay the same ? what if you have change VAR2 somewhere else.

and for that reason we can not let this with out lock as it is.

And now imaging that with 20 variables... totally mess.

Possible solutions

Because of the multithread of asp.net of many pools, the only way to keep the same data across pools, and across computers is to have a common database, or a common to all process program like the asp.net State Service.

I select to have a common database as more easy than create a program for that propose.

Now if we connect a cookie that we make, to the user to the data user of the session, and we control the data using a totally custom database field that we know what we like to lock, what not, how to save or change, or compare and keep the last write wins data, we can make this work, and totally disable the asp.net session that is a generic session keeper made for all needs, but not made to handle special cases like this one.

Can asp.net make this work on the future release, yes he can by making an extra field called dirty, and on the session save data, make a merge of data using the dirty field, or something like that, but can not make this now work as it is - at least from what I have found until now.
Actually SessionStateItem have a dirty flag on properties, but did not make this merge on the end of save data. I will really love if some one else think for a solution to the existing ms asp.net session state keeper, I am write what I have found up to now, but this is not mean that for sure there is no way - I have not found it as it is.

Hope that all helps :)

Custom SessionStateModule

in this answer https://mcmap.net/q/99717/-session-lock-causes-asp-net-websites-to-be-slow the James after write a custom module says: I still can't believe the custom implementation of ASP.Net Session locks the session for the whole request.

Aldas answered 26/1, 2012 at 16:32 Comment(7)
Excellent points (will +1 later), but it misses the fact that even 'locked' ASP sessions can be optimistic in a multi server environment anyway. If I need exclusive locking then I need it to work whether InProc on a single server or spread across a big farm. If I can't absolutely rely on exclusive locking then I have to treat everything as if it might be overwritten/discarded by a concurrent call anyway.Cerebrate
@Cerebrate Its only one field that asp.net save on data base - only one binary data field. One binary data field that keep all the keys. This can not be be under optimistic concurrency control because the one data is delete all the rest, or needs a merge method, or a lock for every key change, but the lock on every key change is making too slow, because here we speak that is database type lock.Aldas
Yeah, I get that - it's not just last-write-wins for one key-value pair, it's the entire session. It doesn't change the fact that the lock exclusive still doesn't guarantee an exclusive lock. I've got to code for the optimistic scenario whenever I use sessions, so why can't I make that the default?Cerebrate
@Cerebrate after that post, I made my totally custom session keeper. Its take me less time than the time I spend to find a solution to make ms work. The minus is that is totally custom solution and not a generic one like the asp.net. All the rest are super cool and actually I implemented the way you say it.Aldas
That's what I've ended up doing too - it was the only way I could be sure that some .Net component wouldn't try to lock the session or assume that it was exclusive.Cerebrate
@Cerebrate My tips:I use a global BasicPage over the Page. In this global I open the database with the OnPreInit, save only what have change when OnUnload. I lock the session class only when I made a new session and I unlock it right way after the new session. I left the save-lock on the database. This now is a little more smooth.Aldas
Mine is an MVC app with some legacy WebForms sections. I've plugged my solution in at the Global.asax level because it has to account for custom authentication too (we support SAML2 SSO, which ASP doesn't)Cerebrate

© 2022 - 2024 — McMap. All rights reserved.