У меня есть. net Core 2 API + Entity Framework + PostgreSQL. Таблица с целочисленным полем, скажем,
class Order
{
public Guid Id { get; set;}
public int ShopId { get; set; }
public OrderType Type { get; set; }
public int OrderId { get; set; }
}
В этой таблице у нас есть заказы из многих магазинов, два типа заказов: OldSystem и NewSystem. Это означает, что у нас уже есть много старых заказов. Этот новый проект может создавать новые заказы («Новая система»), и нам нужно автоматическое увеличение для OrderId в каждом магазине! Orderd 1, 2, 3, ... в магазине 1, заказы 1, 2, 3 ... в магазине 2.
Другими словами, я мог бы написать что-то вроде:
var lastOrderId = await dbContext.Orders.Where(x =>
x.ShopId == currentShopId &&
x.OrderId != null &&
x.Type == OrderType.NewSystem)
.MaxAsync(x => x.OrderId);
var nextOrderId = (lastOrderId ?? 0) + 1;
newOrder.OrderId = nextOrderId;
await dbContext.Orders.InsertAsync(newOrder);
await dbContext.SaveChangesAsync();
Но он не защищен от дубликатов. В то же время другой API-запрос может сделать то же самое: получить Max + 1 и Insert + Save.
Я вижу несколько возможных решений, прошу совета.
- Optimisti c блокировка: после сохранения проверять дубликаты OrderId (в том магазине и типе), если найдены - генерировать снова, проверять снова и так далее ... В случае высокой загрузки - возможен бесконечный l oop.
- Добавьте новую таблицу «Генератор последовательностей», у каждого магазина есть собственная запись, запись блокировки во время генерации - но EF не предоставляет никакого механизма блокировки.
- Записывает некоторые необработанные SQL (или хранимая процедура). Звучит не очень хорошо.
- Используйте RedLock или что-то еще для какой-то распределенной блокировки (система масштабируема) в коде. Используйте shopId в качестве ключа для блокировки (разрешите разным магазинам генерировать собственные последовательности одновременно). Слишком сложно, и нам нужен Redis для этого решения.
Есть идеи? Заранее спасибо!