UPDATE with Race Condition (带竞态条件的更新)

huangapple go评论96阅读模式
英文:

UPDATE with Race Condition

问题

我需要跟踪通过webhook接收到的WhatsApp API状态通知的状态。

我构建了一个像这样的存储过程

  1. UPDATE wam
  2. SET
  3. [timestamp]=j.[timestamp],
  4. [status]=j.[status]
  5. FROM WAMessages wam
  6. JOIN OPENJSON(@json)
  7. WITH (
  8. message_id nvarchar(128) '$.entry[0].changes[0].value.statuses[0].id'
  9. ,[status] nvarchar(128) '$.entry[0].changes[0].value.statuses[0].status'
  10. ,[timestamp] bigint '$.entry[0].changes[0].value.statuses[0].timestamp'
  11. ) j on wam.message_id=j.message_id
  12. IF @@ROWCOUNT=0
  13. BEGIN
  14. INSERT INTO WAMessages
  15. (
  16. message_id
  17. ,[status]
  18. ,[timestamp]
  19. --,other fields
  20. )
  21. SELECT
  22. message_id
  23. ,[status]
  24. ,[timestamp]
  25. FROM OPENJSON(@json)
  26. WITH (
  27. message_id nvarchar(128) '$.entry[0].changes[0].value.statuses[0].id'
  28. ,[status] nvarchar(128) '$.entry[0].changes[0].value.statuses[0].status'
  29. ,[timestamp] bigint '$.entry[0].changes[0].value.statuses[0].timestamp'
  30. --,other fields
  31. ) j
  32. END

这个方法有效,但由于'sent'状态消息和'delivered'状态消息同时接收,所以随机地只处理一个消息:

有时我只收到sent,有时只收到delivered

存在一个我无法解决的竞争条件问题

我尝试添加WITH UPDLOCKLEVEL SERIALIZABLE,并且使用MERGE语句,但都没有效果:

显然,如果我删除UPDATE语句(和IF @@ROWCOUNT=0),我会得到2条单独的记录,一条用于**'sent',另一条用于'delivered'**状态,但目标是每个message_id只有一条包含发送/已送达/已读详情的记录。

你能提供正确的解决方法吗?

英文:

I need keep track of the status of WhatsApp API status notifications received via a webhook.

I built a stored procedure like this:

  1. UPDATE wam
  2. SET
  3. [timestamp]=j.[timestamp],
  4. [status]=j.[status]
  5. FROM WAMessages wam
  6. JOIN OPENJSON(@json)
  7. WITH (
  8. message_id nvarchar(128) '$.entry[0].changes[0].value.statuses[0].id'
  9. ,[status] nvarchar(128) '$.entry[0].changes[0].value.statuses[0].status'
  10. ,[timestamp] bigint '$.entry[0].changes[0].value.statuses[0].timestamp'
  11. ) j on wam.message_id=j.message_id
  12. IF @@ROWCOUNT=0
  13. BEGIN
  14. INSERT INTO WAMessages
  15. (
  16. message_id
  17. ,[status]
  18. ,[timestamp]
  19. --,other fields
  20. }
  21. SELECT
  22. message_id
  23. ,[status]
  24. ,[timestamp]
  25. FROM OPENJSON(@json)
  26. WITH (
  27. message_id nvarchar(128) '$.entry[0].changes[0].value.statuses[0].id'
  28. ,[status] nvarchar(128) '$.entry[0].changes[0].value.statuses[0].status'
  29. ,[timestamp] bigint '$.entry[0].changes[0].value.statuses[0].timestamp'
  30. --,other fields
  31. ) j
  32. END

That works, but since 'sent' status message and 'delivered' status message are received simultaneously I get randomly only one of the messages to be processed:

Sometime I get only sent, sometimes only delivered:

There is a race condition issue I've not been able to solve:

I tried adding a WITH UPDLOCK or a LEVEL SERIALIZABLE and also using MERGE Statement, but nothing works:

clearly if I remove the UPDATE statement (and IF @@ROWCOUNT=0) I get 2 separated records, one for 'sent' and the other for 'delivered' statuses, but the target is to have only one record for each message_id with sent/delivered/read details.

Can suggest the right path to solve this issue?

答案1

得分: 1

通常,在具有 OLTP 负载的同一表上使用 UPDATE 和 INSERT 逻辑并不是一个很好的设计。为了解决这个问题:

  1. 假设您总是收到两条消息。我建议检查消息状态,如果是“sent”并且没有数据库记录,则等待1秒并重试您的更新。
  2. 分别插入两条记录,然后使用另一个进程删除重复项。
英文:

It is generally not a great design to have UPDATE else INSERT logic on the same table with OLTP loads.

To fix it:

  1. Assuming you always get two messages. I suggest to check the message status and if it is "sent" and no DB record exists then wait for 1 second and retry your update.
  2. Insert both records separately and then have another process to remove duplicates.

huangapple
  • 本文由 发表于 2023年5月25日 08:17:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76328142.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定