top of page
Search

Race Condition в Dataverse: почему данные перезаписываются

Writer: Sarov+
Sarov+
Sep 17
2 min read

Что произойдёт, если два процесса одновременно обновят одну запись в Dataverse? Результат может зависеть от того, какой запрос выполнится последним, а не от того, какие данные актуальнее. Такое состояние называют Race Condition, или состоянием гонки.

В этой статье мы разберём, как возникает эта проблема, как мы столкнулись с ней в интеграции JotForm, Azure Functions и Dataverse и почему проверки версии записи не всегда достаточно.

 

А узнать больше можно в нашем видео: 

 

Как возникает конфликт

 

Типичный сценарий — «проверить, затем выполнить действие». Процесс ищет запись и решает: создать новую или обновить существующую.

Если два процесса выполняются одновременно, они могут:

  • не найти запись и оба создать её — так появляются дубликаты;

  • прочитать одну версию записи и затем перезаписать изменения друг друга.

 

Логика каждого процесса по отдельности выглядит правильной. Ошибка возникает именно при их параллельной работе: между проверкой и записью другой процесс успевает изменить исходное состояние.

Причём источниками изменений могут быть не только пользователи, но и Power Automate, плагины, внешние интеграции и Azure Functions. Поэтому проблему важно учитывать на уровне всей системы.

 

Реальный кейс: JotForm и Azure Functions

 

В нашей интеграции обработка данных происходила так:

  1. Пользователь отправлял форму в JotForm.

  2. Webhook вызывал Azure Function, которая помещала данные в Azure Queue.

  3. Другая Azure Function забирала сообщение из очереди.

  4. По Submission ID она находила и обновляла запись в Dataverse либо создавала новую.

 

Submission ID не был настроен как Alternate Key: в таблице уже накопились дубликаты, которые мешали переходу на такой механизм. Поэтому поиск записи и выбор между созданием и обновлением выполнялись вручную.

Если пользователь быстро редактировал форму, в очереди появлялись два сообщения с одним идентификатором. После задержки запуска функции они могли обрабатываться параллельно.

Изначально проверка версии записи не использовалась. Поэтому при обновлении сохранялись данные того процесса, который завершался последним, — даже если они были старее.

 

Что изменили и почему этого недостаточно

 

Мы добавили optimistic concurrency:

  • при чтении получали RowVersion;

  • при обновлении требовали совпадения версии через ConcurrencyBehavior.IfRowVersionMatches;

  • при конфликте фиксировали ошибку в логах и запускали повторную обработку сообщения — retry.

 

Этот механизм не блокирует запись заранее. Он позволяет проверить, не изменил ли её другой процесс после чтения. Если версии не совпадают, обновление отклоняется. Так незаметная перезапись превратилась в обнаруживаемый конфликт.

Но порядок обновлений по-прежнему не гарантировался. Более старое сообщение могло обработаться позже нового и перезаписать актуальные данные при повторной попытке. Успешный retry означает, что операция выполнена, но не доказывает актуальность её содержимого.

Поэтому следующий шаг — учитывать дату изменения submission в JotForm. RowVersion показывает, менялась ли запись после чтения, но не определяет, какое входящее событие новее.

 

Заключение

 

Optimistic concurrency помогает обнаруживать конфликты обновлений, но не решает все проблемы параллельной обработки. Чтобы сохранять актуальные данные, необходимо учитывать и версию записи в Dataverse, и последовательность событий в исходной системе.

Главный вывод из нашего кейса: технически успешное обновление не всегда означает правильный бизнес-результат. Контроль конкурентных операций должен учитывать не только возможность записи, но и то, какие данные действительно должны сохраниться.

 
 
 

Comments


Power Platform logo

Подписывайся на наши ресурсы.

  • Telegram
  • LinkedIn
  • Facebook
  • Twitter
  • YouTube
  • Instagram

© 2035 by The Pop Show. Powered and secured by Wix

bottom of page