.NET Forum / ASP.NET / General / March 2008
Session management on web farm with sql server
|
|
Thread rating:  |
wdudek - 27 Feb 2008 19:49 GMT We have a website that works fine when hosted on a single server, however it experiences some strange problems when run on a web farm using SQL Server to handle session state. When running on the farm, in the same method call an object that just had a value set will no longer have the value set a couple of lines later. In this example the object is pulled out of session, altered and placed back in session before a following line of code tries to use the altered value, only to find out that it appears to have never been changed. The application essentially stores 1 object that contains several other objects in session. The group that manages teh web farm is telling me that this is the problem. They are saying that because of the serialization/deserialization in sql server, the reference to the inner objects isn't flowing through to the object being stored in SQL Server. Does this sound correct? Should there be any differance between how a website code funcitons on a single server versus a web farm? They provided me with the below documentation, but have not yet been able to tell me where it came from. This has been an ongoing battle over who's problem it really is, any information on this subject will be appreciated. Also as a note all of the objects are serializable.
Thanks,
Bill
This is the reponse I got from our techincal support group There is no problem with the SQL Server settings for storing the session as session objects get created, retrieved and updated in the SQL Server. The issue is with the way the session object is updated in the C# code. Session in asp.net behaves as follows.
InProc: In this mode session data is stored in the AppDomain of the application. All the objects stored in the session are actually stored in AppDomain and a reference is created for the session.
SQL Server/State Server: In this mode session data is serialized and stored in SQL server database. When sessions is loaded system will fetch the data from database and deserialize it and creates the objects with deserialize data and bind them to state bag. In this mode the actual object references and session object references are different. Because of this, the S&S application is able to retrieve data when session is in InProc but not in SQL Server. What's happening in S&S application is as below. In case of InProc session Inquiry object and Search object have same reference, so updating one object is will show effect all its references. In case of SQL Server session each object will have its own reference (each time new object is created from deserialize data), so updating one object will not update the other object in the session. To solve this, C# code needs to be modified where the session related objects are updated such that it gets updated in the session correctly.
Peter Bromberg [C# MVP] - 27 Feb 2008 21:51 GMT It is quite possible that the nested objects are not serializing correctly. At any rate, it's best *not* to try to store complex objects in Session of any type. Instead, store values and reconstruct your objects from the values stored in Session. -- Peter Site: http://www.eggheadcafe.com UnBlog: http://petesbloggerama.blogspot.com Short Urls & more: http://ittyurl.net
> We have a website that works fine when hosted on a single server, however > it experiences some strange problems when run on a web farm using SQL Server [quoted text clipped - 55 lines] > To solve this, C# code needs to be modified where the session related > objects are updated such that it gets updated in the session correctly. bruce barker - 27 Feb 2008 23:00 GMT session is a collection of objects. with in proc, this collection is a static collection. with out-of-proc session managers (like sqlsession), the session collection is an instance collection tired to the request. at the end of the request, it serialized out to the store. at the start of the request, its deserialied from the store. during request processing, there is no difference between inproc and out-of-proc sessions.
if you see a difference. then you object is not serializing/deserializing correctly. you will have to fix the logic. the most common issue, is not handling mulitple to references to the same object correctly.
simple example:
// first request
myObject obj = new myObject(); List<myObject> list = new List<myObject>(); list.Add(obj); list.Add(obj); Session["list"] = list; list[0].Value="1"; // list[0].Value == list[1].Value == "1"; list[0].Value="2"; // list[0].Value == list[1].Value == "2";
// next request
List<myObject> list = (myObject) Session["list']; list[0]="3"; // list[0].Value != list[1].Value
this is because at serialzztion time list[0] & list[1] where deserialized. when deserialiezed, two objects were created, but each with the same value. now changing one does not change the other.
so, if your object has mutiple refences to the same object (say the same object is in two collections) then you have to write a custom serializer.
the approach i use for complex objects I store in session, is there is a base collection that objects belong to, and other references just serialize the lookup key.
-- bruce (sqlwork.com)
> We have a website that works fine when hosted on a single server, however > it experiences some strange problems when run on a web farm using SQL Server [quoted text clipped - 55 lines] > To solve this, C# code needs to be modified where the session related > objects are updated such that it gets updated in the session correctly. wdudek - 28 Feb 2008 18:57 GMT Thanks, This explains exactly what is happening. I can look this up, but is anyone aware of any links that discuss this issue in more depth? I understand the concept of custom serialization but have never worked with it.
Thanks again
Bill
> session is a collection of objects. with in proc, this collection is a static > collection. with out-of-proc session managers (like sqlsession), the session [quoted text clipped - 96 lines] > > To solve this, C# code needs to be modified where the session related > > objects are updated such that it gets updated in the session correctly. Steven Cheng - 29 Feb 2008 05:15 GMT Hi Bill,
For binary serialization and implement your custom serialization approach, here are some reference that should be helpful:
#Run-time Serialization http://msdn.microsoft.com/msdnmag/issues/02/04/net/
#Serialization in the .NET Framework http://www.15seconds.com/issue/020903.htm
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
>From: =?Utf-8?B?d2R1ZGVr?= <wdudek@newsgroup.nospam> >References: <9E5A2E55-EA73-475D-A7E0-29C503F212D8@microsoft.com> <1D85C258-6104-4E4A-9F18-050635B2909D@microsoft.com>
>Subject: RE: Session management on web farm with sql server >Date: Thu, 28 Feb 2008 10:57:02 -0800
>Thanks, > This explains exactly what is happening. I can look this up, but is [quoted text clipped - 105 lines] >> > To solve this, C# code needs to be modified where the session related >> > objects are updated such that it gets updated in the session correctly. wdudek - 29 Feb 2008 16:56 GMT Thanks,
There isn't much out there regarding this problem. My background is more windows oriented than web, but it seems that if web sites really are going to replace the desktop application as allot of people would have you believe that this particular issue would come up more often. Just my 2 cents, but thanks again for the links I'll take a look.
Bill
> Hi Bill, > [quoted text clipped - 165 lines] > >> > objects are updated such that it gets updated in the session > correctly. Steven Cheng - 03 Mar 2008 02:08 GMT Thanks for your followup Bill,
For serialization, since winform/desktop application can hold most objects in memory while ASP.NET web application is stateless(request, response based) and may work between client and server machines, serialization plays more important role here.
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
>From: =?Utf-8?B?d2R1ZGVr?= <wdudek@newsgroup.nospam> >References: <9E5A2E55-EA73-475D-A7E0-29C503F212D8@microsoft.com> <1D85C258-6104-4E4A-9F18-050635B2909D@microsoft.com> <A1F1F814-7CB8-43CB-A481-5902580998E3@microsoft.com> <bLKqzIpeIHA.360@TK2MSFTNGHUB02.phx.gbl>
>Subject: RE: Session management on web farm with sql server >Date: Fri, 29 Feb 2008 08:56:04 -0800
>Thanks, > [quoted text clipped - 175 lines] >> >> > objects are updated such that it gets updated in the session >> correctly. Nathan - 03 Mar 2008 22:26 GMT Steven:
I am having a similar problem to Bill.
My web application works correctly when running with In Process Session State, but loses data when running with SQL Server Session State. After weeks of debugging, using the VS 2005 debugger and adding code to log object/variable states to a file, it appears that when I change the content of a collection which is referenced by an object stored in Session, the update is only recognized while running with In Process Session State.
I am guessing that the session serialization code only checks the root object for changes (if that) before deciding whether to re-serialize that object and sending it to the session database. As a result, my changes are never stored in the session database and cannot be de-serialized when the page posts back or I move on to the next page.
Do you know whether this is correct? If so, how do I work around it so my changes to subordinate objects are propogated to session? If not, what else should I look for and what else could I do to ensure my changes get stored into the session database?
Thanks, Nathan
> Thanks for your followup Bill, > [quoted text clipped - 227 lines] >>> >> > objects are updated such that it gets updated in the session >>> correctly. Steven Cheng - 04 Mar 2008 02:02 GMT Thanks for your input Nathan,
Yes, I think you're running into the same problem. Bruce's explanation is quite good, here is his previous reply for your reference:
================ session is a collection of objects. with in proc, this collection is a static collection. with out-of-proc session managers (like sqlsession), the session collection is an instance collection tired to the request. at the end of the request, it serialized out to the store. at the start of the request, its deserialied from the store. during request processing, there is no difference between inproc and out-of-proc sessions.
if you see a difference. then you object is not serializing/deserializing correctly. you will have to fix the logic. the most common issue, is not handling mulitple to references to the same object correctly.
simple example:
// first request
myObject obj = new myObject(); List<myObject> list = new List<myObject>(); list.Add(obj); list.Add(obj); Session["list"] = list; list[0].Value="1"; // list[0].Value == list[1].Value == "1"; list[0].Value="2"; // list[0].Value == list[1].Value == "2";
// next request
List<myObject> list = (myObject) Session["list']; list[0]="3"; // list[0].Value != list[1].Value
this is because at serialzztion time list[0] & list[1] where deserialized. when deserialiezed, two objects were created, but each with the same value. now changing one does not change the other.
so, if your object has mutiple refences to the same object (say the same object is in two collections) then you have to write a custom serializer.
the approach i use for complex objects I store in session, is there is a base collection that objects belong to, and other references just serialize the lookup key.
-- bruce (sqlwork.com) ================================
to resolve this, you can consider:
1. change the data structure you use and avoid nested class members of reference type.
2. add custom binary serialization for your class. For example, for those class members that originally stored as reference, you may need to always serialize them into a value type data (such as a byte array or a string) so that you can ensure it is always copied and updated with the root object.
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
>Reply-To: "Nathan" <nathan.e.duncanson@lmco.com> >From: "Nathan" <nathan.e.duncanson@lmco.com> >Subject: Re: Session management on web farm with sql server >Date: Mon, 3 Mar 2008 16:26:12 -0600
>Steven: > [quoted text clipped - 20 lines] >Thanks, >Nathan Nathan - 05 Mar 2008 00:04 GMT I inherited the application I am working with. When it was written, the original author(s) freely stored complex custom objects in session, fully expecting their component objects to also be stored and readily accessed after posting back the current page or navigating to new pages. Because the application was written using "In Process" session state, everything worked -- until we put it in a load balanced server farm.
To make the application work with "out of process" session state, I marked every class [Serializable] and manually coded an implementation of the ISerializable interface -- both the GetObjectData method and a de-serialization constructor. In the GetObjectData method, I call the SerializationInfo.AddValue(string, object, Type) method for each field of the class. For collections, I call custom methods that iterate through each element, modifying the identifier string with the collection indices. In the de-serialization constructor, I do the reverse calling the SerializationInfo.GetValue(string, Type) method and casting as appropriate.
Example:
[Serializable] class MyClass : MyBase, ISerializable { public MyClass () { } public MyClass(SerializationInfo Info, StreamingContext Context) : base(Info, Context) { _field = ()Info.GetValue("field", typeof()); _propertiesList = MiscUtilities.DeserializeHashtable("propertiesList", Info, Context); }
private MyOtherClass _field = null; public MyOtherClass Field { get { return _field; } set { _field = value; } }
private Hashtable _propertiesList = new Hashtable(); public MyBase this[string key] { get { return (MyBase)_propertiesList[key]; } set { if (value is MyBase) { _propertiesList[key] = value; } } }
public void GetObjectData(SerializationInfo Info, StreamingContext Context){ base.GetObjectData(Info, Context); Info.AddValue("field", _field, typeof()); MiscUtilities.SerializeHashtable("propertiesList", _propertiesList, Info, Context); } }
class MyBase : ISerializable { // constructors MyBase(){}; MyBase(..., ...) {...}; // fields & properties // methods GetObjectData(..., ...) {...}; }
class MyOtherClass : MyBase, ISerializable { // constructors MyBase(){}; MyBase(..., ...) {...}; // fields & properties // methods GetObjectData(..., ...) {...}; }
class MiscUtilities { SerializeHashtable(string name, Hashtable table, SerializationInfo Info, StreamingContext Context) { int length = table.Count; Info.AddValue(name + ".length", length, typeof(int)); int i = 0; foreach(DictionaryEntry entry in table) { Info.AddValue(name + "[" + i.ToString() + "].key", entry.Key, typeof(string)); Type valueType = (entry.Value != null) ? entry.Value.GetType() : typeof(null); Info.AddValue(name + "[" + i.ToString() + "].valueType", valueType, typeof(Type)); Info.AddValue(name + "[" + i.ToString() + "].value", entry.Value, valueType); } } DeserializeHashtable(string name, SerializationInfo Info, StreamingContext Context) { Hashtable hOut = new Hashtable(); int length = Info.GetInt32(name + ".length"); for(int i = 0; i < length; i++) { string key = Info.GetString(name + "[" + i.ToString() + "].key", typeof(string)); Type valueType = (Type)Info.GetValue(name + "[" + i.ToString() + "].valueType", typeof(Type)); object value = Info.GetValue(name + "[" + i.ToString() + "].value", valueType); hOut[key] = value; } return hOut; } // other collection serialization and de-serialization routines (ArrayList, ICollection, IDictionary, ...) }
The above code is a very simplified example of what I am actually working with, but I think it conveys the idea that I have manually implemented the ISerializable interface to ensure each field of every custom object gets properly serialized and de-serialized. But I am still losing data between postbacks and during page navigation which should have been stored in session. While running the debugger, I set breakpoints in the "GetObjectData" methods of the disappearing objects -- the debugger never stops in those objects' GetObjectData methods. The objects are stored in a custom collection, which is attached to yet another custom object and that object is the one placed into session.
From the documentation I have read, I believe the binary formatter is supposed to traverse the object graph from the topmost object all the way down to the bottom until every referenced object and collection has been serialized. How come that is not happening? The top level object, which is directly placed into session gets serialized and deserialized, but the collections and objects it references don't seem to make it. What am I missing?
Somewhere in code:
MyClass MyObject = new MyClass(); // MyObject properties are set, collections are filled,
Later, in code references are made and passed to MyObject as various methods operate on it or pass it on to another method. Eventually, a reference to MyObject is passed to function that stores it in session.
public void SomeMethod(MyBase obj) { // ... Session["MyObject"] = obj; // ... }
When following along with the debugger, it seems that "obj" really is "MyObject". It has all the same values, it references all the same objects and collections, its collections reference the same set of objects; all the changes as methods operated on it and its components are still present. However, when I write the Session object's contents to a log file, after Session["MyObject"] has been set to "obj", the content of Session["MyObject"] has lost changes that were made to MyObject's components. Collections are reset to a previous state and component objects may also have lost their updates. What is going on? If I run the application with "In Process" session state the updates remain after placing the top level object into session -- they only disappear when running with "out of process" session state.
Steven Cheng - 05 Mar 2008 01:21 GMT Hi Nathan,
I think the problem here may be due to the collection class or any other further container class that will hold reference to your custom objects. Yes, you've implemented custom class's serialziation. However, for webfarm sql session state scenario, as Bruce has mentioned that you need to take care of the root class objects stored in session. For example, if some other class instance(which contains your custom type as member) is stored in session, you will need to take care of the serialization from that class(provide a customer wrapper class that implement custom serialziation). Also, have you tried put your custom class object directly in session rather than some base class reference to see whether it works?
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
This posting is provided "AS IS" with no warranties, and confers no rights.
-------------------- <eIzLjvZfIHA.1500@TK2MSFTNGHUB02.phx.gbl>
>Subject: Re: Session management on web farm with sql server >Date: Tue, 4 Mar 2008 18:04:04 -0600
>I inherited the application I am working with. When it was written, the original author(s) freely stored complex custom objects in session, fully expecting their component objects to also be stored and readily accessed after posting back the current page or navigating to new pages. Because the application was written using "In Process" session state, everything worked -- until we put it in a load balanced server farm.
>To make the application work with "out of process" session state, I marked every class [Serializable] and manually coded an implementation of the ISerializable interface -- both the GetObjectData method and a de-serialization constructor. In the GetObjectData method, I call the SerializationInfo.AddValue(string, object, Type) method for each field of the class. For collections, I call custom methods that iterate through each element, modifying the identifier string with the collection indices. In the de-serialization constructor, I do the reverse calling the SerializationInfo.GetValue(string, Type) method and casting as appropriate.
>Example: >[Serializable] [quoted text clipped - 64 lines] >} >The above code is a very simplified example of what I am actually working with, but I think it conveys the idea that I have manually implemented the ISerializable interface to ensure each field of every custom object gets properly serialized and de-serialized. But I am still losing data between postbacks and during page navigation which should have been stored in session. While running the debugger, I set breakpoints in the "GetObjectData" methods of the disappearing objects -- the debugger never stops in those objects' GetObjectData methods. The objects are stored in a custom collection, which is attached to yet another custom object and that object is the one placed into session.
>From the documentation I have read, I believe the binary formatter is supposed to traverse the object graph from the topmost object all the way down to the bottom until every referenced object and collection has been serialized. How come that is not happening? The top level object, which is directly placed into session gets serialized and deserialized, but the collections and objects it references don't seem to make it. What am I missing?
>Somewhere in code: > MyClass MyObject = new MyClass(); [quoted text clipped - 7 lines] > } >When following along with the debugger, it seems that "obj" really is "MyObject". It has all the same values, it references all the same objects and collections, its collections reference the same set of objects; all the changes as methods operated on it and its components are still present. However, when I write the Session object's contents to a log file, after Session["MyObject"] has been set to "obj", the content of Session["MyObject"] has lost changes that were made to MyObject's components. Collections are reset to a previous state and component objects may also have lost their updates. What is going on? If I run the application with "In Process" session state the updates remain after placing the top level object into session -- they only disappear when running with "out of process" session state.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|