Files
flvx/plans/042-sqlite-tunnel-entry-update-deadlock.md
T
sagit 2df061a19f fix(backend): resolve SQLite deadlock in tunnel entry updates (#334)
- Fix deadlock when updating tunnel entries with offline nodes
- Add test file for tunnel entry SQLite operations
- Update contract tests for entry port conflict and limiter sync
- Add plan documents for SQLite deadlock fix and contract semantics
2026-03-18 09:00:20 +08:00

2.8 KiB

042 - SQLite 隧道编辑添加入口节点卡死排查

Issue

  • 现象:SQLite 数据库下,编辑已有隧道并新增入口节点时接口卡住;PostgreSQL 下同样操作正常。
  • 初步判断:tunnelUpdate 在事务尚未提交时触发了额外 repository 读查询,SQLite 配置 MaxOpenConns(1),容易在同一请求内形成自锁等待。

Goal

  • 找出 SQLite 与 PostgreSQL 行为差异的根因。
  • 修复隧道编辑新增入口节点时的阻塞问题,同时不破坏现有的入口端口冲突校验。
  • 补充最小回归测试,锁定“事务内校验不可再次占用根连接”的场景。

Checklist

  • 复核 tunnelUpdate 在新增入口节点路径上的调用链,确认事务内哪些查询绕过了 tx。
  • 为相关 repository 查询补齐 Tx 版本,避免 SQLite 单连接下的自锁等待。
  • 调整 handler 中入口端口冲突校验,保证事务内全程复用同一个 tx。
  • 增加针对 SQLite 的回归测试,验证事务内校验不会阻塞。
  • 运行相关 handler/backend 测试并记录结果。

Notes

  • 重点关注 validateTunnelEntryPortConflictsForNewEntries:当前它在 tx.Commit() 前执行,但内部调用 ListForwardsByTunnel / ListForwardPorts / HasOtherForwardOnNodePort 等非事务查询。
  • SQLite 在 go-backend/internal/store/repo/repository.go 中显式设置了 SetMaxOpenConns(1),因此这种模式在 SQLite 下会比 PostgreSQL 更容易表现为“卡死”。

实际改动

  • go-backend/internal/store/repo/repository_control.go
    • 新增 ListForwardsByTunnelTx、ListForwardPortsTx、HasOtherForwardOnNodePortTx,并让原有非事务方法复用统一实现。
  • go-backend/internal/http/handler/mutations.go
    • tunnelUpdate 在事务内执行入口端口冲突校验时显式传入当前 tx。
    • validateTunnelEntryPortConflictsForNewEntries 改为全程使用事务查询。
    • 新增 validateForwardPortAvailabilityTx,避免事务内回落到根连接查询。
  • go-backend/internal/http/handler/tunnel_entry_sqlite_test.go
    • 新增 SQLite 回归测试,验证开启事务后执行新增入口校验不会阻塞。

测试结果

通过

cd go-backend && go test ./internal/http/handler/...
  • 结果:通过。

额外检查

cd go-backend && go test ./tests/contract/...
  • 结果:未全绿;当前失败集中在既有的入口端口语义合同用例:
    • TestIssue313_EntryPortCrossTunnelConflictContract
    • TestTunnelUpdateChangesEntryNodeButLeavesOldForwardRuntimeContract
    • TestTunnelUpdateEntryTransitionsCleanupForwardRuntimeContract
  • 备注:这些失败反映的是“新增/切换入口时端口校验预期”与现有合同用例之间的行为差异,不是本次 SQLite 事务自锁修复本身的编译或阻塞问题。