问题描述
生产环境数据库日志有大量报错内容为context canceled
的记录。
排查步骤
- 根据报错日志,定位到报错
context canceled
发生在集中在ORM提交事务之前的DDL方法;
报错处业务场景:
服务为B/S架构,数据同步页面后端服务使用golang-Goframe框架开发,数据库为PostgreSQL,服务业务逻辑为首次进入数据同步页面即执行数据同步(客户刷新页面并不会触发数据同步,只会显示历史数据),期间批量并发调用数据查询API并将返参更新到数据库中,因执行更新操作之前需要删除数据库原有数据,故整个删除更新操作开启了事务。1
2
3
4tx,err:=g.DB().Begin(ctx)
if err!=nil{}
tx.DB().Model("table").Delete("key = ?", key)
_, err := tx.DB().Model("table").Data(data).Batch(10).Insert() - 回溯代码发现
Begin(ctx)
传入的context
是请求handle使用的context
,分别开始排查断开handle的原因- 通过接口测试工具排查接口耗时是否大于接口超时限制;
- 通过数据库最大连接数设置,观察活跃连接数排查服务是否存在连接池泄露问题;
- 假设是用户主动刷新页面导致连接被关闭;
- 经测试,在接口返回之前模拟用户刷新操作可复现问题;
- 将
Begin(ctx)
的入参ctx改为通过Background()
返回的根上下文,保证程序同步动作不因请求连接断开而结束; - 为减少接口响应时间,还将事务拆分成多个小事务,也尝试使用乐观锁而不是用事务来执行
Update
操作;