记一次 Laravel MySQL 验证出 bug 的经历

  • 本人之前做了一个简单的会议预约系统
  • 逻辑非常的简单
  • 建立用户占用时间表 used_time
  • 和会议预约表 meeting_books
  • 规定会议时间以半小时为单位,最长不得超过两个小时
  • 用户每预定一个时间我就会向占用时间表里插入时间
  • 比如预定 08:00~09:00 我就会向占用时间表插入 08:0008:30
  • 08:0008:30 代表的是时间段的含义,是08:00~08:3008:30~09:00 的意思

预约创建的逻辑是
  1. 在占用时间表,验证请求时间是否被占用,没有则进行第2步,有则返回失败
  2. 创建预约,并将请求写入操作日志
  3. 向占用时间表插入数据

  • 假设用户选中 2018-08-30 08:00~09:00 的时间在一号会议室开会
  • 在创建订单的时候我会先从占用时间表 used_time 查找 一号会议室 2018-08-30 里有没有 08:0008:30
  • 如果有返回时间已被占用,创建失败
  • 没有就创建预约,并纪录日志
  • 然后再占用时间表里插入 一号会议室 2018-08-3008:0008:30 时间段
  • 最后返回 201 给前端

出现的问题
  • 但有一次出现了一个bug,一个用户再同一天同一个时间段创建了两个请求
  • 我当时很困惑,这个用户是怎么通过前端验证和后端验证的
  • 于是私聊她,她说是第一个请求时出现error 提示她失败
  • 于是她又重新立马创建了一个
  • 但是最后两个请求都成功了

伪代码逻辑
  • 这里为什么 createfindOrFail ,是因为 create 返回的是创建实例,而不是所有数据,我日志需要纪录所有数据
//创建数据
$meeting = meeting::create($data);

//查询数据
$meeting = meeting::findOrFail($meeting->id);

//纪录操作日志
Log::insert($meeting);
.......
//占用时间表插入
usedTime::insert($data);

问题出现的原因
  • 通过这番调查我大概明白了问题出现的原因
  • 线上数据库是读写分离的,主库写入,但更新数据后马上查询从库可能没来得及更新数据
  • 当网络不好时遇到从库更新不及时 findOrFail 就会报 404,导致后面所有操作都失效,自然验证也失效了
解决方法
  • 更新完后,查询指定使用主库
  • 更新后禁止查询,直接使用 create 返回的 model 实例,对于数据库中的有默认值的字段,手动加入到 model 实例中
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 5

这个配置有设置过吗?

'mysql' => [
    ...
    'sticky'    => true,
    ...
],
5年前 评论
Aaron

@baiachen 这配置的作用是什么呢?

5年前 评论

@Aaron
sticky 是一个 可选值,它可用于立即读取在当前请求周期内已写入数据库的记录。若 sticky 被启用,并且当前请求周期内执行过「写」操作,那么任何「读」操作都将使用「写」连接。这样可确保同一周期内写入的数据在同一周期内都可以被立即读取。它是否启用,取决于应用程序的需求

5年前 评论
Aaron

@baiachen 谢谢

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!