Senior
How to atomically implement availability check and order creation when moving logic to a service?
sobes.tech AI
Answer from AI
For atomic checks of availability and order creation in a Go service, database transactions are usually used. Within a single transaction, availability (for example, stock presence) is checked first, then the order is created. If something goes wrong, the transaction is rolled back, ensuring data consistency.
An example using a database and the database/sql package:
func CreateOrderIfAvailable(db *sql.DB, productID int, quantity int, userID int) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
var available int
err = tx.QueryRow("SELECT stock FROM products WHERE id = ? FOR UPDATE", productID).Scan(&available)
if err != nil {
return err
}
if available < quantity {
return fmt.Errorf("not enough stock")
}
_, err = tx.Exec("UPDATE products SET stock = stock - ? WHERE id = ?", quantity, productID)
if err != nil {
return err
}
_, err = tx.Exec("INSERT INTO orders (user_id, product_id, quantity) VALUES (?, ?, ?)", userID, productID, quantity)
if err != nil {
return err
}
return tx.Commit()
}
Here, FOR UPDATE locks the row with the product, preventing race conditions during parallel requests. The entire logic in the service is wrapped in a transaction, which ensures atomicity of the operation.