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);
Some notes for clarity:
private Injected<IOrderRepository> _orderRepository;
The project in which the above code is located is currently on Commerce 13.2.0.
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:
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.
Is this something you looked for https://vimvq1987.com/watch-out-for-singletons/ ?
Indeed :) But do use Singletons if you are expected to scale high! :D
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 ?
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.
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 ?