mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 00:11:59 +00:00
复核 R-d4(升级前的 SQLite 备份只拷了应用库和断点库):填报的记录表 records/**/*.db 也用 SQLite 的备份接口拷进同一份备份,相对路径照旧,目录 0700、文件 0600;库在 PostgreSQL 时不做这份(记录表跟着整目录备份)。deploy/README 写清这份备份含什么、不含什么(上传原件和版本库、工件库、附件、知识库、沙箱工作区、.secret_key 要整目录备份),恢复步骤加上记录表
This commit is contained in:
+23
-7
@@ -10,8 +10,9 @@
|
||||
用的仍是 SQLite,只是让下一次 migrate-to-postgres 认得出数据卷里那半份是自己上次留下的。
|
||||
--container / --volume / --network 记下这个数据目录的内置库用的名字;第一次记时生成部署编号 id
|
||||
(deploy.sh 把它打在库的容器、数据卷、网络的标签上,按它认哪些是这个部署的)。--get 键 打印一项
|
||||
backup-sqlite 拷一份 agentlab.db、checkpoints.db 到 <数据目录>/backups/sqlite/<时间>/(SQLite 的备份接口,一致的一份),
|
||||
只留最近 --keep 份
|
||||
backup-sqlite 拷一份 agentlab.db、checkpoints.db 和填报的记录表 records/**/*.db 到 <数据目录>/backups/sqlite/<时间>/
|
||||
(SQLite 的备份接口,各自一致的一份,记录表保持 records/ 下的相对路径),只留最近 --keep 份。上传原件、
|
||||
上传表格的版本库、工件、附件、沙箱工作区不在里面:那些要整个数据目录一起备份
|
||||
backup-dir 建(或收紧)备份目录:从 backups/ 起每一层 0700(pg_dump 写进去之前用)
|
||||
prune 目录里按名字排序只留最近 --keep 个(pg_dump 的备份用)
|
||||
|
||||
@@ -172,18 +173,33 @@ def prune(folder: Path, keep: int) -> list[Path]:
|
||||
return gone
|
||||
|
||||
|
||||
def _sqlite_sources(data: Path) -> list[Path]:
|
||||
"""要备份的 SQLite 文件(相对数据目录):应用库、断点库,和填报的记录表(records/ 下,原地追加写,开着服务直接拷可能拷到
|
||||
写了一半的,所以也走备份接口)。"""
|
||||
found = [Path(name) for name in ("agentlab.db", "checkpoints.db") if (data / name).is_file()]
|
||||
records = data / "records"
|
||||
if records.is_dir():
|
||||
found += sorted(p.relative_to(data) for p in records.rglob("*.db") if p.is_file())
|
||||
return found
|
||||
|
||||
|
||||
def backup_sqlite(data: Path, *, keep: int) -> Path | None:
|
||||
"""agentlab.db、checkpoints.db 拷到 backups/sqlite/<时间>/。两个都没有(库在 PostgreSQL)交回 None。"""
|
||||
sources = [p for p in (data / "agentlab.db", data / "checkpoints.db") if p.is_file()]
|
||||
if not sources:
|
||||
"""agentlab.db、checkpoints.db、records/**/*.db 拷到 backups/sqlite/<时间>/(相对路径照旧)。应用库和断点库都没有(库在
|
||||
PostgreSQL)交回 None——记录表那时由整目录备份管。"""
|
||||
sources = _sqlite_sources(data)
|
||||
if not any(p.parent == Path(".") for p in sources):
|
||||
return None
|
||||
with _private_umask():
|
||||
root = backup_dir(data, "sqlite")
|
||||
target = root / _stamp()
|
||||
target.mkdir(exist_ok=True)
|
||||
target.chmod(BACKUP_DIR_MODE)
|
||||
for src in sources:
|
||||
_copy_sqlite(src, target / src.name)
|
||||
for rel in sources:
|
||||
dst = target / rel
|
||||
for folder in reversed(dst.relative_to(target).parents[:-1]):
|
||||
(target / folder).mkdir(exist_ok=True)
|
||||
(target / folder).chmod(BACKUP_DIR_MODE)
|
||||
_copy_sqlite(data / rel, dst)
|
||||
prune(root, keep)
|
||||
return target
|
||||
|
||||
|
||||
@@ -135,6 +135,38 @@ def test_backup_sqlite_copies_a_consistent_snapshot_including_the_wal(tmp_path,
|
||||
assert len(list(target.iterdir())) == 2 and not (target / "20200101T000000Z").exists()
|
||||
|
||||
|
||||
def test_backup_sqlite_also_copies_the_record_tables(tmp_path):
|
||||
"""复核 R-d4:填报的记录表 records/**/*.db(原地追加写的 SQLite)也用备份接口拷进同一份备份,相对路径照旧。"""
|
||||
sqlite3.connect(tmp_path / "agentlab.db").close()
|
||||
(tmp_path / "records" / "nested").mkdir(parents=True)
|
||||
rec = sqlite3.connect(tmp_path / "records" / "sales.v1.db")
|
||||
rec.execute("PRAGMA journal_mode=WAL")
|
||||
rec.execute("CREATE TABLE records (v TEXT)")
|
||||
rec.execute("INSERT INTO records VALUES ('还在 WAL 里的一行')")
|
||||
rec.commit()
|
||||
sqlite3.connect(tmp_path / "records" / "nested" / "visits.v2.db").close()
|
||||
(tmp_path / "records" / "notes.txt").write_text("不是库,不拷")
|
||||
try:
|
||||
target = ops.backup_sqlite(tmp_path, keep=5)
|
||||
finally:
|
||||
rec.close()
|
||||
copied = sorted(str(p.relative_to(target)) for p in target.rglob("*") if p.is_file())
|
||||
assert copied == ["agentlab.db", "records/nested/visits.v2.db", "records/sales.v1.db"]
|
||||
db = sqlite3.connect(target / "records" / "sales.v1.db")
|
||||
assert db.execute("SELECT v FROM records").fetchall() == [("还在 WAL 里的一行",)]
|
||||
db.close()
|
||||
for folder in (target / "records", target / "records" / "nested"):
|
||||
assert _mode(folder) == 0o700
|
||||
assert _mode(target / "records" / "sales.v1.db") == 0o600
|
||||
|
||||
|
||||
def test_record_tables_alone_are_left_to_the_whole_directory_backup(tmp_path):
|
||||
"""库在 PostgreSQL(没有 agentlab.db、checkpoints.db)时不做这份备份:记录表跟着整个数据目录备份。"""
|
||||
(tmp_path / "records").mkdir()
|
||||
sqlite3.connect(tmp_path / "records" / "sales.v1.db").close()
|
||||
assert ops.backup_sqlite(tmp_path, keep=5) is None
|
||||
|
||||
|
||||
def test_backup_dir_creates_and_tightens_every_level(tmp_path, capsys):
|
||||
(tmp_path / "backups").mkdir(mode=0o755)
|
||||
(tmp_path / "backups").chmod(0o755)
|
||||
|
||||
+5
-2
@@ -66,10 +66,12 @@ deploy.sh 依次:检查 docker 和架构 → 导入镜像(`docker load -i im
|
||||
另用 `pg_dump` 备份数据库。
|
||||
|
||||
`./deploy.sh upgrade` 换版本之前会自动备份一次库:SQLite 拷到数据目录的 `backups/sqlite/<时间>/`(SQLite 的备份接口,
|
||||
一致的一份),内置 PostgreSQL 用 `pg_dump` 存到 `backups/postgres/`,各只留最近 5 份(`agentlab.env` 里 `BACKUP_KEEP=` 改)。
|
||||
各自一致的一份:`agentlab.db`、`checkpoints.db`,和填报的记录表 `records/` 下的各个 `.db`,相对路径照旧),内置 PostgreSQL
|
||||
用 `pg_dump` 存到 `backups/postgres/`(整个库,含断点),各只留最近 5 份(`agentlab.env` 里 `BACKUP_KEEP=` 改)。
|
||||
备份里是整个库(含加密存着的密钥),所以 `backups/` 和下面每一层目录都是 0700、备份文件 0600,属主同数据目录(uid 10001):
|
||||
宿主机上要看、要拷走都得用 sudo(`sudo ls -l data/backups/sqlite/`、`sudo cp -a data/backups /别处/`)。
|
||||
这只是升级的保险,不代替下面的整目录备份:工件、上传文件和 `.secret_key` 不在里面。
|
||||
这只是升级的保险,不代替下面的整目录备份:上传的原件和上传表格的各版本库、工件库、表单附件、知识库、沙箱工作区、加密密钥
|
||||
`.secret_key` 都不在里面(用内置 PostgreSQL 时记录表也不在 `pg_dump` 里,它们是数据目录里的文件)。
|
||||
|
||||
用 SQLite 的部署要从 `backups/sqlite/` 恢复(以缺省的 `./data` 为例,`<时间>` 换成目录名):
|
||||
|
||||
@@ -77,6 +79,7 @@ deploy.sh 依次:检查 docker 和架构 → 导入镜像(`docker load -i im
|
||||
./deploy.sh stop
|
||||
sudo rm -f data/agentlab.db-wal data/agentlab.db-shm data/checkpoints.db-wal data/checkpoints.db-shm
|
||||
sudo cp -p data/backups/sqlite/<时间>/agentlab.db data/backups/sqlite/<时间>/checkpoints.db data/ # -p:属主仍是 10001
|
||||
sudo cp -rp data/backups/sqlite/<时间>/records/. data/records/ # 记录表(有的话),同样先删掉它们的 -wal、-shm
|
||||
./deploy.sh start
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user