文章目录
- 引言:没有数据竞争,为什么还会改错?
- 一、本篇学习目标与准备
- 二、锁和版本号,分别解决什么问题?
- 1. Mutex 保护正在发生的内存操作
- 2. 版本号判断读取之后有没有发生修改
- 3. 两个页面的请求会怎样变化?
- 三、先把版本规则约定清楚
- 1. 什么时候增加版本?
- 2. 旧版本碰巧与当前状态相同,能否直接成功?
- 3. 它和幂等性是什么关系?
- 四、让版本条件贯穿 Handler、Service 和 Store
- 1. 修改存储接口
- 2. Handler 为什么继续使用指针?
- 3. 用可识别错误区分冲突
- 五、内存版本:比较与修改必须放在同一个锁里
- 六、SQLite 版本:把比较条件写进 UPDATE
- 1. 为什么“先 SELECT,再 UPDATE”不够?
- 2. 没返回行,不一定代表任务不存在
- 3. 为什么限制一个连接仍然需要版本号?
- 七、已有数据库怎么增加 version?
- 1. 只改 CREATE TABLE 不会修改旧表
- 2. 重启时不能把版本重新设为1
- 3. 本篇迁移的适用范围
- 八、实际走一遍:成功、冲突、刷新、再修改
- 1. 启动服务
- 2. 创建任务
- 3. 页面A设置完成
- 4. 模拟页面B提交旧版本
- 5. 读取最新任务后,明确要求恢复未完成
- 九、前端收到409,怎样处理才有意义?
- 1. 保留读取时拿到的版本
- 2. 为什么不能自动刷新版本后再次写入?
- 3. 还需要防重复点击吗?
- 十、测试要检查返回值,也要检查没有发生什么
- 1. 内存与 SQLite 共用版本行为测试
- 2. 让多个请求真正竞争同一版本
- 3. HTTP 与迁移测试
- 4. 本次实际验证范围
- 十一、实战练习与下一步
- 练习1:增加标题编辑
- 练习2:测试状态变回原值
- 练习3:设计冲突提示
- 参考资料
✍创作者:全栈弄潮儿
🏡 个人主页:全栈弄潮儿的个人主页
🏙️ 个人社区,欢迎你的加入:全栈开发社区
📙 专栏:Go零基础实战
引言:没有数据竞争,为什么还会改错?
第21篇,我们给内存 map 加了锁;第22篇把任务保存到 SQLite;第23篇又增加了分页和状态筛选。
看起来,任务已经能安全地创建、查询和修改。
但假设你打开两个页面,它们都显示同一条任务未完成:
- 页面A把任务设为已完成,保存成功。
- 页面B还停留在旧画面,稍后保存表单,把里面的
completed=false发给服务端。 - 服务端照单全收,任务又变回未完成。
两次请求即使严格