fix(借库扫码出库): 撤销 joinedload 修复 PG "FOR UPDATE cannot be applied to nullable side of outer join"

- 83b3db6 引入的 joinedload(ModelClass.base) 触发 LEFT OUTER JOIN,
  而 with_for_update() 会被 SQLAlchemy 透传到 join 的 nullable 侧,
  PG 直接抛 FeatureNotSupported,且连表加锁有死锁风险
- 退回最安全的单表 FOR UPDATE 模式,接受 N+1 lazy 加载的代价
- 在 防线3 上方加防回归注释,明确禁止未来再加 joinedload
- process_return 中的另两处 joinedload 不带 FOR UPDATE,不受 PG 限制,保留
This commit is contained in:
DXC
2026-06-16 13:55:46 +08:00
parent 83b3db693a
commit b79b0f99af

View File

@ -95,8 +95,11 @@ class TransService:
# ============================================== # ==============================================
# ★ 防线3并发超卖与负库存 - 锁行后再查可用库存 # ★ 防线3并发超卖与负库存 - 锁行后再查可用库存
# ⚠️ 不要在此加 joinedload(ModelClass.base)PG 禁止 FOR UPDATE
# 应用到 outer join 的 nullable 侧,会报 FeatureNotSupported
# 并有死锁风险。stock.base 走单条 lazy 加载是已知取舍。
# ============================================== # ==============================================
stock = ModelClass.query.options(joinedload(ModelClass.base)).with_for_update().get(stock_id) stock = ModelClass.query.with_for_update().get(stock_id)
if not stock: raise ValueError(f"库存不存在 ID:{stock_id}") if not stock: raise ValueError(f"库存不存在 ID:{stock_id}")
# ============================================== # ==============================================