mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 00:31:58 +00:00
2714890d0d
README.zh-CN.md / README.md 只留:这是什么、为什么用它、主要功能(按领域一行一条,链到文档)、快速开始、部署、文档索引、 开发与测试、许可(原文不动)。原来的每一节按读者和任务分到 17 篇主题文档:ask-data、workflows、evidence、 data-sources(新增:接入数据库、只读保证、上传表格、跨库合并、遮罩列)、data-catalog、tools(新增:工具、审批档位、 能力登记、端点、网络规则——它们管所有工作流,不只应用)、apps、evaluation-and-ledger、schedules(含通知)、deployment、 configuration、postgresql-and-scaling、identity-and-security、architecture、development、upgrade-notes(按时间倒序, 每波先写要做的事)、faq。删掉的只有模块名(api/copilot_gen.py、chat/drive.py、data/schema_view.py)、重复的话和 「以前…现在…」的叙述;操作步骤、命令、配置名、限制、权限边界都有归宿。中文是原文,英文按新的中文写;英文里界面文字 只写英文名,开头注明界面目前只有简体中文,中文占位符换成英文占位符,中文数字的例子改成不依赖中文的说法。 数据剖析可以同时剖析 2–3 张表(baf23998)收进 data-catalog。 新增 scripts/check-docs.py:英文文档(README.md、docs/en/**)不许有中日韩字符和全角符号(代码块也算), README、docs/**、deploy/README.md 的相对链接和锚点都要通,docs/zh-CN 与 docs/en 文件一一对应、标题层级相同。 中文提示里指向 README 章节的地方改指新文档:cluster/guard.py、cluster/version.py、db/base.py 两处报错, data/schema_view.py 文档串,.env.example 三处,scripts/prod.sh、scripts/test-postgres.sh,terms.ts 两处、 DataSourcesTab.tsx、check-manage.mjs 注释,deploy/README.md 三处链接。deploy/agentlab.env 未动。 验收:check-docs 0 问题(英文文档 18 个 0 处中文,相对链接 369 条 0 断链,18 对标题层级一致);相关后端测试 117 通过、7 跳过;tsc -b、check-copy 通过。
87 lines
3.5 KiB
Python
87 lines
3.5 KiB
Python
"""SQLite 只支持单进程:服务启动时拿数据目录上的文件锁(data/.agentlab.lock,flock 独占、不等),拿不到就拒绝启动。
|
||
|
||
**为什么拒绝而不是凑合。** SQLite 应用库下,运行的序号、并发名额、同源队、调度当班……这些在库里协调的东西照样能用,但
|
||
上传表格的版本库、填报记录表、断点库都是数据目录里的 SQLite 文件,两个进程一起写同一个文件靠的是文件锁,换不成库里的锁;
|
||
uvicorn --workers 2 起来的第二个进程以前也只是悄悄地不写上传版本(503),别的照跑、各算各的,最难查。现在照实说:第二个进程
|
||
起不来,报错写明「SQLite 只支持单进程;要多进程把应用库换成 PostgreSQL」。
|
||
|
||
进程退出(含强杀)时操作系统放掉文件锁,重启(dev.sh 的 --reload 先停旧的再起新的)不受影响。同一个进程里重复取(测试里
|
||
一次次起停服务)是幂等的;数据目录换了就放掉旧的、对新目录重取。没有 fcntl 的平台(Windows)只记一条警告。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
import os
|
||
from pathlib import Path
|
||
|
||
from app.core.config import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
LOCK_FILE = ".agentlab.lock"
|
||
|
||
SINGLE_PROCESS = ("应用库是 SQLite 时只支持单进程:数据目录 {dir} 已经被另一个 AgentLab 进程使用({hint})。"
|
||
"SQLite 不能多进程部署——不要给 uvicorn 加 --workers、不要让两个服务指着同一个数据目录;"
|
||
"要多进程,把应用库换成 PostgreSQL(AGENTLAB_DATABASE_URL,见 docs/zh-CN/postgresql-and-scaling.md「用 PostgreSQL」「多进程」)")
|
||
|
||
|
||
class SingleProcessOnly(RuntimeError):
|
||
"""SQLite 下第二个进程启动:拒绝。"""
|
||
|
||
|
||
_held: tuple[Path, int] | None = None
|
||
|
||
|
||
def _path() -> Path:
|
||
return settings.data_dir / LOCK_FILE
|
||
|
||
|
||
def acquire() -> None:
|
||
"""SQLite 时取数据目录上的独占锁,拿不到抛 SingleProcessOnly。PostgreSQL 时什么都不做(可以多进程)。"""
|
||
global _held
|
||
if settings.db_dialect != "sqlite":
|
||
return
|
||
if settings.workers > 1:
|
||
raise SingleProcessOnly(SINGLE_PROCESS.format(dir=settings.data_dir, hint=f"AGENTLAB_WORKERS={settings.workers}"))
|
||
path = _path()
|
||
if _held is not None and _held[0] == path:
|
||
return
|
||
release()
|
||
try:
|
||
import fcntl
|
||
except ImportError:
|
||
logger.warning("这个平台没有 fcntl,无法确认数据目录只被一个进程使用:SQLite 只支持单进程部署")
|
||
return
|
||
path.parent.mkdir(parents=True, exist_ok=True)
|
||
fd = os.open(path, os.O_RDWR | os.O_CREAT, 0o600)
|
||
try:
|
||
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||
except OSError:
|
||
os.close(fd)
|
||
holder = ""
|
||
try:
|
||
holder = path.read_text(encoding="utf-8").strip()
|
||
except OSError:
|
||
pass
|
||
raise SingleProcessOnly(SINGLE_PROCESS.format(
|
||
dir=settings.data_dir, hint=f"占着它的是 {holder}" if holder else "另一个进程占着它")) from None
|
||
try:
|
||
os.ftruncate(fd, 0)
|
||
os.write(fd, f"pid {os.getpid()}".encode())
|
||
except OSError:
|
||
pass
|
||
_held = (path, fd)
|
||
|
||
|
||
def release() -> None:
|
||
global _held
|
||
if _held is not None:
|
||
try:
|
||
os.close(_held[1])
|
||
except OSError:
|
||
pass
|
||
_held = None
|
||
|
||
|
||
__all__ = ["LOCK_FILE", "SINGLE_PROCESS", "SingleProcessOnly", "acquire", "release"]
|