Files
AgentLab/backend/app/cluster/guard.py
T
ThearchyHelios 2714890d0d 文档重新梳理:README 精简成介绍和索引,细节按主题拆进 docs/zh-CN 与 docs/en(17 篇一一对应),英文文档不含中文
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 通过。
2026-10-21 10:23:26 +08:00

87 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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"]