Плагины Microsoft Dynamics CRM - одновременные обновления

У меня есть настраиваемая сущность, которая обновляется почти в реальном времени (около 1000 обновлений в минуту). Поскольку одна и та же запись объекта может обновляться в разных запросах пакетного обновления, а запросы принимаются из разных асинхронных источников, есть вероятность, что более недавнее обновление в CDS может быть перезаписано более старым обновлением.

  1. Обновление 1 (с настраиваемым атрибутом datamodifiedon, установленным на 8.02 PM) для записи Entity A обрабатывается асинхронной службой S1.
  2. Обновление 2 (с настраиваемым атрибутом datamodifiedon, установленным на 8.03 PM) для записи Entity A обрабатывается асинхронной службой S2.

Из-за задержки в сети или по какой-либо другой причине служба S1 работает немного медленнее, а служба S2 продолжила и запустила свой вызов обновления для записи Entity A с измененными данными в 20.03.

Сервис-1 теперь запускает Обновление-1 для той же записи объекта, и обновление, произошедшее в 20.03, перезаписывается обновлением, произошедшим в 20.02, что приводит к потере данных.

Я зарегистрировал плагин для сообщения Update SDK моего настраиваемого объекта на этапе 10 (предварительная проверка). Этот плагин сравнивает атрибут datamodifiedon из входных параметров с атрибутом, установленным в изображениях пред-сущностей из контекста плагина. И выдает исключение Invalid Plugin Execution, если изображение до объекта уже имеет более свежий атрибут datamodifiedon.

// If ModifiedOn value in pre-image is later than the one received in input parameters, this is an obsolete request and must be rejected.
if (preImage.Contains("datamodifiedon")
    && preImage.GetAttributeValue<DateTime>("datamodifiedon") != DateTime.MinValue
    && entity.Contains("datamodifiedon")
    && entity.GetAttributeValue<DateTime>("datamodifiedon") != DateTime.MinValue
    )
{
    if (DateTime.Compare(preImage.GetAttributeValue<DateTime>("datamodifiedon"), entity.GetAttributeValue<DateTime>("datamodifiedon")) > 0)
    {
        string traceMessage = "PreOperationLiveWorkItemUpdatePlugin: Update request is obsolete. datamodifiedon field found in pre-entity image: "
            + preImage.GetAttributeValue<DateTime>("datamodifiedon")
            + "datamodifiedon field in Input parameters: "
            + entity.GetAttributeValue<DateTime>("datamodifiedon");

        tracingService.Trace(traceMessage);
        throw new InvalidPluginExecutionException(traceMessage);
    }
}

Поскольку обновления происходят очень часто, существует ли вероятность того, что 1. Обновление-2 (данные, измененные в 20.03) находится на стадии предварительной проверки. После успешной проверки, актуальное обновление в базе данных выполняется. 2. Теперь Update-1 переходит в стадию предварительной проверки. Поскольку Обновление-2 еще не зафиксировано в базе данных, будет ли изображение пред-сущности, полученное на этом этапе, по-прежнему старым значением и позволит ли пройти проверку?

Поможет ли регистрация этого плагина на этапе Pre-Operation или Post-Operation вместо этапа Pre-Validation, поскольку два других этапа выполняются в одной транзакции базы данных?

Есть ли другой способ решить эту проблему параллелизма? Поскольку обновление инициируется пакетным вызовом odata, предварительное условие eTag не может использоваться в заголовке запроса.




Ответы (2)


Вам следует попробовать использовать Оптимистичный параллелизм. Если вы можете проверить версию строки перед ее обновлением.

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

Пример кода

person Arun Vinoth    schedule 13.07.2019
comment
Спасибо за ответ. Нужно ли мне явно получать запись в моем подключаемом модуле предварительной проверки / подготовки к работе? Будет ли это задерживать обновления дальше или повлияет на производительность на количество записей, которые можно обновить за минуту? - person Vanathi; 13.07.2019
comment
Вы должны получать его из источника, который его обновляет, чтобы он знал, что делать в случае сбоя. Тогда вам даже не понадобится плагин для обработки параллелизма. - person Daryl; 15.07.2019

Вы хотите зарегистрировать этот плагин на этапе подготовки к работе. Я думаю, у вас есть веские основания полагать, что изображение пред-сущности потенциально может быть грязным (я не уверен, извлекается ли предварительное изображение в транзакции), поэтому вместо использования предварительного изображения извлеките новую копию записи и проверьте отметка времени полученной записи относительно отметки времени на вашей цели. Поскольку все ваши операции являются синхронными транзакциями с одной и той же таблицей, а метка времени - это поле в этой таблице, я думаю, что это гарантирует, что у вас нет грязных записей.

Однако:
Если ваши операции Update1 и Update2 обновляют разные поля, теперь вы можете столкнуться с обратной потерей данных. Если Update1 устанавливает поле A, а update2 устанавливает поле B, то изменение, внесенное в поле A, не будет учитываться, если оно будет обработано после обновления поля B.

person Zach Mast    schedule 12.07.2019
comment
Спасибо за ответ. Поле dataupdatedon будет доступно в целевой сущности в каждом сделанном запросе на обновление. Так что с этой стороны мы хороши. Единственное, что меня беспокоит: если плагин запускается параллельно для обоих обновлений, образ пред-сущности не будет выполнять грязное чтение транзакционных данных других обновлений. Образ пред-сущности будет напоминать уже зафиксированную запись в базе данных. Если Update2 будет зафиксирован в базе данных CDS сразу после того, как проверка Update1 на этапе подготовки к операции завершится успешно, будет ли Update1 по-прежнему выполняться и перезаписывать? - person Vanathi; 13.07.2019
comment
Я думаю, у вас есть веские основания полагать, что изображение до объекта может быть грязным, и я соответствующим образом обновил свой ответ. - person Zach Mast; 13.07.2019
comment
Спасибо за обновленный ответ. Учитывая, что у нас происходит обновление данных в реальном времени, я боюсь, что этот дополнительный вызов извлечения вызовет большую задержку и повлияет на производительность на количество записей, обновляемых в минуту. На каком этапе вы бы порекомендовали зарегистрировать плагин с доступными вариантами? Предварительная проверка (10), до операции (20) или после операции (30) Обеспечит ли какой-либо из этих этапов дополнительное преимущество перед другими? - person Vanathi; 13.07.2019
comment
Вам нужно, чтобы это произошло перед операцией, чтобы это было частью транзакции обновления. Это влечет за собой небольшое дополнительное чтение во время каждой операции записи, однако оптимистичный параллелизм, который предлагает Арун (который выглядит круто), по-видимому, также влечет за собой дополнительные операции чтения под капотом. - person Zach Mast; 13.07.2019