checkpoint: SQLite 清理——业务/测试层统一 PostgreSQL(删 aiosqlite/fakeredis)
- setting: DATABASE_TYPE 收窄为 postgres,DB_URI/ASYNC_DB_URI 去 sqlite 分支 - database: create_async_engine_and_session 去 sqlite 分支(同步 psycopg 引擎保留给 jobstore) - number_gen: 去 DATABASE_TYPE 非 postgres 早退(advisory lock 恒定走 PG) - chat/crud: 删 SqliteDb 分支(agno 无硬依赖) - pyproject/requirements/uv.lock: 删 aiosqlite、fakeredis;conftest 口径注释同步 - test_stat_analysis_tc: docstring 口径 SQLite→PG
This commit is contained in:
@@ -3,7 +3,6 @@ from typing import Any
|
||||
from agno.db.base import SessionType
|
||||
from agno.db.mysql import MySQLDb
|
||||
from agno.db.postgres import PostgresDb
|
||||
from agno.db.sqlite import SqliteDb
|
||||
from agno.session.team import TeamSession
|
||||
|
||||
from app.config.setting import settings
|
||||
@@ -34,7 +33,6 @@ class ChatSessionCRUD:
|
||||
db_mapping = {
|
||||
"mysql": lambda: MySQLDb(db_url=db_uri, db_schema=settings.DATABASE_NAME, create_schema=False),
|
||||
"postgres": lambda: PostgresDb(db_url=db_uri, db_schema="public", create_schema=False),
|
||||
"sqlite": lambda: SqliteDb(db_file=db_uri.replace("sqlite:///", "")),
|
||||
}
|
||||
|
||||
if db_type not in db_mapping:
|
||||
|
||||
@@ -10,7 +10,6 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from app.common.enums import EnvironmentEnum
|
||||
from app.config.path_conf import ENV_DIR, STATIC_DIR
|
||||
|
||||
|
||||
# JWT 开发默认密钥:仅允许 DEV 环境使用,生产命中即拒绝启动
|
||||
_DEV_DEFAULT_SECRET_KEY = "dpb-dev-secret-key-do-not-use-in-production"
|
||||
|
||||
@@ -110,8 +109,8 @@ class Settings(BaseSettings):
|
||||
AUTOFETCH: bool | None = None # AUTOFLUSH 别名(优先级高于 AUTOFLUSH,兼容旧环境变量名)
|
||||
EXPIRE_ON_COMMIT: bool = False # 是否在提交时过期
|
||||
|
||||
# 数据库连接(生产 PostgreSQL;测试套件用 SQLite)
|
||||
DATABASE_TYPE: Literal["postgres", "sqlite"] = "postgres"
|
||||
# 数据库连接(开发与生产统一 PostgreSQL)
|
||||
DATABASE_TYPE: Literal["postgres"] = "postgres"
|
||||
DATABASE_HOST: str = "localhost"
|
||||
DATABASE_PORT: int = 5432
|
||||
DATABASE_USER: str = "dpb"
|
||||
@@ -305,19 +304,11 @@ class Settings(BaseSettings):
|
||||
|
||||
@property
|
||||
def ASYNC_DB_URI(self) -> str:
|
||||
if self.DATABASE_TYPE == "postgres":
|
||||
db_connect = f"postgresql+asyncpg://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
|
||||
else:
|
||||
db_connect = f"sqlite+aiosqlite:///{self.DATABASE_NAME}.db"
|
||||
return db_connect
|
||||
return f"postgresql+asyncpg://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
|
||||
|
||||
@property
|
||||
def DB_URI(self) -> str:
|
||||
if self.DATABASE_TYPE == "postgres":
|
||||
db_connect = f"postgresql+psycopg://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
|
||||
else:
|
||||
db_connect = f"sqlite:///{self.DATABASE_NAME}.db"
|
||||
return db_connect
|
||||
return f"postgresql+psycopg://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
|
||||
|
||||
@property
|
||||
def FASTAPI_CONFIG(self) -> dict[str, Any]:
|
||||
|
||||
@@ -49,29 +49,19 @@ def create_async_engine_and_session(db_url: str = settings.ASYNC_DB_URI) -> tupl
|
||||
- tuple[AsyncEngine, async_sessionmaker[AsyncSession]]: 异步数据库引擎和会话工厂。
|
||||
"""
|
||||
try:
|
||||
# 异步数据库引擎
|
||||
if settings.DATABASE_TYPE == "sqlite":
|
||||
async_engine = create_async_engine(
|
||||
url=db_url,
|
||||
echo=settings.DATABASE_ECHO,
|
||||
echo_pool=settings.ECHO_POOL,
|
||||
pool_pre_ping=settings.POOL_PRE_PING,
|
||||
future=settings.FUTURE,
|
||||
pool_recycle=settings.POOL_RECYCLE,
|
||||
)
|
||||
else:
|
||||
async_engine = create_async_engine(
|
||||
url=db_url,
|
||||
echo=settings.DATABASE_ECHO,
|
||||
echo_pool=settings.ECHO_POOL,
|
||||
pool_pre_ping=settings.POOL_PRE_PING,
|
||||
future=settings.FUTURE,
|
||||
pool_recycle=settings.POOL_RECYCLE,
|
||||
pool_size=settings.POOL_SIZE,
|
||||
max_overflow=settings.MAX_OVERFLOW,
|
||||
pool_timeout=settings.POOL_TIMEOUT,
|
||||
pool_use_lifo=settings.POOL_USE_LIFO,
|
||||
)
|
||||
# 异步数据库引擎(统一 PostgreSQL 连接池参数)
|
||||
async_engine = create_async_engine(
|
||||
url=db_url,
|
||||
echo=settings.DATABASE_ECHO,
|
||||
echo_pool=settings.ECHO_POOL,
|
||||
pool_pre_ping=settings.POOL_PRE_PING,
|
||||
future=settings.FUTURE,
|
||||
pool_recycle=settings.POOL_RECYCLE,
|
||||
pool_size=settings.POOL_SIZE,
|
||||
max_overflow=settings.MAX_OVERFLOW,
|
||||
pool_timeout=settings.POOL_TIMEOUT,
|
||||
pool_use_lifo=settings.POOL_USE_LIFO,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 数据库连接失败 {e}")
|
||||
raise
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
存量数据迁移:首次使用某序列名时由 seed_fn(服务端传入)从既有记录推算
|
||||
起始号(原扫描逻辑),此后全部走原子递增,不再扫描。
|
||||
"""
|
||||
from typing import Any, Awaitable, Callable
|
||||
from collections.abc import Awaitable, Callable
|
||||
|
||||
from sqlalchemy import select, text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -23,10 +23,6 @@ class NumberGenService:
|
||||
|
||||
@staticmethod
|
||||
async def _acquire_lock(db: AsyncSession, name: str) -> None:
|
||||
from app.config.setting import settings
|
||||
|
||||
if settings.DATABASE_TYPE != "postgres":
|
||||
return # sqlite 测试库无 advisory lock,退化为普通自增(单进程无并发竞争)
|
||||
# hashtextextended 对文本做稳定 64 位哈希,同名序列恒映射同一把锁
|
||||
await db.execute(text("SELECT pg_advisory_xact_lock(hashtextextended(:n, 0))"), {"n": name})
|
||||
|
||||
|
||||
@@ -43,15 +43,13 @@ postgres = ["asyncpg==0.31.0"]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"aiosqlite==0.22.1", # 测试:SQLite 内存库(conftest 固定 DATABASE_TYPE=sqlite,勿移除)
|
||||
"fakeredis>=2.21.0,<3", # 测试:内存 Redis,配合 TESTING
|
||||
"pytest==9.0.2",
|
||||
"pytest-asyncio>=0.24.0", # 异步测试支持
|
||||
"ruff==0.14.13", # 代码检查/格式化
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
default-groups = ["dev"] # 本地 uv sync 含 dev;生产 pip -r requirements.txt 不含 pytest/ruff/fakeredis
|
||||
default-groups = ["dev"] # 本地 uv sync 含 dev;生产 pip -r requirements.txt 不含 pytest/ruff
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "tuna"
|
||||
@@ -118,7 +116,7 @@ ignore = [
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/conftest.py" = ["E402"] # 先设置 TESTING/SQLite 环境再 import 应用
|
||||
"tests/conftest.py" = ["E402"] # 先设置 TESTING/PostgreSQL 环境再 import 应用
|
||||
|
||||
[tool.ruff.format]
|
||||
docstring-code-format = true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 与 pyproject.toml [project] dependencies 一致(生产 pip;不含 dev 组:pytest / ruff / fakeredis)
|
||||
# 与 pyproject.toml [project] dependencies 一致(生产 pip;不含 dev 组:pytest / ruff)
|
||||
agno==2.5.8 # AI agent框架
|
||||
aiofiles==24.1.0 # 文件操作
|
||||
alembic==1.18.4 # 数据库迁移
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
本包装层以 subprocess 顺序调用这些脚本,断言 rc==0 —— 与 run_regression.py 的执行契约一致。
|
||||
|
||||
关键点:
|
||||
- 全部基于真实 PG16 + Redis(本工程无 SQLite),仅当探测到 PG/Redis 可达才跑,否则 skipif 跳过。
|
||||
- 不 import 任何 app 模块(避免被 conftest 的 sqlite 环境污染);subprocess env 显式构建:
|
||||
剥掉 conftest 注入的 DATABASE_TYPE/DATABASE_NAME/POOL_SIZE/MAX_OVERFLOW/CAPTCHA_ENABLE,
|
||||
- 全部基于真实 PG16 + Redis,仅当探测到 PG/Redis 可达才跑,否则 skipif 跳过。
|
||||
- 不 import 任何 app 模块(测试库生命周期由 conftest 管理,与统计引擎 tc 独立);subprocess env 显式构建:
|
||||
清空可能残留的 DATABASE_*/REDIS_*/POOL_*/CAPTCHA_ENABLE 等连接变量,
|
||||
置 ENVIRONMENT=dev / DATABASE_TYPE=postgres / PYTHONPATH=backend / PYTHONUTF8=1,
|
||||
再并入从 backend/env/.env.dev 解析的真实连接值(可被 TC_CI_* 覆盖,用于验证 skipif 路径)。
|
||||
- 标记 pg(pyproject 已注册),`pytest -m pg` 只跑本组;run_ci.py 的验收门即此命令。
|
||||
@@ -118,7 +118,7 @@ def pg_ready() -> tuple[bool, bool, str]:
|
||||
|
||||
|
||||
def _sub_env() -> dict:
|
||||
"""显式构建子进程环境:剥掉 conftest 注入的 sqlite 变量,置真实 PG/Redis 连接。"""
|
||||
"""显式构建子进程环境:清空可能残留的连接变量,置真实 PG/Redis 连接。"""
|
||||
env = dict(os.environ)
|
||||
for k in ("DATABASE_TYPE", "DATABASE_NAME", "POOL_SIZE", "MAX_OVERFLOW",
|
||||
"CAPTCHA_ENABLE", "ENVIRONMENT", "PYTHONPATH", "PYTHONIOENCODING"):
|
||||
|
||||
Generated
-35
@@ -46,15 +46,6 @@ wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aiosqlite"
|
||||
version = "0.22.1"
|
||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821, upload-time = "2025-12-23T19:25:43.997Z" }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405, upload-time = "2025-12-23T19:25:42.139Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.18.4"
|
||||
@@ -197,8 +188,6 @@ postgres = [
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "aiosqlite" },
|
||||
{ name = "fakeredis" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "ruff" },
|
||||
@@ -243,8 +232,6 @@ provides-extras = ["postgres"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "aiosqlite", specifier = "==0.22.1" },
|
||||
{ name = "fakeredis", specifier = ">=2.21.0,<3" },
|
||||
{ name = "pytest", specifier = "==9.0.2" },
|
||||
{ name = "pytest-asyncio", specifier = ">=0.24.0" },
|
||||
{ name = "ruff", specifier = "==0.14.13" },
|
||||
@@ -353,19 +340,6 @@ wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fakeredis"
|
||||
version = "2.34.1"
|
||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||
dependencies = [
|
||||
{ name = "redis" },
|
||||
{ name = "sortedcontainers" },
|
||||
]
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/11/40/fd09efa66205eb32253d2b2ebc63537281384d2040f0a88bcd2289e120e4/fakeredis-2.34.1.tar.gz", hash = "sha256:4ff55606982972eecce3ab410e03d746c11fe5deda6381d913641fbd8865ea9b", size = 177315, upload-time = "2026-02-25T13:17:51.315Z" }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/49/b5/82f89307d0d769cd9bf46a54fb9136be08e4e57c5570ae421db4c9a2ba62/fakeredis-2.34.1-py3-none-any.whl", hash = "sha256:0107ec99d48913e7eec2a5e3e2403d1bd5f8aa6489d1a634571b975289c48f12", size = 122160, upload-time = "2026-02-25T13:17:49.701Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.138.2"
|
||||
@@ -1251,15 +1225,6 @@ wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sortedcontainers"
|
||||
version = "2.4.0"
|
||||
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
|
||||
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" }
|
||||
wheels = [
|
||||
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.51"
|
||||
|
||||
Reference in New Issue
Block a user