Link to home
Start Free TrialLog in
Avatar of Ervik
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?
Avatar of Ted Bouskill
Ted Bouskill
Flag of Canada image

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.
Avatar of Ervik
Ervik

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.
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.
Avatar of Ervik

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?
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.
Avatar of Ervik

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.Address;
      target.CustomerId=source.CustomerId;
      target.DeliveryDate=source.DeliveryDate;
      target.OrderDate=source.OrderDate;
      target.OrderId=source.OrderId;
      target.OrderValue=source.OrderValue;
      target.PostalCode=source.PostalCode;
      target.ProjectDescription=source.ProjectDescription;
      target.ProjectID=source.ProjectID;
      target.ReferenceLabel=source.ReferenceLabel;
      target.ReferenceNo=source.ReferenceNo;
      target.ReferenceText=source.ReferenceText;
      target.ShowAlternativProduct=source.ShowAlternativProduct;
      target.ShowDiscount=source.ShowDiscount;
      target.TransportNotice=source.TransportNotice;
      target.UserID=source.UserID;
      target.UserName=source.UserName;

            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.OrderConfirmationCode = source.OrderConfirmationCode;
            target.OrderConfirmationFaxNo = source.OrderConfirmationFaxNo;
            target.CustomerEmail = source.CustomerEmail;
            if (source.DeliveryType == 2)
            {
      target.FirstPriorityWarehouseCode = source.ProffMarketCode;
      target.ShippingMethodCodeForFirstPriorityWarehouse = "HE";
            }
                  ArrayList orderItems=new ArrayList();
      foreach (OrderItemEntity orderItem in source.OrderItems) {
            orderItems.Add(CopyToOrderItemDto(orderItem));
            if (string.IsNullOrEmpty(orderItem.Comment) == false)
            {
            OrderItemEntity commentOrderItem = new OrderItemEntity();
            commentOrderItem.ProductId = "20";
            commentOrderItem.Description = orderItem.Comment;
            orderItems.Add(CopyToOrderItemDto(commentOrderItem));
            }
      }
      target.OrderItems=(OrderItem[]) 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))
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.
Avatar of Ervik

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(FieldPersistenceType.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?
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.
Avatar of Ervik

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?
OK, then put that attribute back.  I'm not familiar with it but FieldPersistence(FieldPersistenceType.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.
Avatar of Ervik

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.
Avatar of Ervik

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(FieldPersistenceType.Session)]) 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.
What are you storing in sessions?  Is there anything static being stored in sessions?
Avatar of Ervik

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.
Avatar of Ervik

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(_punchOutTicketKey);
Session["PunchOutTicket"] = ticket;
Session["PunchOutTicket_EmptyCart"] = "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().Replace(" ", "");
Session["fax"] = txtFaxNo.Text.Trim().Replace(" ", "");
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.SelectedValue);

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?
Avatar of Ervik

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.
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.
Avatar of Ervik

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
Avatar of Ervik
Ervik

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Ervik

ASKER

The sulution offered by the expert, did not help i our situation, but may be correct in other senarios.