Ervik
asked on
IIS return wrong page
I have a strange problem which I hope someone can help me solve.
I run a webshop and has just made a replement page for a main page (order confirm) at the webstore. The original page was named order.aspx, and my replasement was named order2.aspx. And we then routet to this new page when we switched. And removed the old page from the project, in Visual Studio 2010, compiled and deployed.
After good amount of testing on two testing webserververs IIS 6.0 and IIS 7.0 it was rolled out on our production server (which use IIS 6.0 on Windows server 2003). Everything seems fine, and we get feedback of how mucht better this page has been. But suddently, we get som alarming feedback. Some customers claim that they could see other customers shopping charts. (Which is not so good since they would also be able to see the discount other have on some products.) The website has around 1000 simultan users, and we get report on this error 0 - 3 times pr. day.
We could reproduce this behavior when we set something on the page to not validate, and keep sending (pressing the submit button) several times. At some time we have to press 8 - 15 times to get a bogus page back. At some time we never get bogus page, and we also some times could get the bogus page with just 2 - 4 submits.
We was never able to reproduce this on any test envoironment. We have to roll back to use the old order.aspx page, and the problem went away. But we really want to use this new version that fixes many other problems that was in the original page.
We have found that a probe is running every 8th minute to check that the website is up and running. And this script calls the old order.aspx page, that was not removed from the server, but is just markup, and no code, since the old order.aspx page was removed from the project befor compilation and deployment. (The old order.aspx pages markup was also left on the testsites where we could not reproduce the error.)
Is there any way the IIS could be tricked to return somthing if the probe tried to call the old order.aspx markup?
Or can any one see an other plausible sulution on this case?
I run a webshop and has just made a replement page for a main page (order confirm) at the webstore. The original page was named order.aspx, and my replasement was named order2.aspx. And we then routet to this new page when we switched. And removed the old page from the project, in Visual Studio 2010, compiled and deployed.
After good amount of testing on two testing webserververs IIS 6.0 and IIS 7.0 it was rolled out on our production server (which use IIS 6.0 on Windows server 2003). Everything seems fine, and we get feedback of how mucht better this page has been. But suddently, we get som alarming feedback. Some customers claim that they could see other customers shopping charts. (Which is not so good since they would also be able to see the discount other have on some products.) The website has around 1000 simultan users, and we get report on this error 0 - 3 times pr. day.
We could reproduce this behavior when we set something on the page to not validate, and keep sending (pressing the submit button) several times. At some time we have to press 8 - 15 times to get a bogus page back. At some time we never get bogus page, and we also some times could get the bogus page with just 2 - 4 submits.
We was never able to reproduce this on any test envoironment. We have to roll back to use the old order.aspx page, and the problem went away. But we really want to use this new version that fixes many other problems that was in the original page.
We have found that a probe is running every 8th minute to check that the website is up and running. And this script calls the old order.aspx page, that was not removed from the server, but is just markup, and no code, since the old order.aspx page was removed from the project befor compilation and deployment. (The old order.aspx pages markup was also left on the testsites where we could not reproduce the error.)
Is there any way the IIS could be tricked to return somthing if the probe tried to call the old order.aspx markup?
Or can any one see an other plausible sulution on this case?
If one user can see another users information that can mean only one thing. Some data is being stored in a static variable. On the test/development environment you might not be running into a peak load that is causing a problem with it. Look through the code thoroughly. You should NOT be using static for any data that is applied to a specific user.
ASKER
There is no static varibles on the page that post to the sever, where the error occurs.
There are a few static function in the system, mosty on the datalayer.
I seach for static variables in the whole sulution and found one strin array in the datalayer:
private static readonly string[] AllFacetFields = new[] { "Leverandør", "Hovedgruppe" };
I really can't see that this could be the culpit? (Leverandør means Vendor and Hovedgruppe mens maingroup.)
The system consist of several layers. (Database, DataService, BusinessRules, BusinessFacade, BusinessEnities and Web. The Web prosject consist of the markup and codebehind classes that is the actual web pages. The system is built on .net 1.1 and later converte to .net 4.0. It uses frames. On the order page only two other frames are present. The top frame, that shows the top meny and the current users, and the left frame that shows the menu.
There are a few static function in the system, mosty on the datalayer.
I seach for static variables in the whole sulution and found one strin array in the datalayer:
private static readonly string[] AllFacetFields = new[] { "Leverandør", "Hovedgruppe" };
I really can't see that this could be the culpit? (Leverandør means Vendor and Hovedgruppe mens maingroup.)
The system consist of several layers. (Database, DataService, BusinessRules, BusinessFacade, BusinessEnities and Web. The Web prosject consist of the markup and codebehind classes that is the actual web pages. The system is built on .net 1.1 and later converte to .net 4.0. It uses frames. On the order page only two other frames are present. The top frame, that shows the top meny and the current users, and the left frame that shows the menu.
Any static function can cause problems if it has any results specific to a user even temporarily in a database access layer. Remember each page request is stateless and the only way to maintain state is via a session and/or static objects that are shared while two requests are occurring concurrently.
Therefore if you have two users see the same data then it has to be a shared static session variable or a static object.
Therefore if you have two users see the same data then it has to be a shared static session variable or a static object.
ASKER
I don't know if two users sees the same data, only that one user can sometime see a page that contains info that is not for this user, but for a different user.
I have now gone through all static functions, but none of them are changed between the version that do not return wrong data, and the new version that sometimes return wrong data.
If a static function was the culpit, should't we get the same error even from the old posting page, or could there be a differense here?
I have now gone through all static functions, but none of them are changed between the version that do not return wrong data, and the new version that sometimes return wrong data.
If a static function was the culpit, should't we get the same error even from the old posting page, or could there be a differense here?
Not necessarily. Sometimes you change the workflow and load enough that the error will become more apparent.
If UserA can see the data for UserB then it is the 'Same' data. Sorry for not making that clear.
If UserA can see the data for UserB then it is the 'Same' data. Sorry for not making that clear.
ASKER
We have a class: public class EntityMapper which have serveral static functions like:
public static Order CopyToOrderDto(OrderEntity source) {
Order target=new Order();
target.Address=source.Addr ess;
target.CustomerId=source.C ustomerId;
target.DeliveryDate=source .DeliveryD ate;
target.OrderDate=source.Or derDate;
target.OrderId=source.Orde rId;
target.OrderValue=source.O rderValue;
target.PostalCode=source.P ostalCode;
target.ProjectDescription= source.Pro jectDescri ption;
target.ProjectID=source.Pr ojectID;
target.ReferenceLabel=sour ce.Referen ceLabel;
target.ReferenceNo=source. ReferenceN o;
target.ReferenceText=sourc e.Referenc eText;
target.ShowAlternativProdu ct=source. ShowAltern ativProduc t;
target.ShowDiscount=source .ShowDisco unt;
target.TransportNotice=sou rce.Transp ortNotice;
target.UserID=source.UserI D;
target.UserName=source.Use rName;
target.AddressCo = source.AddressCo;
target.CustomerProjectId = source.CustomerProjectId;
target.CustomerOrderId = source.CustomerOrderId;
target.CustomerDepartment = source.CustomerDepartment;
target.CustomerMobilePhone = source.CustomerMobilePhone ;
target.CustomerEmployeeId = source.CustomerEmployeeId;
target.CustomerWarehouse = source.CustomerWarehouse;
target.CustomerReference = source.CustomerReference;
target.OrderConfirmationCo de = source.OrderConfirmationCo de;
target.OrderConfirmationFa xNo = source.OrderConfirmationFa xNo;
target.CustomerEmail = source.CustomerEmail;
if (source.DeliveryType == 2)
{
target.FirstPriorityWareho useCode = source.ProffMarketCode;
target.ShippingMethodCodeF orFirstPri orityWareh ouse = "HE";
}
ArrayList orderItems=new ArrayList();
foreach (OrderItemEntity orderItem in source.OrderItems) {
orderItems.Add(CopyToOrder ItemDto(or derItem));
if (string.IsNullOrEmpty(orde rItem.Comm ent) == false)
{
OrderItemEntity commentOrderItem = new OrderItemEntity();
commentOrderItem.ProductId = "20";
commentOrderItem.Descripti on = orderItem.Comment;
orderItems.Add(CopyToOrder ItemDto(co mmentOrder Item));
}
}
target.OrderItems=(OrderIt em[]) orderItems.ToArray(typeof( OrderItem) );
return target;
}
This function maps the fields from the order object recived from the mainframe through MQ (IBM) - Biztalke and a webservece, to the order object used in the front end.
Several other functioions in this class maps other objects before sending and recivening to the mainframe. All this functions (or methods if you perfer) are declared as static.
Since only one instance of EntityMapper exist userA and userB will both use the same CopyToOrderDto methode and hence UserB can get userA's data. Which makes sense to me.
So the sulution will be to remove the static from the functions and change the calling of them to instance a new object each time. Can you confirm that this is what you recomend?
I will try to get accept for trying this. And if we tries it and the error goes away, then this case is solved.
(I'm a temp working on this project while the programmer is on sick leave. No other in the company works on .NET - only on mainframe (Cobol I guess))
public static Order CopyToOrderDto(OrderEntity
Order target=new Order();
target.Address=source.Addr
target.CustomerId=source.C
target.DeliveryDate=source
target.OrderDate=source.Or
target.OrderId=source.Orde
target.OrderValue=source.O
target.PostalCode=source.P
target.ProjectDescription=
target.ProjectID=source.Pr
target.ReferenceLabel=sour
target.ReferenceNo=source.
target.ReferenceText=sourc
target.ShowAlternativProdu
target.ShowDiscount=source
target.TransportNotice=sou
target.UserID=source.UserI
target.UserName=source.Use
target.AddressCo = source.AddressCo;
target.CustomerProjectId = source.CustomerProjectId;
target.CustomerOrderId = source.CustomerOrderId;
target.CustomerDepartment = source.CustomerDepartment;
target.CustomerMobilePhone
target.CustomerEmployeeId = source.CustomerEmployeeId;
target.CustomerWarehouse = source.CustomerWarehouse;
target.CustomerReference = source.CustomerReference;
target.OrderConfirmationCo
target.OrderConfirmationFa
target.CustomerEmail = source.CustomerEmail;
if (source.DeliveryType == 2)
{
target.FirstPriorityWareho
target.ShippingMethodCodeF
}
ArrayList orderItems=new ArrayList();
foreach (OrderItemEntity orderItem in source.OrderItems) {
orderItems.Add(CopyToOrder
if (string.IsNullOrEmpty(orde
{
OrderItemEntity commentOrderItem = new OrderItemEntity();
commentOrderItem.ProductId
commentOrderItem.Descripti
orderItems.Add(CopyToOrder
}
}
target.OrderItems=(OrderIt
return target;
}
This function maps the fields from the order object recived from the mainframe through MQ (IBM) - Biztalke and a webservece, to the order object used in the front end.
Several other functioions in this class maps other objects before sending and recivening to the mainframe. All this functions (or methods if you perfer) are declared as static.
Since only one instance of EntityMapper exist userA and userB will both use the same CopyToOrderDto methode and hence UserB can get userA's data. Which makes sense to me.
So the sulution will be to remove the static from the functions and change the calling of them to instance a new object each time. Can you confirm that this is what you recomend?
I will try to get accept for trying this. And if we tries it and the error goes away, then this case is solved.
(I'm a temp working on this project while the programmer is on sick leave. No other in the company works on .NET - only on mainframe (Cobol I guess))
Yes, that looks like the problem. No objects with specific customer data should be static on a web application. I wonder if it was done as a workaround to deal with a compile issue.
ASKER
I have a hard time try to convince my manager to try this. (He's hard to believe that code that has run fine for years, suddenly don't work correctly any more.)
Could it be that they who made the system had some sort of locking? I know I don't use one attribute that was used in the previous page. The attribute lead to a custom dll that is the property of the consulting company that has made the webshop in the first place, and we don't have source for this dll. The attribute is:
[FieldPersistence(FieldPer sistenceTy pe.Session )]
I removed it to se if there was any difference. But could not find any in testing, so this was not used in the version that produced the error.
>I wonder if it was done as a workaround to deal with a compile issue.
It could have been in an atemt to optimize it for speed. I have heard talk about a round of optimzation a few years ago. (Done by the same company.)
But using static function and som kind of locking is slower that using instances, right?
Could it be that they who made the system had some sort of locking? I know I don't use one attribute that was used in the previous page. The attribute lead to a custom dll that is the property of the consulting company that has made the webshop in the first place, and we don't have source for this dll. The attribute is:
[FieldPersistence(FieldPer
I removed it to se if there was any difference. But could not find any in testing, so this was not used in the version that produced the error.
>I wonder if it was done as a workaround to deal with a compile issue.
It could have been in an atemt to optimize it for speed. I have heard talk about a round of optimzation a few years ago. (Done by the same company.)
But using static function and som kind of locking is slower that using instances, right?
So to be clear, by removing the attribute the error disappeared? It might have been implemented to solve a perceived problem.
Using static functions with locking will cause performance and other problems because UserB would have to wait for UserA to unlock the record.
By the way, some errors might not show for years until the right conditions are met like load on the server.
Using static functions with locking will cause performance and other problems because UserB would have to wait for UserA to unlock the record.
By the way, some errors might not show for years until the right conditions are met like load on the server.
ASKER
>So to be clear, by removing the attribute the error disappeared? It might have been >implemented to solve a perceived problem.
No, you missunderstand. There was this attribute on the page_load method on the old page, but I skiped it on the new page. And we got the error when the attribut is missing. Have not tried it with the attribut set.
But anyway, it shoud be better to use instanced methods than to use static methods and some kind of locking, to prevent contention errors?
No, you missunderstand. There was this attribute on the page_load method on the old page, but I skiped it on the new page. And we got the error when the attribut is missing. Have not tried it with the attribut set.
But anyway, it shoud be better to use instanced methods than to use static methods and some kind of locking, to prevent contention errors?
OK, then put that attribute back. I'm not familiar with it but FieldPersistence(FieldPers istenceTyp e.Session) might force the data to be per session which is what you want.
If you get rid of the static methods you don't need locking. Without static methods each user will get their own copy of the object. Locking is painful on web applications because UserA has to wait for UserB's locks to clear if they are trying to get a page concurrently.
If you get rid of the static methods you don't need locking. Without static methods each user will get their own copy of the object. Locking is painful on web applications because UserA has to wait for UserB's locks to clear if they are trying to get a page concurrently.
ASKER
Hi tedbilly.
I'm pretty sure that your solution is correct, but I think I have to wait to this is proven, so it has to wait until my boss dessides to put it in production again. Sorry about that.
You don't have to answeere this post. I will log in and accept your solution, when proved.
I'm pretty sure that your solution is correct, but I think I have to wait to this is proven, so it has to wait until my boss dessides to put it in production again. Sorry about that.
You don't have to answeere this post. I will log in and accept your solution, when proved.
ASKER
So we have put it in production, on Wednesdays evening. (Static function changed to instanse functions. And instantiated when used.)
When I arrived at workt the next morning the first customer has called in, that he could see data form an other customer.
I then change the function back to static as it was before, and put in the attribute on the page
([FieldPersistence(FieldPe rsistenceT ype.Sessio n)]) and pushed it out to producktion. But just an hour or so, a new customer called in with the same problem.
So we have rolled back again to the old confirmation page (Order.aspx). And are back to square one.
When I arrived at workt the next morning the first customer has called in, that he could see data form an other customer.
I then change the function back to static as it was before, and put in the attribute on the page
([FieldPersistence(FieldPe
So we have rolled back again to the old confirmation page (Order.aspx). And are back to square one.
What are you storing in sessions? Is there anything static being stored in sessions?
ASKER
If you by session mean: Session["identfier"] = whatever, we only store some ID's for current user and so on.
Yes that is what I was talking about. Are there any objects stored in the Session that have a static member? That will cause problems.
ASKER
No objects as far as I'm aware of. Here is the complete list of Sessions in the Order2.aspx page:
private void PunchOutLogon(string _punchOutTicketKey)
TicketManager ticketManager = new TicketManager(60, list);
Ticket ticket = ticketManager.GetTicket(_p unchOutTic ketKey);
Session["PunchOutTicket"] = ticket;
Session["PunchOutTicket_Em ptyCart"] = "true";
Session["UserId"] = _user.UserID;
Session["ProjId"] = order.ProjectID;
Session["Error"] = ex.Message
Session["isPosted"] = "posted";
Session["Ref1"] = tbxReference1.Text.Trim();
Session["Ref2"] = tbxReference2.Text.Trim();
Session["email"] = tbxEmailAddress.Text.Trim( );
Session["mobil"] = txtCusMobile.Text.Trim().R eplace(" ", "");
Session["fax"] = txtFaxNo.Text.Trim().Repla ce(" ", "");
Session["ansattnr"] = txtEmployeeNo.Text.Trim();
Session["kundeOrdreNr"] = txtCusOrderNo.Text.Trim();
Session["kundeProjNr"] = txtCusProjectNo.Text.Trim( );
Session["kundeAvd"] = txtCusDepartment.Text.Trim ();
Session["kundeLager"] = txtCusWarehouse.Text.Trim( );
Session["rbMailSMS"] = rbMailSMS.SelectedIndex;
Session["AddressId"] = itemId; // itemId = int.Parse(ddAddressBook.Se lectedValu e);
Sessions are mostly used when saving state on the page when loading an other page (also new) that let users edit, add and delete adresses, and retrun back to where they where.
TicketManager is used for customers that have tight integration with the webshop via PunchOut. But they use a different website (different port), and no problem with that part of the program.
private void PunchOutLogon(string _punchOutTicketKey)
TicketManager ticketManager = new TicketManager(60, list);
Ticket ticket = ticketManager.GetTicket(_p
Session["PunchOutTicket"] = ticket;
Session["PunchOutTicket_Em
Session["UserId"] = _user.UserID;
Session["ProjId"] = order.ProjectID;
Session["Error"] = ex.Message
Session["isPosted"] = "posted";
Session["Ref1"] = tbxReference1.Text.Trim();
Session["Ref2"] = tbxReference2.Text.Trim();
Session["email"] = tbxEmailAddress.Text.Trim(
Session["mobil"] = txtCusMobile.Text.Trim().R
Session["fax"] = txtFaxNo.Text.Trim().Repla
Session["ansattnr"] = txtEmployeeNo.Text.Trim();
Session["kundeOrdreNr"] = txtCusOrderNo.Text.Trim();
Session["kundeProjNr"] = txtCusProjectNo.Text.Trim(
Session["kundeAvd"] = txtCusDepartment.Text.Trim
Session["kundeLager"] = txtCusWarehouse.Text.Trim(
Session["rbMailSMS"] = rbMailSMS.SelectedIndex;
Session["AddressId"] = itemId; // itemId = int.Parse(ddAddressBook.Se
Sessions are mostly used when saving state on the page when loading an other page (also new) that let users edit, add and delete adresses, and retrun back to where they where.
TicketManager is used for customers that have tight integration with the webshop via PunchOut. But they use a different website (different port), and no problem with that part of the program.
OK, you are storing a Ticket object in the session. Does that object have static methods or properties?
ASKER
I don't think so.
Here is the declaration and internal properties:
public class Ticket
{
private string password;
private string username;
private string userId;
private string browserFormPost;
private string buyerCookie;
private DateTime ticketCreated;
private Guid key;
private RequestOperation operation;
private CXMLEnvelope data;
private string projectId;
public Ticket(string username, string password, RequestOperation operation, string browserFormPost, string buyerCookie, string projectId, CXMLEnvelope request, string userId)
{
this.password = password;
this.username = username;
this.browserFormPost = browserFormPost;
this.buyerCookie = buyerCookie;
this.operation = operation;
this.ticketCreated = DateTime.Now;
this.key = Guid.NewGuid();
this.projectId = projectId;
this.data = request;
this.userId = userId;
}
private RequestOperation operation; -> this i just an enum
private CXMLEnvelope data; -> this is a normal public class se belove:
[Serializable]
[XmlRoot("cXML",IsNullable =true)] //, Namespace = "http://xml.cxml.org/schemas/cXML/1.2.016/cXML.dtd")]
public class CXMLEnvelope
{
There are hovever severalt other static classes in the system. But they are used for logging, and getting data from web.config and so on.
Here is the declaration and internal properties:
public class Ticket
{
private string password;
private string username;
private string userId;
private string browserFormPost;
private string buyerCookie;
private DateTime ticketCreated;
private Guid key;
private RequestOperation operation;
private CXMLEnvelope data;
private string projectId;
public Ticket(string username, string password, RequestOperation operation, string browserFormPost, string buyerCookie, string projectId, CXMLEnvelope request, string userId)
{
this.password = password;
this.username = username;
this.browserFormPost = browserFormPost;
this.buyerCookie = buyerCookie;
this.operation = operation;
this.ticketCreated = DateTime.Now;
this.key = Guid.NewGuid();
this.projectId = projectId;
this.data = request;
this.userId = userId;
}
private RequestOperation operation; -> this i just an enum
private CXMLEnvelope data; -> this is a normal public class se belove:
[Serializable]
[XmlRoot("cXML",IsNullable
public class CXMLEnvelope
{
There are hovever severalt other static classes in the system. But they are used for logging, and getting data from web.config and so on.
Hmm, well other than incorrect data, the only way this error occurs is shared state on the server and that only occurs with static values. There must be something hidden somewhere that is causing this effect.
ASKER
We are currently investating some caching that may cause this effect.
The Application Cache in ASP.NET is shared with all users. If you want data to be specific to a user, then use Sessions.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
The sulution offered by the expert, did not help i our situation, but may be correct in other senarios.