簡體   English   中英

在 postgres 中刪除和插入的競爭條件

[英]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以原子方式執行此操作。 如果沒有看到deleteOrderssaveTransactionOrders的內容,我就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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM