Documentation
¶
Overview ¶
Package sqlitex 在 glebarez/sqlite 之上补一条 database/sql 不会替你做的事: **事务结束后保证连接是干净的**。
为什么需要它(这是线上整库锁死的直接原因):
- SQLite 的回滚日志模式(journal_mode=delete)下,COMMIT 需要 EXCLUSIVE 锁; 只要有别的连接持着 SHARED(一次普通查询就算),它就得等,等不到 busy_timeout 就返回 SQLITE_BUSY。**而 SQLite 的语义是:COMMIT 返回 BUSY 时 事务并没有结束,仍然活着**(可以重试 COMMIT 或 ROLLBACK)。
- Go 的 database/sql 在 Tx.Commit 出错时只做一件事:把连接放回池子。 它既不会调用 driver.Tx.Rollback,也不会因为 BUSY 丢弃连接 (见 GOROOT/src/database/sql/sql.go 的 Tx.Commit → tx.close(err) → putConn(err):只有 err == driver.ErrBadConn 才会 Close,其余一律回池)。
- 于是那条连接带着一个**没结束的写事务**回到空闲池:RESERVED/PENDING 锁永远 不放。之后所有读都等满 busy_timeout 报 SQLITE_BUSY,所有写在拿锁时失败, 在这条连接上 Begin 直接报 "cannot start a transaction within a transaction (1)"——整库死锁,直到进程重启(重启时 SQLite 用残留的 -journal 回滚)。
这个包做的事只有两件:
- COMMIT 失败时**自己把事务回滚掉**(先驱动 Rollback,再退回 SQL ROLLBACK)。
- 回滚不掉时返回 driver.ErrBadConn,让 database/sql 丢弃这条连接(进程内关闭 文件描述符、释放 fcntl 锁)——宁可换一条新连接,也不把脏连接放回池子。
用 DriverName + sqlite.Dialector{DriverName: sqlitex.DriverName} 启用,见 models.Init。
Index ¶
Constants ¶
View Source
const DriverName = "sqlite-guarded"
DriverName 是受保护驱动的注册名。
与 glebarez 自带的 "sqlite" 并存:测试与迁移脚本里直接用 "sqlite" 仍然可用, 只有服务自己的连接池走这一条。
Variables ¶
This section is empty.
Functions ¶
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.