Re[42]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.02.26 04:30
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sinclair, Вы писали:


S>>>>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual.

S>>·>Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара?
S>>Нет, это всё можно записать двумя стейтментами на весь заказ, вообще без раундтрипов.
·>Интересно, как это выглядит?
Примерно так:
BEGIN TRANSACTION;

WITH
-- 1) Лочим заказ и одновременно проверяем, что он в draft
ord AS (
  SELECT o.orderId
  FROM orders o
  WHERE o.orderId = @orderId
    AND o.state = 'draft'
  FOR UPDATE
),

-- Позиции заказа (orderId уже фиксируем через join на ord)
req AS (
  SELECT oi.productId, oi.quantity
  FROM orderItem oi
  JOIN ord ON ord.orderId = oi.orderId
),

-- 2) Лочим строки stock в фиксированном порядке (anti-deadlock)
locked AS (
  SELECT s.productId, s.available
  FROM stock s
  JOIN req r ON r.productId = s.productId
  ORDER BY s.productId
  FOR UPDATE
),

-- 2b) Проверка all-or-nothing:
--  - заказ был draft (ord существует)
--  - для каждой позиции есть строка stock
--  - available хватает для каждой позиции
chk AS (
  SELECT
    (SELECT COUNT(*) FROM ord) AS ord_cnt,
    (SELECT COUNT(*) FROM req) AS need_cnt,
    (SELECT COUNT(*) FROM locked) AS locked_cnt,
    (SELECT COUNT(*)
       FROM locked l
       JOIN req r USING (productId)
      WHERE l.available >= r.quantity
    ) AS ok_cnt
),

-- 3) Обновляем stock только если ВСЁ ОК
upd_stock AS (
  UPDATE stock s
  SET available = s.available - r.quantity,
      reserved  = s.reserved  + r.quantity
  FROM req r, chk
  WHERE s.productId = r.productId
    AND chk.ord_cnt = 1
    AND chk.need_cnt > 0
    AND chk.need_cnt = chk.locked_cnt
    AND chk.need_cnt = chk.ok_cnt
  RETURNING 1
),

-- 4) Меняем статус заказа только если stock реально обновился
upd_order AS (
  UPDATE orders o
  SET state = 'reserved'
  WHERE o.orderId = @orderId
    AND EXISTS (SELECT 1 FROM upd_stock)
  RETURNING o.orderId, o.state
)

SELECT * FROM upd_order;

COMMIT;

·>И что если кто-то закинет заказ на тысячу позиций?
Будет попытка зарезервировать заказ на тысячу позиций.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.