minimal 备份增加 freeze 副本路径
Project CI / AI game creator shell Rust smoke (push) Successful in 2m35s
Project CI / AI game creator shell Rust crates (push) Successful in 1m14s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m4s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 7m28s
Project CI / Backend tests (push) Successful in 7m25s
Project CI / Frontend tests (push) Successful in 7m18s
Project CI / Repository checks (push) Successful in 5m51s
Project CI / Native shell tests (push) Successful in 9m16s
Project CI / AI game creator shell web tests (push) Successful in 3m41s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m35s
Project CI / AI game creator shell Rust crates (push) Successful in 1m14s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m4s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 7m28s
Project CI / Backend tests (push) Successful in 7m25s
Project CI / Frontend tests (push) Successful in 7m18s
Project CI / Repository checks (push) Successful in 5m51s
Project CI / Native shell tests (push) Successful in 9m16s
Project CI / AI game creator shell web tests (push) Successful in 3m41s
- database-backup-to-oss.mjs 新增 --freeze-dir:minimal 模式先把选中子集 rsync 成冻结副本(保持原始相对路径)再从副本上传,避免热备时最新 commitlog 持续追加触发 stat 漂移失败 - files-minimal systemd 模板与发布前 minimal 备份默认传入 freeze 目录 - 生产运维门禁同步 freeze 口径断言
This commit is contained in:
@@ -1028,13 +1028,13 @@ const checks = [
|
||||
},
|
||||
{
|
||||
file: 'deploy/systemd/genarrative-database-backup-files-minimal.conf',
|
||||
includes: '--mode full --minimal --retain-snapshots 2',
|
||||
includes: '--mode full --minimal --retain-snapshots 2 --freeze-dir',
|
||||
reason:
|
||||
'files-minimal 定时备份必须使用 minimal 口径并保留上游默认的最近 2 份 snapshot。',
|
||||
},
|
||||
{
|
||||
file: 'scripts/deploy/production-stdb-publish.sh',
|
||||
includes: '--minimal --retain-snapshots',
|
||||
includes: '--freeze-dir',
|
||||
reason:
|
||||
'发布前备份默认使用 minimal 热备,避免 40G 级冷备空间门槛与停服。',
|
||||
},
|
||||
|
||||
@@ -107,6 +107,7 @@ function usage() {
|
||||
将 SpacetimeDB 数据目录以 .tar.gz 或逐文件 catalog 形式上传到阿里云 OSS 指定 bucket。
|
||||
默认 full 模式保持原有全量冷备行为;history 模式只归档已被最新 snapshot 覆盖的历史 commitlog 与旧 snapshot。
|
||||
--storage-format files 不打包:按原相对路径建立 catalog,文件内容以 SHA-256 不可变对象上传;重复运行只上传新增或变化内容。
|
||||
--freeze-dir <path> 仅用于 --minimal:先把选中子集 rsync 成冻结副本再上传,避免热备时最新 commitlog 追加导致 stat 漂移;副本内保持原始相对路径。
|
||||
--minimal 只备份上游 retention 语义下仍需要的部分(最近 --retain-snapshots 份 snapshot + 其后 commitlog 段 + 身份/配置/控制库/模块字节);只支持 files 格式,且不停止任何服务。
|
||||
archive history 必须有已验真的 full baseline state;files history 必须复用同一 work-dir 中已发布的 full catalog state。
|
||||
--defer-upload 只生成本地冷备份和 manifest,不上传;后续用 --upload-archive 异步上传。
|
||||
@@ -204,6 +205,7 @@ function parseArgs(argv) {
|
||||
minFreeBytes: '',
|
||||
mode: 'full',
|
||||
minimal: false,
|
||||
freezeDir: '',
|
||||
retainSnapshots: DEFAULT_RETAIN_SNAPSHOTS,
|
||||
baselineState: '',
|
||||
baselineManifest: '',
|
||||
@@ -301,6 +303,9 @@ function parseArgs(argv) {
|
||||
case '--retain-snapshots':
|
||||
options.retainSnapshots = readValue();
|
||||
break;
|
||||
case '--freeze-dir':
|
||||
options.freezeDir = readValue();
|
||||
break;
|
||||
case '--baseline-state':
|
||||
options.baselineState = readValue();
|
||||
break;
|
||||
@@ -4387,7 +4392,7 @@ async function main() {
|
||||
const env = loadEffectiveEnv(args.envFiles);
|
||||
const isProductionLike =
|
||||
existsSync(DEFAULT_PRODUCTION_DATA_DIR) && process.platform !== 'win32';
|
||||
const dataDir = resolvePath(
|
||||
let dataDir = resolvePath(
|
||||
firstNonEmpty(
|
||||
args.dataDir,
|
||||
env.GENARRATIVE_DATABASE_BACKUP_DATA_DIR,
|
||||
@@ -4466,6 +4471,51 @@ async function main() {
|
||||
throw new Error('--minimal 只支持 --mode full:minimal 自身就是自包含快照。');
|
||||
}
|
||||
|
||||
if (args.minimal && args.freezeDir) {
|
||||
if (storageFormat !== 'files') {
|
||||
throw new Error('--freeze-dir 只支持 --minimal --storage-format files。');
|
||||
}
|
||||
const freezeDir = resolvePath(args.freezeDir);
|
||||
const plan = discoverMinimalPlan({
|
||||
dataDir,
|
||||
retainSnapshots: args.retainSnapshots,
|
||||
});
|
||||
console.log(
|
||||
`[database-backup] minimal freeze: candidates=${plan.candidates.length}, size=${plan.totalSizeBytes}, freezeDir=${freezeDir}`,
|
||||
);
|
||||
if (!args.dryRun) {
|
||||
mkdirSync(freezeDir, { recursive: true });
|
||||
const listPath = join(workDir, 'minimal-freeze-files.txt');
|
||||
mkdirSync(workDir, { recursive: true });
|
||||
writeFileSync(
|
||||
listPath,
|
||||
`${plan.candidates.map((item) => item.path).join('\n')}\n`,
|
||||
'utf8',
|
||||
);
|
||||
const result = spawnSync(
|
||||
'rsync',
|
||||
[
|
||||
'-a',
|
||||
'-r',
|
||||
'--delete',
|
||||
`--files-from=${listPath}`,
|
||||
`${dataDir}/`,
|
||||
`${freezeDir}/`,
|
||||
],
|
||||
{ stdio: 'inherit' },
|
||||
);
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`minimal freeze 拷贝失败: rsync exit=${result.status ?? '<signal>'}`,
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`[database-backup] minimal freeze 完成,后续从冻结副本上传: ${freezeDir}`,
|
||||
);
|
||||
}
|
||||
dataDir = freezeDir;
|
||||
}
|
||||
|
||||
if (args.checkSpaceOnly) {
|
||||
assertSufficientWorkDirSpace({
|
||||
dataDir,
|
||||
|
||||
@@ -856,7 +856,12 @@ case "${BACKUP_MODE}" in
|
||||
)
|
||||
if [[ "${BACKUP_MINIMAL}" == "1" ]]; then
|
||||
echo "[production-stdb-publish] publish 前执行 minimal 热备(最近 ${BACKUP_RETAIN_SNAPSHOTS} 份 snapshot + 其后 commitlog),不停服务"
|
||||
SYNC_BACKUP_ARGS+=(--mode full --minimal --retain-snapshots "${BACKUP_RETAIN_SNAPSHOTS}")
|
||||
SYNC_BACKUP_ARGS+=(
|
||||
--mode full
|
||||
--minimal
|
||||
--retain-snapshots "${BACKUP_RETAIN_SNAPSHOTS}"
|
||||
--freeze-dir "${GENARRATIVE_STDB_PUBLISH_BACKUP_FREEZE_DIR:-/var/lib/genarrative/database-backups/publish-minimal-freeze}"
|
||||
)
|
||||
else
|
||||
echo "[production-stdb-publish] publish 前同步执行 OSS 冷备份(storage-format=${BACKUP_STORAGE_FORMAT}),失败会阻断发布"
|
||||
SYNC_BACKUP_ARGS+=(--stop-service spacetimedb.service)
|
||||
|
||||
Reference in New Issue
Block a user