SQLAlchemy 内存 SQLite 已建表,请求却报 no such table:连接没有共享
内存 SQLite 已建表但 FastAPI TestClient 请求仍报 no such table 时,检查线程与连接范围;区分串行 StaticPool 和并发共享内存 URI,保留事务与数据生命周期限制。
启动代码已经执行 create_all,日志也出现 CREATE TABLE 和 COMMIT,但 FastAPI TestClient 发起请求后仍报 sqlite3.OperationalError: no such table。如果数据库 URL 使用 :memory:,应先检查请求与建表是否用了同一底层连接。
普通 SQLite 内存数据库属于创建它的连接。同样的 :memory: URL 再开一个连接,会得到另一份独立数据库。已经设置 check_same_thread=False,只是允许连接跨线程使用,不会自动让不同连接共享数据。
建表成功,只说明那条连接里的表存在
同步 SQLAlchemy 的 pysqlite 方言在普通 :memory: 场景默认使用 SingletonThreadPool,每个线程维护自己的连接。启动线程和处理请求的线程不同,就可能各自访问一份内存数据库。
核对建表使用的 engine、请求依赖绑定的 engine 和实际数据库 URL。项目不应在每次请求中重新 create_engine;即使两处字符串相同,新建的普通内存连接也不等于原来的库。多个进程也不能通过 :memory: 共享数据。
可以在真正失败的连接上查看表列表,再与建表连接对照。下面是只读 SQL 示例,检查的是 main 库:
SELECT name FROM main.sqlite_master WHERE type = 'table';
一处有目标表、另一处没有,说明需要继续核对连接范围。文件数据库则应检查实际路径、迁移与表名,不能因同样报 no such table 就改成内存连接方案。
串行测试可明确复用一个连接
以下配置适用于同一进程中的串行内存测试:所有数据库访问必须完整串行化,没有重叠的 Session 或事务操作。它会让 engine 的连接取用复用同一底层连接:
from sqlalchemy import create_engine
from sqlalchemy.pool import StaticPool
engine = create_engine(
"sqlite://",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
将建表和请求使用的 sessionmaker 都绑定到这一个 engine,并在处理测试请求前初始化模型。已有业务模型仍须完整导入;连接共享不会替 create_all 找到尚未登记的模型。
StaticPool 不提供独立的并发事务。多个 Session 共用一条连接时,一个 Session 的 rollback 可能撤销另一个尚未提交的工作,commit 也可能互相干扰。因此仅给 FastAPI 加这段配置,不代表并发请求已安全。应覆盖整个数据库业务单元的串行访问,不能只给某条 SQL 加锁就宣称事务隔离成立。
测试结束后关闭会话并清理 engine。普通内存连接关闭会丢失数据;不要在测试请求之间 dispose engine,然后期待表还存在。
并发内存测试需要不同的连接设计
当前 SQLAlchemy 文档对并发内存使用建议采用 SQLite URI 的共享缓存方式,让不同连接拥有各自事务状态,同时访问同一个进程内的内存库。以下是同步 pysqlite 的结构示例,名称应按测试隔离规则设置:
engine = create_engine(
"sqlite:///file:debug_test?mode=memory&cache=shared&uri=true"
)
uri=true 让驱动按 URI 参数处理,mode=memory 指定内存库,cache=shared 开启共享。缺少必要的 URI 配置时,文件名可能被当作普通磁盘路径;不要仅看字符串含 memory 就认定没有创建文件。
同一进程、使用同一个名字的连接会访问同一份库。彼此应隔离的测试需使用不同名称,不能让多个测试套件共用 debug_test。最后一条连接关闭后,库也会消失;连接池与测试夹具需共同管理其生命周期。
这不让多个进程共享内存,也不消除 SQLite 的写锁和并发限制。异步 aiosqlite 的 engine 与池规则需按对应版本核对,不能把同步 create_engine 配置直接贴进异步调用。需要持久数据或正式并发负载时,应按项目选择持久数据库方案。
验收要穿过真实请求线程
保存原测试数据库配置与夹具,先用隔离数据检查初始化后发出的真实请求,确认它能看到同一批表和数据。只在启动线程执行一次查询,无法验证 TestClient 或 Web 请求的连接选择。
再覆盖失败回滚、连续请求与测试清理。若设计允许并发,检查事务之间是否互相撤销,以及关闭连接后是否按预期重新初始化。成功标准包括表不再意外消失、测试数据互不污染和生命周期可解释。
配置回退应一起恢复夹具、建表与请求绑定。内存数据没有持久恢复保障,不能把重建空表当成业务数据已经找回;这些配置应只用于明确允许丢失数据的测试范围。


