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