mysql - Laravel 悲观锁定不能防止在多个重复请求中对未提交的行进行 SELECT
问题描述
双击按钮时,我试图防止无意的重复请求第二次更改我的模型。我执行以下操作。
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 悲观锁无法正常工作
有什么解释或解决方案吗?
解决方案
一个可能的解决方案是在点击端点之前禁用按钮,即使您设法解决了这个问题,这也很重要,因为您根本不应该允许(尽可能多地)发送重复请求。
推荐阅读
- join - U-sql Inner JOIN 耗时太长
- docker - Docker 将只读挂载点挂载为卷
- bash - Azure DevOps Pipelines 中的 bash 脚本任务如何访问服务连接?
- javascript - jsPDF | autoTable 的 dataKey 不能以角度工作
- asp.net-core - 从 Windows 身份验证迁移到登录页面
- php - 如何通过 docusign 创建信封 API 更新“未命名”字段?
- objective-c - 在 drawRect NSView (macOS & ObjC) 上绘制图像(作为覆盖)
- c# - 为了避免空值,如何制定一种更好的逻辑来将 api 结果传递给我的项目中的对象
- node.js - 如何在 Node.js 后端使用 Passport.js 实现刷新令牌流?
- quasar-framework - Quasar中的QLayout