简体   繁体   English

保存到 supabase 数据库时的竞争条件

[英]Race condition when saving to the supabase database

Im subscribing to a webhook that is listening for Calendly session creation (invitee.created) and session cancelation (invitee.canceled).我订阅了一个正在监听 Calendly 会话创建 (invitee.created) 和会话取消 (invitee.canceled) 的 webhook。 When I get the data I update the relevant record in the database.当我得到数据时,我会更新数据库中的相关记录。 This works fine for the most part as users would either create or cancel an event.这在大多数情况下都可以正常工作,因为用户可以创建或取消事件。 However, when they reschedule an event, Calendly cancels the session and rebooks it with the new date which introduces a race condition bug.但是,当他们重新安排活动时,Calendly 会取消会议并使用新日期重新预订,这会引入竞争条件错误。

some code for context:一些上下文代码:

// data is what I get from the webhook which is 
// an object containing information about the session 
// booked or cancellled. It looks something like this:

{
  created_at: '2022-07-06',
  created_by: 'https://api.calendly.com/users/3a1c8403',
  event: 'invitee.created',
  payload: {
    cancel_url: 'https://calendly.com/cancellations/058cb81f',
    created_at: '2022-07-06',
    email: 'xx@xxx.com',
    event: 'https://api.calendly.com/scheduled_events/7867fa63',
    first_name: null,
    last_name: null,
    name: 'xxxx',
    new_invitee: null,
    no_show: null,
    old_invitee: 'https://api.calendly.com/scheduled_events//invitees/283ba2b1-da59-4b2c',
    payment: null,
    questions_and_answers: [ [Object] ],
    reconfirmation: null,
    reschedule_url: 'https://calendly.com/reschedulings/058cb81f',
    rescheduled: false,
    routing_form_submission: null,
    status: 'active',
    text_reminder_number: null,
    timezone: 'Asia',
    tracking: {
      utm_campaign: null,
      utm_source: '158',
      utm_medium: null,
      utm_content: null,
      utm_term: null,
      salesforce_uuid: null
    },
    updated_at: '2022-07-06T16:53:23.191059Z',
    uri: 'https://api.calendly.com/scheduled_events/7867f'
  }
}


// then my logic:
const getSessionData = () => {
  axios
    .request({
      method: 'GET',
      url: `http://xxxx`,
      headers: {
        'Content-Type': 'application/json',
        Authorization: process.env.CALENDLY_API_KEY,
      },
    })



export default async (req, res) => {
  const body = (await buffer(req)).toString()
  const data = body ? JSON.parse(body) : null

  if(data) {
    switch (data?.event) {
      case 'invitee.canceled':
        const processCancellation = async () => {
          const cancelPromise = getSessionData(event)
          const cancelSessionData = await cancelPromise
    
          // update database with sessionData
          await supabase
            .from('booking')
            .update({
              // cancelSessionData...
            })
            .eq('id', sessionId)
        }
        processCancellation()
        break

      case 'invitee.created':
        const processBooking = async () => {
          const createPromise = getSessionData(event)
          const createSessionData = await createPromise
          await supabase
            .from('booking')
            .update({
              // createSessionData...
            })
            .eq('id', sessionId)
        }
        processBooking()
        break

      default:
        console.log(
          `Sorry, no data!`
        )
    }
  }
  res.send({ received: true })
}

How would I approach something like this?我将如何处理这样的事情?

Your code doesn't seem to have any race conditions on it, since it appears to be awaiting the completion of all promises before moving forward.您的代码似乎没有任何竞争条件,因为它似乎正在等待所有承诺的完成,然后再继续前进。

I think that your problem might be related to how Calendy calls your webhook, because you said that it first makes a call to cancel the old meeting, and then it makes another call to book a new one.我认为您的问题可能与 Calendy 如何调用您的 webhook 有关,因为您说它首先调用取消旧会议,然后再次调用预订新会议。

You cannot assume that these operations will be executed sequentially, since there may be cases when the re-book meeting request arrives before the cancel meeting request, because of the network conditions.您不能假设这些操作将按顺序执行,因为由于网络条件,可能存在重新预订会议请求在取消会议请求之前到达的情况。

And even if they do arrive in the correct order, you probably have more than one application server running on your backend, so there is a possibility that the re-book meeting request gets processed before the cancel meeting request.即使它们确实以正确的顺序到达,您的后端也可能运行多个应用程序服务器,因此重新预订会议请求有可能在取消会议请求之前得到处理。

To solve this, you might want to check the type of event you receive on the payload: event: 'invitee.created', .要解决此问题,您可能需要检查在有效负载上收到的事件类型: event: 'invitee.created', Maybe Calendy sends a different type of event when the meeting is re-booked, so then you can ignore the cancelation request if that is the case.重新预订会议时,可能 Calendy 会发送不同类型的事件,因此如果是这种情况,您可以忽略取消请求。

However if you do this, you might not be able to cancel a meeting that was re-booked at least once.但是,如果您这样做,您可能无法取消至少重新预订一次的会议。 So you can also look at the updated_at timestamp that comes from Calendy on your database, you can ignore requests that are older than the current time stamp.因此,您还可以查看数据库上来自 Calendy 的updated_at时间戳,您可以忽略早于当前时间戳的请求。

Also, be sure to never compare the timestamp of your server with the timestamp from Calendy, because the two might not be in sync.此外,请确保永远不要将服务器的时间戳与 Calendy 的时间戳进行比较,因为两者可能不同步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM