У меня есть высокочастотная конечная точка, которая получает координаты GPS от мобильных приложений.
Код: Выделить всё
[HttpPost("{courierId:guid}/locations")]
public async Task ReceiveLocations(Guid courierId, [FromBody] LocationsDto locationsDto)
{
Вот мой метод репозитория:
Код: Выделить всё
public async Task AddOrUpdate(LastLocationEntity lastLocationEntity)
{
var existingCourierLocation = await applicationDbContext.LastLocations
.FindAsync(lastLocationEntity.CourierId);
if (existingCourierLocation is null)
applicationDbContext.LastLocations.Add(lastLocationEntity);
else
applicationDbContext.Entry(existingCourierLocation).CurrentValues.SetValues(lastLocationEntity);
await applicationDbContext.SaveChangesAsync();
}
Код: Выделить всё
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes.
---> Npgsql.PostgresException (0x80004005): 23505: duplicate key value violates unique constraint "PK_LastLocations"
...
Detail: Key ("CourierId")=(...) already exists.
Я успешно воспроизвел это с помощью k6, отправив пакет одновременных запросов для одного и того же CourierId в пустую базу данных.
Я понимаю, что это Состояние гонки во время первоначальной вставки:
- Два (или более) запроса выполняют FindAsync одновременно.
- Оба получают null, поскольку транзакция еще не зафиксирована.
- Оба пытаются добавить новый объект.
- вызывается. Первый выполняется успешно, второй завершается с ошибкой 23505.
Код: Выделить всё
SaveChangesAsync
Каков стандартный способ обработки этого сценария «Вставка или обновление» (Upsert) в EF Core, чтобы избежать условий гонки?
/>Следует ли мне заключить это в блок try-catch с политикой повтора или есть способ выполнить атомарный UPSERT (например, INSERT ... ON CONFLICT) непосредственно в EF Core, не переходя к необработанному SQL?
Спасибо!>
Подробнее здесь: https://stackoverflow.com/questions/798 ... ndition-on
Мобильная версия