User can purchase products with my project. However, product stock count must be equal or greater then requested quantity in purchase request. If It is ok, then a purchase record is saved on the table. After then, stock count is updated for this product in the another table. So there are 3 operations and if I send 20 concurrently requests to purchase 20 of product A which has 100 stock count in the db from another golang app, it works wrongly. It opens 20 purchase records in db and stock count updated as 60. Normally, after 5 requests operated, it must throws error because of stock count of product. I could not tweak method for this purpose. How Can I do?
This is the method, It calls repository methods 3 times but there is no special operations there only gorm save and update operations:
var wg sync.WaitGroup
wg.Add(1)
errs := make(chan error, 1)
go func(ctx context.Context, request *dto.CreateTicketPurchaseRequest) {
defer wg.Done()
productStockCount, err := t.stockRepo.GetTicketStockCount(productId) // first check available stock count
if err != nil {
errs <- err
return
}
if productStockCount.StockCount < payload.Quantity {
err = fmt.Errorf("no enough products")
errs <- err
return
}
u := new(entity.TicketPurchases)
u.Quantity = payload.Quantity
u.UserId = payload.UserId
u.StockId = productId
err = t.purchaseRepo.PurchaseTicket(u) // if stock count is ok, then purchase product
if err != nil {
errs <- err
return
}
productStockCount.StockCount = productStockCount.StockCount - u.Quantity
err = t.stockRepo.RemoveStockCount(productStockCount) // update stock count for product
if err != nil {
errs <- err
return
}
close(errs)
}(ctx, payload)
wg.Wait()
if err = <-errs; err != nil {
return c.JSON(http.StatusBadRequest, nil)
}
Repoimplementations must know about transactions. You might want to either explicitly pass it around, or put it into the context and pass context around so all parts can use the same transaction.