{
  "id": 18406270,
  "title": "100 people bought a mug. The stock went from 100 to 99.",
  "url": "https://urgent.news/2026/11/06/100-people-bought-a-mug-the-stock-went-from-100-to-99",
  "topic": "finance",
  "section": "Finance & Markets",
  "published": "2026-11-06T00:29:40.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/haiduc0147/100-people-bought-a-mug-the-stock-went-from-100-to-99-494d"
  },
  "original_language": "en",
  "account": "The story begins with a seemingly simple piece of code designed to decrement a product's stock count by one when a purchase is made. However, the code contains a critical flaw that leads to a race condition, resulting in inaccurate stock counts and lost sales. To understand the issue, let's examine the code step by step.\n\nThe code starts by querying the database to retrieve the current stock level for the requested product. If the stock count is greater than one, it proceeds to execute an UPDATE query to decrement the stock count by one. The updated stock count is then returned.\n\nAt first glance, the code seems to function correctly. However, when multiple requests are made concurrently, problems arise. Imagine two buyers, Alice and Bob, both attempting to purchase the same product. Alice reads the stock count as 5, while Bob also reads the same stock count of 5. In the meantime, both of their queries are sent to the database.\n\nNext, Node.js handles the requests concurrently. Bob's request is processed first, reading the stock count as 5. After that, Alice's request is executed, decrementing the stock count from 5 to 4. Bob's request continues executing, trying to decrement the stock count again from 5, resulting in an incorrect count of 4.\n\nThis scenario demonstrates a lost update, where the database operation intended to decrement the stock count fails to update the actual value due to simultaneous requests. In this case, both Alice and Bob's attempts to decrement the stock count were overwritten, leading to a final stock count of 4 instead of the expected 4.\n\nThe article then explores several solutions to address this race condition issue. The first solution is to let the database handle the decrement operation directly, eliminating the need for JavaScript to perform the calculation. By updating the stock count within the database using an UPDATE query with the condition `stock = 1`, the decrement operation becomes atomic, and lost updates are avoided.\n\nThe second solution involves locking the row when reading the stock count. By adding `FOR UPDATE` to the SELECT query, a lock is placed on the row, preventing other transactions from modifying it until the current transaction is committed. This ensures that the stock count is read consistently and updated atomically.\n\nThe third solution introduces a version or timestamp column to the product table. When reading the stock count, the code retrieves the current version or timestamp along with the count. When updating the stock count, the code checks if the version or timestamp has changed since the read. If the version or timestamp matches, the update proceeds; otherwise, the code retries the update operation. This approach prevents lost updates without requiring exclusive locks on the row.\n\nThe article concludes by emphasizing the importance of identifying and addressing race conditions in code that involves concurrent reads and writes. The key takeaway is that even single-threaded languages like JavaScript can encounter race conditions when dealing with database operations. By understanding the underlying principles and applying appropriate solutions, developers can ensure data integrity and prevent lost updates in their applications.",
  "summary": "I wrote the most normal checkout code there is. Read the stock, check it isn't zero, write stock minus one. Then I sent 100 buy requests at it at the same time, against a real Postgres database. All 100 went through. The stock went from 100 to 99. async function buy ( id ) { const { rows } = await db . query ( \" SELECT stock FROM products WHERE id = $1 \" , [ id ]); const stock = rows [ 0 ]. stock…",
  "key_points": [
    "Code contains race condition flaw",
    "Two buyers attempt to purchase same product",
    "Lost update leads to incorrect stock count"
  ],
  "editors_take": "Flawed concurrency logic in database operations forces developers to move beyond simple application-side calculations to ensure data integrity through atomic updates or row locking.",
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}