«Сериализуемые» транзакции против защищенного атомарного UPDATE, условий параллелизмаMySql

Форум по Mysql
Ответить
Anonymous
 «Сериализуемые» транзакции против защищенного атомарного UPDATE, условий параллелизма

Сообщение Anonymous »

Некоторое время я занимаюсь созданием параллельных систем. Раньше я нашел несколько статей о "serializable" транзакции. Я использовал код сначала ef core в первую очередь для любых обновлений базы данных.
Разница между «зафиксированным чтением» и «повторяемым чтением» в SQL Server.
Представьте себе случай использования, когда два одновременных пользователя выкупают что-то дважды, а это следует выкупать только один раз (сканирование купона на продукт или что-то в этом роде). Пользователь либо выкупает, либо нет. В худшем случае кто-то выкупает дважды, используя 2 устройства. (хороший сценарий: 1 пользователь выкупает что-то и не может выкупить снова или другой пользователь выкупает то же самое)
Всякий раз, когда я сталкивался с проблемой, когда несколько машин или устройств пытаются активировать одну и ту же запись одновременно (я не говорю сейчас об оптимистическом параллелизме, поскольку я не ожидаю, что пользователь изменит данные, предоставив мне свои собственные входные данные, а кто-то другой переопределит это, это просто чистый вызов чтобы получить существующую запись и изменить статус), я использовал следующий код (передача делегата
CommitIsolatedTransactionForEntityFuncAsync

это просто работало нормально и выполняло свою работу при отладке и тестировании транзакции, которая «пришла после первой», и была откатана. Таким образом, нет несогласованности данных и, что наиболее важно, нет двух пользователей, которые дважды активируют какой-либо вариант использования, который следует активировать только один раз. (даже как сказал автор статьи, «хотя они могут облегчить вам жизнь, всегда полностью блокируют все возможные одновременные операции»
public async Task CommitIsolatedTransactionForEntityFuncAsync(Func methodToExecute)
{
var strategy = _context.Database.CreateExecutionStrategy();

await strategy.ExecuteAsync(async () =>
{
await using var dbContextTransaction = await _context.Database.BeginTransactionAsync(System.Data.IsolationLevel.Serializable);
try
{
await methodToExecute();
await _context.SaveChangesAsync();

await dbContextTransaction.CommitAsync();
}
catch
{
await dbContextTransaction.RollbackAsync();
throw;
}
});
}

вот пример методаToExecute
public async Task SetSaleStatusToRedeemed(Guid saleId, Guid machineId)
{
var currentSaleForRedemption = await _context.Set().SingleAsync(x => x.SaleId == saleId);
currentSaleForRedemption.Redeemed = DateTime.UtcNow;
currentSaleForRedemption.SaleStatus = SaleStatus.Redeemed;
currentSaleForRedemption.MachineId = machineId;
}

Однако кто-то мне подсказал, что даже если это сработает, это не так эффективно, а также может привести к взаимоблокировкам, и мне следует использовать следующее защищенное атомарное ОБНОВЛЕНИЕ:
public async Task TryRedeemSaleAsync(Guid saleId, Guid machineId)
{
var rows = await _context.Database.ExecuteSqlInterpolatedAsync($@"
UPDATE Sales
SET
Redeemed = {DateTime.UtcNow},
SaleStatus = {(int)SaleStatus.Redeemed},
MachineId = {machineId}
WHERE SaleId = {saleId}
AND SaleStatus {(int)SaleStatus.Redeemed}
");

return rows == 1;
}

Как я могу гарантировать ровно однократную обработку этой строки без условий гонки?» На самом деле у меня есть версия строки в каждой таблице.
Даже с этим новым подходом мне пришлось бы сделать следующее: какой в этом смысл, если условие гонки по-прежнему выдает ошибку. Какой правильный путь для этого случая? Исключение выдается в обоих случаях, и условие гонки предотвращается, какой из них лучше.
if (rows == 0)
throw new InvalidOperationException("Already redeemed");


Подробнее здесь: https://stackoverflow.com/questions/798 ... ng-conditi
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

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