Как предотвратить исключение DuplateKeyException при использовании обновления MongoDB с оптимистичным параллелизмом (упрC#

Место общения программистов C#
Anonymous
Как предотвратить исключение DuplateKeyException при использовании обновления MongoDB с оптимистичным параллелизмом (упр

Сообщение Anonymous »

Я использую MongoDB с драйвером .NET и пытаюсь реализовать шаблон оптимистичного параллелизма с управлением версиями с помощью UpdateOneAsync и IsUpsert = true.
Вот мой код:

Код: Выделить всё

public Task UpsertAsync(User user, CancellationToken cancellationToken) =>
context.Users.UpdateOneAsync(
Builders.Filter.Eq(u => u.Id, user.Id) &
Builders.Filter.Lt(u => u.Version, user.Version),
Builders.Update
.SetOnInsert(u => u.Id, user.Id)
.Set(u => u.IsDeleted, user.IsDeleted)
.Set(u => u.Version, user.Version)
.Set(u => u.Name, user.Name)
.Set(u => u.UserName, user.UserName)
.Set(u => u.Media, user.Media),
new UpdateOptions { IsUpsert = true },
cancellationToken
);
Цель
  • Вставить документ, если он не существует
  • Обновить его, только если входящая версия больше сохраненной
Проблема
Когда документ уже существует с более высокой версией версия, фильтр не соответствует:

Код: Выделить всё

Id == user.Id AND Version < user.Version → false
Из-за IsUpsert = true MongoDB пытается вставить новый документ, что приводит к:

Код: Выделить всё

DuplicateKeyException (same Id)
Вопрос
Каков рекомендуемый способ обработки этого сценария?
  • Является ли перехват и игнорирование DuulateKeyException правильным/ожидаемым подходом в этом шаблоне?
  • Или есть лучший способ реализовать «обновление только в том случае, если версия более новая, в противном случае ничего не делайте» без попытки вставки?
Я пытаюсь сохранить эту операцию атомарной и избежать условий гонки, поэтому я бы предпочел решение с одним запросом, если это возможно.
Будем признательны за любые рекомендации или лучшие практики.
Обновление ------------------------------------------------------------------
  • Если документ существует и входящая версия новее → обновите его.
  • Если документ существует, а входящая версия старше → игнорируйте ее.
  • Если документ не существует → вставьте его.
В моем сценарии большинство операций — это вставки (документ еще не существует), а обновления с более новыми версиями происходят реже.
Я реализую шаблон CQRS с двумя микросервисы (стороны команд и запросов). На стороне команды я уже использую поток чтения → обновления с оптимистичным параллелизмом и выдаю исключение параллелизма в случае конфликтов.
Причина, по которой я задаю этот вопрос, связана с конкретным крайним случаем на стороне запроса: существует вероятность того, что событие «UserCreated» может быть обработано после события «UserUpdated».
Однако в общем случае события обрабатываются по порядку — сначала событие «UserCreated», а затем события «UserUpdated» в порядке версий.
Моя главная цель — сохранить эту операцию атомарной и в идеале в одном запросе без использования транзакций.

Вернуться в «C#»