{"record":{"id":"730fd3fcc2946cb0","repo":"crossoverJie/JCSprout","slug":"error","errorCode":null,"errorMessage":"库存不足","messagePattern":"库存不足","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"warning","filePath":"MD/third-party-component/seconds-kill.md","lineNumber":149,"sourceCode":"    @Override\n    public int createWrongOrder(int sid) throws Exception{\n\n        //校验库存\n        Stock stock = checkStock(sid);\n\n        //扣库存\n        saleStock(stock);\n\n        //创建订单\n        int id = createOrder(stock);\n\n        return id;\n    }\n    \n    private Stock checkStock(int sid) {\n        Stock stock = stockService.getStockById(sid);\n        if (stock.getSale().equals(stock.getCount())) {\n            throw new RuntimeException(\"库存不足\");\n        }\n        return stock;\n    }\n    \n    private int saleStock(Stock stock) {\n        stock.setSale(stock.getSale() + 1);\n        return stockService.updateStockById(stock);\n    }\n    \n    private int createOrder(Stock stock) {\n        StockOrder order = new StockOrder();\n        order.setSid(stock.getId());\n        order.setName(stock.getName());\n        int id = orderMapper.insertSelective(order);\n        return id;\n    }        \n        \n}","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/crossoverJie/JCSprout/blob/fc4c6e5f6d1772c2aec4c0f94cb3c8e7eb04018f/MD/third-party-component/seconds-kill.md#L131-L167","documentation":"Thrown by checkStock() in the 'wrong order' seconds-kill implementation when stock.getSale() equals stock.getCount() — i.e. every unit of inventory has been sold. This is the naive, non-thread-safe implementation: it reads stock, compares sale vs count, then updates in a separate call, which is exactly the check-then-act gap that causes overselling under concurrency. The exception means stock is exhausted according to the DB read at this instant.","triggerScenarios":"Calling createWrongOrder(sid) when the stock table shows sale == count for that sid. Under concurrent load this check is unreliable — multiple threads read sale < count simultaneously and all proceed past the check before any has written sale+1, producing oversell. After overselling exhausts stock, subsequent calls correctly hit this exception.","commonSituations":"Inventory fully consumed during a flash sale. The stock row was never initialised or was set to count=0. A previous overselling bug has already driven sale beyond count, so the .equals() check (which only catches exact equality) may miss it — note the bug: it should be >= not ==.","solutions":["Switch to the optimistic-lock variant (createOptimisticOrder) which uses a version column to prevent overselling atomically.","Fix the equality check to >= so it catches the oversold case where sale has already exceeded count.","Pre-load stock counts into Redis and check there first to reduce DB pressure (the Redis-cached variant).","Handle the exception at the controller level and return a user-friendly 'out of stock' response."],"exampleFix":"// before — only catches exact equality, not oversold state\nif (stock.getSale().equals(stock.getCount())) {\n    throw new RuntimeException(\"库存不足\");\n}\n\n// after — catches exhaustion and oversold corruption\nif (stock.getSale() >= stock.getCount()) {\n    throw new RuntimeException(\"库存不足\");\n}","handlingStrategy":"try-catch","validationCode":"// Pre-check stock before calling the order service to give an early rejection.\nStock stock = stockService.getStockById(sid);\nif (stock.getSale() >= stock.getCount()) {\n    throw new RuntimeException(\"库存不足\");\n}\n// Still call the service — the real guard must be the DB-level lock.\nint orderId = orderService.createOptimisticOrder(sid);","typeGuard":null,"tryCatchPattern":"try {\n    int orderId = orderService.createWrongOrder(sid);\n    return ResponseEntity.ok(orderId);\n} catch (RuntimeException e) {\n    if (\"库存不足\".equals(e.getMessage())) {\n        return ResponseEntity.status(409).body(\"Sold out\");\n    }\n    throw e;\n}","preventionTips":["Use the optimistic-lock variant (createOptimisticOrder) instead of the naive createWrongOrder to prevent overselling.","Fix the equality check from == to >= so it catches already-oversold state.","Handle the exception at the controller level and return a user-friendly 'out of stock' response rather than a 500."],"tags":["seconds-kill","stock","concurrency","data-integrity"],"backgroundTag":null,"analyzedSha":"fc4c6e5f6d1772c2aec4c0f94cb3c8e7eb04018f","analyzedAt":"2026-08-14T05:43:20.992Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}