[英]Race condition for delete and insert in postgres
我正在開發一個節點項目,我在其中導入了用於數據庫操作的 pg 庫。 我有一個 Kafka 隊列,我從中獲取事件並將它們存儲在數據庫中。 我正在從 kafka 獲取訂單,每次更新訂單時都會生成一個新事件,我需要刪除舊的訂單詳細信息並將其替換為新的。
下面是代碼
async function saveOrders(orders: Array<TransactionOnOrders>) {
const client = await pool.connect()
try {
await client.query('BEGIN')
if (orders.length) {
const deleted = await deleteOrders(client, orders[0].orderId)
logger.debug(`deleted rowCount ${deleted.rowCount} ${orders[0].orderId}`)
}
const queries = orders.map(ord => saveTransactionOnOrders(client, ord))
await Promise.all(queries)
await client.query('COMMIT')
} catch (e) {
await client.query('ROLLBACK')
throw e
} finally {
client.release()
}
}
訂單更新非常頻繁,我們收到了很多事件,造成了競爭條件,導致記錄不被刪除和插入額外的記錄。 例如:假設我們收到一個 order123 的事件並且交易正在進行中,直到它完成 order123 的另一個事件的時間被接收,因此刪除查詢返回 0 行受影響,插入查詢插入另一行,導致 2 行。應該只有一個記錄存在。
我試圖更改無法正常工作並導致錯誤的隔離級別
await client.query('BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ')
await client.query('BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE')
我在這里做錯了什么還是有更好的方法來處理上述情況?
如果您更新行而不是刪除它們並重新創建它們,這可能會更容易。 在這種情況下,您可以依靠行鎖來防止並發更新。
使用INSERT ... ON CONFLICT
來“插入”傳入的行。 這是原子的,沒有競爭條件。
正如其他人所建議的那樣,這里的理想選擇是使用INSERT ... ON CONFLICT
以原子方式執行此操作。 如果沒有看到deleteOrders
和saveTransactionOrders
的內容,我就saveTransactionOrders
。
如果這不是一個選項,您應該使用SERIALIZABLE
作為隔離級別。 然后您會收到一些序列化錯誤,但可以安全地重試。 如果您使用了 @databases ( https://www.atdatabases.org/docs/pg-guide-transactions ),您只需傳遞retrySerializationFailures: true
即可重試:
async function saveOrders(orders: Array<TransactionOnOrders>) {
await pool.tx(async client =>
if (orders.length) {
const deleted = await deleteOrders(client, orders[0].orderId)
logger.debug(`deleted rowCount ${deleted.rowCount} ${orders[0].orderId}`)
}
const queries = orders.map(ord => saveTransactionOnOrders(client, ord))
await Promise.all(queries)
}, {
isolationLevel: IsolationLevel.SERIALIZABLE,
retrySerializationFailures: true,
})
}
@databases 處理啟動事務,並在異步回調結束時提交/回滾。 它還重試序列化失敗。
如果您正在處理大量事件,您可能會由於序列化失敗的頻率很高而遇到性能問題,因此會重試。
您可以在 node.js 中使用“鎖”來確保一次只有一個進程更新一組給定的訂單。 https://www.atdatabases.org/docs/lock應該使實現起來相當簡單。 但這只會鎖定單個 node.js 進程上的並發事務,因此您仍然需要事務處理來處理多個 node.js 進程。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.