Пессимистическая блокировка Laravel не может предотвратить SELECT в незафиксированной строке в нескольких повторяющихся запросах - PullRequest
0 голосов
/ 17 мая 2019

Я пытался помешать непреднамеренному дублированию запроса изменить мою модель во второй раз при двойном нажатии кнопки. Я делаю следующее.

Log::debug('Begin transaction');
DB::beginTransaction();

$pdo = DB::connection()->getPdo();
$pdo->exec('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');

try {
    $user = \App\User::sharedLock()->find($id);
    Log::debug('Select user: ' . pickfield($user));

    if ($user->checkout) {
        Log::debug('Already checked out, rollback');
        DB::rollback();

        return Redirect::back()->with('error', 'Already checked out');
    }

    Log::debug('Changing checkout..');
    $user->checkout = new \DateTime($request->input('checkout'));

    Log::debug('Saving checkout: ' . pickfield($user));
    $user->save();

    Log::debug('Commit!');
    DB::commit();
} catch(\Exception $e) {
    Log::debug('Error, rollback!');
    DB::rollback();
}

Транзакции по-прежнему выполняются, поэтому они перекрываются друг с другом, и оператор SELECT извлекает несогласованную модель даже после того, как я установил общую блокировку с сериализуемой изоляцией.

[2019-05-17 01:02:45] local.DEBUG: Begin transaction
[2019-05-17 01:02:45] local.DEBUG: Begin transaction
[2019-05-17 01:02:45] local.DEBUG: Select user: {"id":3225,"checkout":null}
[2019-05-17 01:02:45] local.DEBUG: Changing checkout..
[2019-05-17 01:02:45] local.DEBUG: Saving checkout: {"id":3225,"checkout":{"date":"2019-05-17 01:02:45.428000","timezone_type":2,"timezone":"Z"}}
[2019-05-17 01:02:45] local.DEBUG: On model updating: {}
[2019-05-17 01:02:45] local.INFO: Sending email to user1@example.com
[2019-05-17 01:02:45] local.DEBUG: Begin transaction
[2019-05-17 01:02:45] local.DEBUG: Select user: {"id":3225,"checkout":null}
[2019-05-17 01:02:45] local.DEBUG: Changing checkout..
[2019-05-17 01:02:45] local.DEBUG: Saving checkout: {"id":3225,"checkout":{"date":"2019-05-17 01:02:45.604000","timezone_type":2,"timezone":"Z"}}
[2019-05-17 01:02:45] local.DEBUG: On model updating: {}
[2019-05-17 01:02:45] local.INFO: Sending email to user1@example.com
[2019-05-17 01:03:00] local.DEBUG: Commit!
[2019-05-17 01:03:00] local.DEBUG: Error, rollback!

Хотя мой код уже гарантирует, что запрос SELECT будет выполнен в середине другой транзакции. Я использую InnoDB.

Связано, нет решения: Пессимистическая блокировка Laravel не работает, как предполагалось

Любое объяснение или решение?

Ответы [ 2 ]

1 голос
/ 17 мая 2019

О! Я был сбит с толку.

Это был мой тип блокировки, который был неправильным. Это должно быть lockForUpdate.

Блокировка «для обновления» предотвращает изменение или выбор строк с помощью другой общей блокировки.

Пока общий замок ,

Общая блокировка предотвращает изменение выбранных строк до Ваша транзакция фиксируется.

Я тестировал оба, но с движком MyISAM и не работает вообще, поэтому я запутался.

1 голос
/ 17 мая 2019

Возможным решением является отключение кнопки перед попаданием в конечную точку, что важно, даже если вам удалось решить эту проблему, поскольку вы не должны (как можно больше) отправлять дублирующиеся запросы вообще.

...