Customers loading carts associated with other customers

Vote:
 

We're facing a perplexing issue in our production environment where customers appear to occasionally get mapped to other people's carts (e.g. retrieving someone else's cart viewmodel, adding/removing items to someone else's cart, or even checking out with someone else's cart). We're obviously very concerned, but we've been unsuccessful in replicating it in dev, and its low frequency (combined with its seemingly random occurrence) has made it a somewhat lower-priority issue, so I can't investigate it full-time. I've pored over the relevant code and can't see anything suspicious. All cart loading is facilitated by the following method:

        public ICart LoadCart(string name = "Default")
        {
            var cart = _orderRepository.Service.LoadOrCreateCart<ICart>(System.Web.HttpContext.Current.User.GetContactId(), name, _currentMarketService.Service);
            SanitiseCart(cart);
            return cart;
        }

Some notes for clarity:

  • We have only a single market, and only ever use the cart name "Default".
  • SanitiseCart() is a private method that that performs some minor housekeeping on line items and shipments.
  • _orderRepository is declared as private Injected<IOrderRepository> _orderRepository;
  • Obviously, none of our cart operations are trying to mutate the CustomerId.
  • We use GetContactId() at multiple other points in the project (e.g. loading customer accounts, order history summaries) and have not heard of any similar issues in those domains.
  • This issue appears to be affecting both anonymous users and registered customers (i.e. CustomerContacts).
  • We're uncertain how long it has been a problem, but a series of logged exceptions (wherein our system tries to remove an item that doesn't exist in the cart) suggest that it may have started after we performed a major Commerce version upgrade several months ago. Unfortunately, the developer who performed the upgrade no longer works at this company, and in any case, I can't find any evidence of significant changes within the cart domain.
  • Normally this is a fairly rare occurrence, but we experienced a sudden large spike in incidents roughly a week ago. Episerver support were contacted and asked to reset the app service, at which point the frequency of incidents dropped to its prior level. We are still uncertain what incited this sudden spike.

The project in which the above code is located is currently on Commerce 13.2.0.

#210766
Dec 05, 2019 8:17
Vote:
 

Tricky! There are two things that comes to mind, not from seeing your code but rather the behavior. We've had that as well, a couple of years ago. We had it twice:

  • One time it was when we were fetching customers based on email, with a Search-method instead of direct lookup, meaning dots became wildcards for some reason.. This is not the issue here, since you're directly looking at the IPrincipal.ContactId
  • The other one is that you have a "singleton leak" somewhere in your app. I.e. You have a service that's registered singleton but has a state, maybe a local cart it uses between method or the more tricky one, that you have a non-singleton service/instance that uses cart in a field but is depending on a singleton instance, so it's singleton-by-dependency.

I'd definately try to look at the dependency graph nearest to where you know this is happening, to see if something is singleton or singleton-by-dependency where it shouldn't.

#210770
Dec 05, 2019 9:08
Vote:
 

Is this something you looked for https://vimvq1987.com/watch-out-for-singletons/ ? 

#210771
Dec 05, 2019 9:27
Vote:
 

Indeed :) But do use Singletons if you are expected to scale high! :D

#210773
Dec 05, 2019 10:03
Vote:
 

I will repeat Joels concern about singletons. The nastiest bugs I have met in real life were caused by singletons.

Remember that an Episerver content (page/block) is a singleton. Every visitor will access the same in-memory instance of a block. If there is any kind of relation from that block to a Cart, then every visitor could access the same Cart instance.

Have you tried reproducing with multiple people acting at the exact same moment ?

#210784
Dec 05, 2019 16:19
Vote:
 

Thanks for the pointers. To my surprise, it seems that our CartService (in which the above method resides) is actually registered as transient, despite not featuring any persistent variables. I would assume that there's no risk of a clash if two separate instances of the class both call LoadOrCreateCart() simultaneously, but it seems worth noting.

I've been looking through the components that call LoadCart() and I've yet to find evidence of shared singleton states. They're mostly controller endpoints that load the cart in a request, perform operations directly and then save it (bit gross, I know, but this app allegedly had a horrid development history). Continued investigation might turn something up, but it'd have to be really obscure.

I haven't tried reproducing it via multiple users acting at the same time. I sort of figured if that was all it took, we'd be seeing it a lot more often. Nevertheless, I suppose if the time window is small enough... maybe I can set up some kind of script to hit the endpoints rapidly.

#210803
Dec 06, 2019 5:14
Vote:
 

Hi fellow people of Episerver forum.

I am putting this thread back to life as I happen to be the dev who took over the project mentioned by OP (who has left since). This issue has had some bigger impacts recently, just thought I could share it here with my findings, and see if anyone would have ideas about how to solve it.

So, long story short, we have anonymous profiles enabled on our eCommerce website (<anonymousIdentification enabled="true" /> in web.config) which activates Microsoft  AnonymousIdentificationModule. This allows guests users to act as normal users. 

It seems that the issue we're having is that, in some scenarios that i haven't been able to reproduce yet, some anonymous users have been assigned an already existing user id of the CustomerContact entity/table. Which then cause carts, wishlists, order history etc... to be kinda shared between some users. Not ideal.

I don't know if it comes from how anonymous ids are generated in the AnonymousIdentificationModule, or if we are having one of those singleton issues mentioned above (although I could not find anything in that space), but it is happening fairly rarely, which does not help to solve.

FYI, All the logic related to generating ids and retrieving the correct customercontact seems to live in Mediachase.Commerce.Security.PrincipalExtensions, look at GetContacId(), GetAnonymousId(), GetCustomerContact().

Anyway, I was just wondering if anyone also got bitten with this anonymous user thing ?

#223986
Jun 09, 2020 6:29
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.