修复:消除使用幂等缺失键间隙锁
This commit is contained in:
@@ -132,12 +132,8 @@ class OperationDao:
|
||||
key_column: InstrumentedAttribute,
|
||||
idempotency_key: str,
|
||||
*extra_conditions: ColumnElement[bool],
|
||||
for_update: bool = False,
|
||||
) -> Any | None:
|
||||
statement = select(model).where(key_column == idempotency_key, *extra_conditions)
|
||||
if for_update:
|
||||
statement = statement.with_for_update().execution_options(populate_existing=True)
|
||||
result = await db.execute(statement)
|
||||
result = await db.execute(select(model).where(key_column == idempotency_key, *extra_conditions))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
@staticmethod
|
||||
@@ -377,8 +373,6 @@ class OperationDao:
|
||||
db: AsyncSession,
|
||||
idempotency_key: str,
|
||||
site_ids: list[int] | None = None,
|
||||
*,
|
||||
for_update: bool = False,
|
||||
) -> BizUsageRecord | None:
|
||||
return await cls._get_by_key(
|
||||
db,
|
||||
@@ -386,7 +380,6 @@ class OperationDao:
|
||||
BizUsageRecord.idempotency_key,
|
||||
idempotency_key,
|
||||
*cls._usage_scope_conditions(site_ids),
|
||||
for_update=for_update,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -169,7 +169,6 @@ class UsageService:
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=site_ids,
|
||||
for_update=False,
|
||||
)
|
||||
if recovered is None:
|
||||
raise ServiceException(message='使用失败,数据冲突')
|
||||
@@ -340,17 +339,16 @@ class UsageService:
|
||||
try:
|
||||
request_hash = cls._build_request_hash(page_object)
|
||||
user_id, user_name = cls._current_identity(current_user)
|
||||
site_ids = await cls._site_scope(query_db, current_user)
|
||||
preflight_site_ids = await cls._site_scope(query_db, current_user)
|
||||
existing = await OperationDao.get_usage_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=site_ids,
|
||||
for_update=False,
|
||||
site_ids=preflight_site_ids,
|
||||
)
|
||||
if existing is not None:
|
||||
if not cls._is_exact_idempotent_result(existing, page_object, request_hash):
|
||||
raise ServiceException(message='使用失败,幂等键已被其他请求使用')
|
||||
if site_ids is not None and existing.site_id not in site_ids:
|
||||
if preflight_site_ids is not None and existing.site_id not in preflight_site_ids:
|
||||
cls._raise_usage_not_accessible(current_user)
|
||||
await cls._lock_and_reauthorize_usage_site(
|
||||
query_db,
|
||||
@@ -367,10 +365,12 @@ class UsageService:
|
||||
)
|
||||
if scanned_pallet is None:
|
||||
cls._raise_pallet_not_accessible(current_user)
|
||||
scanned_pallet_id = int(scanned_pallet.pallet_id)
|
||||
await query_db.rollback()
|
||||
|
||||
inventory = await InventoryDao.get_inventory_for_update(
|
||||
query_db,
|
||||
scanned_pallet.pallet_id,
|
||||
scanned_pallet_id,
|
||||
)
|
||||
if inventory is None:
|
||||
cls._raise_pallet_not_accessible(current_user, '托盘库存不存在')
|
||||
@@ -378,6 +378,7 @@ class UsageService:
|
||||
cls._raise_pallet_not_accessible(current_user, '托盘不在客户现场')
|
||||
expected_site_id = inventory.site_id
|
||||
await cls._assert_pallet_site_access(query_db, current_user, inventory.site_id)
|
||||
locked_site_ids = await cls._site_scope(query_db, current_user)
|
||||
|
||||
locked_pallets = await InventoryDao.get_pallets_by_ids(
|
||||
query_db,
|
||||
@@ -398,8 +399,7 @@ class UsageService:
|
||||
locked_existing = await OperationDao.get_usage_by_idempotency_key(
|
||||
query_db,
|
||||
page_object.idempotency_key,
|
||||
site_ids=site_ids,
|
||||
for_update=True,
|
||||
site_ids=locked_site_ids,
|
||||
)
|
||||
if locked_existing is not None:
|
||||
if locked_existing.site_id != inventory.site_id or not cls._is_exact_idempotent_result(
|
||||
|
||||
Reference in New Issue
Block a user