Request size en session size

Momenteel zit ben ik werkzaam bij een organisatie waar een ISA server als firewall dient en deze heeft een maximum ingesteld op de grootte van de Request Payload. Dit was tot voor kort geen probleem, totdat de ISA server werd geherconfigureerd.

De Request Payload is het stuk code dat de pagina van zichzelf doorstuurt naar een andere (of dezelfde) pagina. Dit is bijvoorbeeld de ViewState en alle controls en waarden binnen een form. Op een bepaalde pagina heel vaak een postback worden gedaan en iedere keer dat dit gebeurde werd de Request groter en groter. Immers, die viewstate wordt groter en alle controls met waarden worden meegestuurd.

Uiteraard wilde ik wel weten hoe groot de requests dan worden. Momenteel stond het limiet ingesteld op 5000 bytes. Dat klinkt wel als veel, maar ik heb er nooit bij stil gestaan hoe groot een request binnen ASP.NET nu eigenlijk normaal is.

Hiervoor heb ik het volgende stukje code maar in de footer van de masterpage binnen de applicatie toegevoegd:

<asp:Content
<span lang="EN-GB" style="font-family:; mso-ansi-language: en-gb;"><font style="font-size: 10pt;"><span><font color="#ff0000">ContentPlaceHolderID="PlaceHolderFooter" ID="ContentFooter" runat="server">
<asp:Literal ID="PageFooterText" runat="server" />
<asp:Literal ID="PageRequestSize" runat="server" />
<asp: Content>

En in de Page Load het volgende:

PageRequestSize.Text = string.Format("Request size: {0} bytes", Request.TotalBytes);

Nu kon ik bij het bezoeken van de pagina’s gelijk zien hoe groot de requests eigenlijk waren.

Tot mijn verbazing kwam de grootte al binnen enkele kliks boven de 15.000 bytes uit, tot maximaal zo’n 38.000 bytes. Best grote requests!
Ook als ik de bron van de pagina bekeek zag ik dat de ViewState enorm groot was (na enkele postbacks). Hier zou behoorlijk wat winst op behaald kunnen worden door die te verkleinen. Het waren dan wel geen 35.000 tekens, maar zou toch voor een aanzienlijke verkleining van het request zorgen. Idealiter zou je hier een AJAX oplossing voor willen bouwen, zodat de pagina dynamisch wordt opgebouwd, maar dat valt natuurlijk niet binnen de scope van het huidige project. Misschien later nog eens.

Om de ViewState te verkleinen kwam ik al snel uit op de SessionPageStatePersister. Door deze te gebruiken wordt de ViewState enorm verkleind, een voorbeeld is op deze link te vinden: https://msdn.microsoft.com/en-us/library/aa479403.aspx

Het enige dat je hiervoor hoeft te doen is het volgende stuk code toevoegen aan de pagina’s (of basepage natuurlijk):

public override PageStatePersister GetStatePersister()
{
    return new SessionPageStatePersister(Page);
}

Nadat dit is gedaan zal er alleen een referentie naar het Session object worden meegegeven in de request en de ViewState zelf zal dan in de sessie worden opgeslagen.

Nadeel van deze oplossing is natuurlijk dat die enorme ViewState nu op de server wordt opgeslagen binnen het Sessie object. Gelukkig zijn er geen duizenden gebruikers die gebruik maken van deze web applicatie, maar toch vonden de beheerders het wel nuttig om te weten wat voor impact dit zou hebben op de server resources. Logisch natuurlijk.

Omdat ik niet met zekerheid kon zeggen wat de exacte grootte van de ViewState binnen het sessie object zou worden moest ik dit dus onderzoeken. Het kan immers zo zijn dat de server hier compressie op uitoefent, de ViewState helemaal wordt uitgeschreven, of op een andere manier wordt opgeslagen binnen de sessie. Hiervoor moet ik dus de grootte van het sessie object zien te achterhalen. Hier kon ik niet een duidelijk antwoord op vinden, dus had ik maar bedacht om alle objecten binnen de sessie langs te lopen en de grootte er van op te vragen. Dit ging redelijk goed, echter zijn er managed objecten die niet geserializeerd willen worden, waardoor dit dus niet (ik weet tenminste niet hoe) kon bepalen hoe groot die waren.

Uiteindelijk ben ik tot het volgende stuk code gekomen. Het werkt nog niet perfect, maar geeft in ieder geval een indruk van hoe groot de sessie ongeveer zou kunnen zijn. Er zijn objecten waarvan ik de grootte niet kan bepalen, dus die vallen jammergenoeg buiten de telling.

long size = 0;
int nonSerializable = 0;
for (int i = 0; i < Session.Count; i++)
{
	object o = new object();

	using (Stream s = new MemoryStream())
	{
		BinaryFormatter formatter = new BinaryFormatter();
		var sessionItem = Session[i];
		if (sessionItem != null)
		{
			var itemType = sessionItem.GetType();
			try
			{
				formatter.Serialize(s, sessionItem);
				size += s.Length;
			}
			catch (SerializationException)
			{
				nonSerializable++;
				int sizeMarshal = Marshal.SizeOf(sessionItem);
				size += sizeMarshal;
			}
		}
	}
}

Op deze manier weet ik ook van hoeveel objecten de grootte niet kon worden bepaald. Nadat ik deze code had gemaakt kreeg ik het bericht dat de Request Payload van de ISA zou worden verhoogd, dus al het uitzoekwerk was niet meer noodzakelijk. Toch heb ik er weer wat van opgestoken, dus was het een nuttige bugmelding.


Share