Stiamo utilizzando un Session Provider out-of-process (ScaleOut) per un'applicazione ASP.NET e abbiamo notato che quando un oggetto che non è configurato correttamente per la mancata serializzazione entra inavvertitamente nella sessione sarà alla fine causare l'intero processo per terminare.Come proteggere i pool di applicazioni dalle eccezioni di serializzazione della sessione?
Riprodurre e gestire questo scenario è dove diventa ancora più interessante.
L'eccezione che termina il processo è sollevata in AnyStaObjectsInSessionState la cui realizzazione è abbastanza semplice:
internal static bool AnyStaObjectsInSessionState(HttpSessionState session)
{
if (session != null)
{
int count = session.Count;
for (int i = 0; i < count; i++)
{
object obj2 = session[i];
if (((obj2 != null) && (obj2.GetType().FullName == "System.__ComObject"))
&& (UnsafeNativeMethods.AspCompatIsApartmentComponent(obj2) != 0))
{
return true;
}
}
}
return false;
}
Ecco la traccia dello stack che mostra come le eccezioni qui terminano il processo:
An unhandled exception occurred and the process was terminated.
Application ID: /LM/W3SVC/1/ROOT
Process ID: 4208
Exception: System.Runtime.Serialization.SerializationException
Message: The constructor to deserialize an object of type 'Lucene.Net.QueryParsers.ParseException' was not found.
StackTrace: at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder holder)
at System.Runtime.Serialization.ObjectManager.DoFixups()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Web.Util.AltSerialization.ReadValueFromStream(BinaryReader reader)
at System.Web.SessionState.SessionStateItemCollection.ReadValueFromStreamWithAssert()
at System.Web.SessionState.SessionStateItemCollection.DeserializeItem(String name, Boolean check)
at System.Web.SessionState.SessionStateItemCollection.DeserializeItem(Int32 index)
at System.Web.SessionState.SessionStateItemCollection.get_Item(Int32 index)
at System.Web.SessionState.HttpSessionStateContainer.get_Item(Int32 index)
at System.Web.Util.AspCompatApplicationStep.AnyStaObjectsInSessionState(HttpSessionState session)
at System.Web.HttpApplicationFactory.FireSessionOnEnd(HttpSessionState session, Object eventSource, EventArgs eventArgs)
at System.Web.SessionState.SessionOnEndTargetWorkItem.RaiseOnEndCallback()
at System.Web.Util.WorkItem.CallCallbackWithAssert(WorkItemCallback callback)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)
InnerException: System.Runtime.Serialization.SerializationException
Message: The constructor to deserialize an object of type 'Lucene.Net.QueryParsers.ParseException' was not found.
StackTrace: at System.Runtime.Serialization.ObjectManager.GetConstructor(Type t, Type[] ctorParams)
at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
Noi vorrei capire due cose:
Quando fa il fuoco FireSessionOnEnd per un fornitore out-of-process e, cosa più importante, come possiamo imitare questo in un ambiente di sviluppo che non è sotto carico? Ho sperimentato un timeout di sessione ridotto (impostato su un minuto), richiamando manualmente Abandon() e invocando manualmente GC.Collect(), tutto senza alcun risultato.
È possibile intercettare gli errori che si verificano in questo passaggio per proteggere il pool di applicazioni? Le eccezioni qui riportate sono registrate con Origine = ASP.NET 2.0.50727.0 e non raggiungono i gestori di errori dell'applicazione in global.asax. Che cosa possiamo fare per evitare questo scenario, anche dopo i controlli appropriati, i saldi & vengono applicati agli oggetti legati alla sessione?
Eventuali approfondimenti sarebbero apprezzati.
Forse dovrebbero risolvere il loro codice per non consentire un'eccezione serialziation per arrestare l'AppDomain. Solo un pensiero. –
È una buona idea, e penso che siano d'accordo; è uno scenario estremamente raro ma che va comunque protetto. – Nariman
@ John: Sembra che stia usando Lucene.net. Anche se è un'app di terze parti, AFAIK, il suo open source e dovrebbe contrassegnare la classe "Serializable' – ram