cancel
Showing results for 
Search instead for 
Did you mean: 

Stock Level goes negative

former_member623977
Active Participant

Hi ,

In our project we are maintaining the stock levels for products. I am facing an issue in a specific scenario and its mentioned below -

I have set a Product A stock to 3 and now two different users, lets say User A and User B, have added 2 qty of Product A in their cart. Since stock is not reserved yet so both can add qty 2 to their cart, when both users simultaneously proceed to payment page and as a result stock level reaches to -1 (negative) order gets placed successfully for both the users.

ideally as soon as its reserved by one of the users it should not allow other to proceed with qty 2 .

Can anyone please suggest or advise me on this issue ? How can I prevent this situation ?

I am unable to replicate this issue on my local but its rarely occurring on Production.

Thanks in advance.

Accepted Solutions (0)

Answers (4)

Answers (4)

0 Kudos

you should not update the stock manually using setter methods of the models, this leads the concurrency issue. even the prod and QA servers will have clusters and there it can run these logic parallel. Hence never update the stock model with the setter method.

to update or to reserve the stock use StockService (interface) methods. this service locks the Stock model object, other threads which want to use same object will be in wait mode it the first transaction is completed.

As we have a validation on place Order, for one user it places order and for the second user it gives error and can't place the order

0 Kudos

The thing is that OOTB Hybris works with StockLevels via jdbcTemplate (see DefaultStockLevelDao).
Any validations in controllers/services don't prevent race conditions and so on, so it will be better do adjust DefaultStockLevelDao and rewrite its queries.
For example, this query is executed during stockService#reserve:
UPDATE stocklevels SET p_reserved = p_reserved - ? WHERE PK = ?
It can be replaced in such manner:
UPDATE stocklevels SET p_reserved = GREATEST(0, p_reserved - ? ) WHERE PK = ?

Also I have to mention that due to direct updating of DB row modifiedtime won't be changed for concrete model. So it should be also adjusted.

former_member618655
Active Participant
0 Kudos

: I assume you have a Proceed to Checkout button on your cart page. This button's action should be /cart/checkout. if you do this, as soon as a user clicks on this button, your call goes to below method in CartPageController.

 @RequestMapping(value = "/checkout", method = RequestMethod.GET)
     @RequireHardLogIn
     public String cartCheck(final RedirectAttributes redirectModel) throws CommerceCartModificationException
     {
         SessionOverrideCheckoutFlowFacade.resetSessionOverrides();
 
         if (!getCartFacade().hasEntries())
         {
             LOG.info("Missing or empty cart");
 
             // No session cart or empty session cart. Bounce back to the cart page.
             GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, "basket.error.checkout.empty.cart",
                     null);
             return REDIRECT_CART_URL;
         }
 
 
         if (validateCart(redirectModel))
         {
             GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, CART_CHECKOUT_ERROR, null);
             return REDIRECT_CART_URL;
         }
 
         // Redirect to the start of the checkout flow to begin the checkout process
         // We just redirect to the generic '/checkout' page which will actually select the checkout flow
         // to use. The customer is not necessarily logged in on this request, but will be forced to login
         // when they arrive on the '/checkout' page.
         return REDIRECT_PREFIX + "/checkout";
     }

This function checks if the cart is empty, if it is. This function will redirect user back to cart page. There is another if-block which checks if the cart is valid if (validateCart(redirectModel)). This validateCart function will check if the cart has been modified or if the stock of added products has changed. And if it does, it will store an appropriate message in the redirectModel, redirects the user back to the cart page and show the appropriate message as a flash message on the top of page. In short, in your scenario, between user A and user B whoever clicks the Proceed to Checkout button first will be taken to checkout page, the other will be redirected back to cart page with a message that your cart has been adjusted.

Hope it helps.

former_member623977
Active Participant
0 Kudos

: Validate Cart is just checking the stock its does not reserve the stock so it will always allow multiple users to add the qty to cart. Therefore it will not work in my case. Thanks for the reply.

0 Kudos

Hi , I do agree that this function will validate the stock in currenct cart , but in our scenario it seems order placement is so fast for both orders that before reserving stock another process skips the validation due to which stock is going to -1. Is there any solution for the same.

former_member618655
Active Participant
0 Kudos

I hate to say this, but if there is a race condition, then it is better you use concurrency. Use synchronize or Lock.

VinayKumarS
Active Contributor
0 Kudos

OOB functionality do not have any validation to check the stock. But if we have such requirement then again on the checkout page. before placing an order process. we will have to check the stock status and stop the user.

I have seen in multiple application we do this validation before placing an order button.

I hope this clarifies.

former_member704915
Participant
0 Kudos

Correct. You need to do a make a call for soft inventory reservation, to check for the stock and reserve it.