修复:避免首事件间隙锁死锁
This commit is contained in:
@@ -284,12 +284,12 @@ class TestInventoryDao(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertIn("biz_pallet_event.idempotency_key = 'event-key'", sql)
|
||||
self.assertNotIn('FOR UPDATE', sql)
|
||||
|
||||
async def test_add_event_allocates_next_sequence_and_updates_inventory_pointer(self) -> None:
|
||||
async def test_add_event_reads_exact_previous_pointer_and_updates_inventory(self) -> None:
|
||||
sequence_result = MagicMock()
|
||||
sequence_result.scalar_one.return_value = 4
|
||||
sequence_result.scalar_one_or_none.return_value = 4
|
||||
self.db.execute.return_value = sequence_result
|
||||
inventory = BizPalletInventory(pallet_id=7, last_event_id=None)
|
||||
inventory = BizPalletInventory(pallet_id=7, last_event_id=40)
|
||||
event = BizPalletEvent(pallet_id=99, event_seq=99, idempotency_key='event-key')
|
||||
flush_count = 0
|
||||
|
||||
@@ -311,15 +311,17 @@ class TestInventoryDao(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(2, self.db.flush.await_count)
|
||||
statement = self.db.execute.await_args.args[0]
|
||||
self.assertTrue(statement.selected_columns[0].compare(BizPalletEvent.event_seq.property.columns[0]))
|
||||
self.assertTrue(statement.get_final_froms()[0].compare(BizPalletEvent.__table__))
|
||||
for dialect in (mysql.dialect(), postgresql.dialect()):
|
||||
sql = compile_sql(statement, dialect)
|
||||
self.assertNotIn('max(', sql.lower())
|
||||
self.assertIn('biz_pallet_event.event_id = 40', sql)
|
||||
self.assertIn('biz_pallet_event.pallet_id = 7', sql)
|
||||
self.assertIn('ORDER BY biz_pallet_event.event_seq DESC', sql)
|
||||
self.assertIn('LIMIT 1', sql)
|
||||
self.assertIn('FOR UPDATE', sql)
|
||||
self.assertNotIn('ORDER BY', sql)
|
||||
self.assertNotIn('LIMIT', sql)
|
||||
|
||||
async def test_add_first_event_starts_sequence_at_one(self) -> None:
|
||||
async def test_add_first_event_starts_at_one_without_query(self) -> None:
|
||||
sequence_result = MagicMock()
|
||||
sequence_result.scalar_one.return_value = None
|
||||
sequence_result.scalar_one_or_none.return_value = None
|
||||
@@ -331,6 +333,25 @@ class TestInventoryDao(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
self.assertEqual(1, event.event_seq)
|
||||
self.assertEqual(51, inventory.last_event_id)
|
||||
self.db.execute.assert_not_awaited()
|
||||
self.db.add.assert_called_once_with(event)
|
||||
self.assertEqual(2, self.db.flush.await_count)
|
||||
|
||||
async def test_add_event_rejects_missing_previous_pointer_without_write(self) -> None:
|
||||
sequence_result = MagicMock()
|
||||
sequence_result.scalar_one_or_none.return_value = None
|
||||
self.db.execute.return_value = sequence_result
|
||||
inventory = BizPalletInventory(pallet_id=8, last_event_id=50)
|
||||
event = BizPalletEvent(pallet_id=8, idempotency_key='broken-pointer')
|
||||
|
||||
with self.assertRaisesRegex(RuntimeError, '托盘库存事件指针不一致'):
|
||||
await InventoryDao.add_event(self.db, inventory, event)
|
||||
|
||||
self.db.execute.assert_awaited_once()
|
||||
self.db.add.assert_not_called()
|
||||
self.db.flush.assert_not_awaited()
|
||||
self.assertEqual(50, inventory.last_event_id)
|
||||
self.assertIsNone(event.event_seq)
|
||||
|
||||
|
||||
class TestOperationDaoReads(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
Reference in New Issue
Block a user