修复生产发布备份状态处理
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 3m53s
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 3m53s
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
备份锁冲突时写入失败状态 JSON 发布脚本安全解析异步备份状态并保留异常文件 生产发布临时目录统一使用 ~/data/tmp
This commit is contained in:
@@ -4517,6 +4517,21 @@ if (
|
||||
for (const line of describeError(error)) {
|
||||
console.error(`[database-backup] ${line}`);
|
||||
}
|
||||
// 即使初始化失败(例如已有进程持有锁),也保持 --result-file 可被机器读取。
|
||||
// 调用方可以检查错误载荷并跳过延后上传,避免对空的临时文件执行 JSON.parse。
|
||||
try {
|
||||
const parsed = parseArgs(process.argv.slice(2));
|
||||
if (parsed.resultFile) {
|
||||
atomicWriteJson(resolvePath(parsed.resultFile), {
|
||||
uploadStatus: 'failed',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
archivePath: '',
|
||||
manifestPath: '',
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// 参数解析或状态文件写入失败时,不能掩盖原始备份错误。
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -519,7 +519,9 @@ prepare_async_backup() {
|
||||
restart_service_args+=(--restart-service-after genarrative-api.service)
|
||||
fi
|
||||
|
||||
ASYNC_BACKUP_STATUS_FILE="$(mktemp /tmp/genarrative-stdb-backup-status.XXXXXX.json)"
|
||||
task_tmp_dir="${HOME}/data/tmp"
|
||||
mkdir -p "${task_tmp_dir}"
|
||||
ASYNC_BACKUP_STATUS_FILE="$(mktemp "${task_tmp_dir}/genarrative-stdb-backup-status.XXXXXX.json")"
|
||||
echo "[production-stdb-publish] publish 前生成本地冷备份,随后会异步上传 OSS"
|
||||
node -- "${ASYNC_BACKUP_SCRIPT}" \
|
||||
--env-file /etc/genarrative/api-server.env \
|
||||
@@ -536,14 +538,32 @@ start_async_backup_upload() {
|
||||
local node_binary=""
|
||||
local unit_name=""
|
||||
local unit_suffix=""
|
||||
local backup_paths=""
|
||||
|
||||
if [[ -z "${ASYNC_BACKUP_STATUS_FILE}" || ! -f "${ASYNC_BACKUP_STATUS_FILE}" ]]; then
|
||||
echo "[production-stdb-publish] 警告:未找到可上传的本地备份状态文件,跳过异步上传" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
ASYNC_BACKUP_ARCHIVE="$(node -e 'const fs=require("node:fs"); const p=process.argv[1]; const o=JSON.parse(fs.readFileSync(p,"utf8")); process.stdout.write(o.archivePath || "");' "${ASYNC_BACKUP_STATUS_FILE}")"
|
||||
ASYNC_BACKUP_MANIFEST="$(node -e 'const fs=require("node:fs"); const p=process.argv[1]; const o=JSON.parse(fs.readFileSync(p,"utf8")); process.stdout.write(o.manifestPath || "");' "${ASYNC_BACKUP_STATUS_FILE}")"
|
||||
# 备份进程可能因数据库锁冲突在写入状态文件前退出,或只留下空/截断文件。
|
||||
# 解析失败时保留状态文件并让调用方继续处理发布失败,不能再让 node JSON.parse
|
||||
# 的堆栈噪声覆盖原始备份错误。
|
||||
if ! backup_paths="$(node -e '
|
||||
const fs = require("node:fs");
|
||||
const p = process.argv[1];
|
||||
let o;
|
||||
try {
|
||||
o = JSON.parse(fs.readFileSync(p, "utf8"));
|
||||
} catch {
|
||||
process.exit(2);
|
||||
}
|
||||
process.stdout.write(`${o.archivePath || ""}\n${o.manifestPath || ""}`);
|
||||
' "${ASYNC_BACKUP_STATUS_FILE}" 2>/dev/null)"; then
|
||||
echo "[production-stdb-publish] 警告:备份状态文件为空或不是有效 JSON,跳过异步上传并保留状态文件: ${ASYNC_BACKUP_STATUS_FILE}" >&2
|
||||
return 1
|
||||
fi
|
||||
ASYNC_BACKUP_ARCHIVE="${backup_paths%%$'\n'*}"
|
||||
ASYNC_BACKUP_MANIFEST="${backup_paths#*$'\n'}"
|
||||
if [[ -z "${ASYNC_BACKUP_ARCHIVE}" || -z "${ASYNC_BACKUP_MANIFEST}" ]]; then
|
||||
echo "[production-stdb-publish] 警告:备份状态文件缺少 archivePath 或 manifestPath,跳过异步上传" >&2
|
||||
return 0
|
||||
@@ -727,7 +747,9 @@ if [[ -n "${RUN_AS_USER}" && "$(id -u)" -eq 0 ]]; then
|
||||
echo "[production-stdb-publish] 发布用户不存在: ${RUN_AS_USER}" >&2
|
||||
exit 1
|
||||
fi
|
||||
PUBLISH_TMP_DIR="$(mktemp -d /tmp/genarrative-stdb-publish.XXXXXX)"
|
||||
task_tmp_dir="${HOME}/data/tmp"
|
||||
mkdir -p "${task_tmp_dir}"
|
||||
PUBLISH_TMP_DIR="$(mktemp -d "${task_tmp_dir}/genarrative-stdb-publish.XXXXXX")"
|
||||
install -m 0644 "${SOURCE_DIR}/spacetime_module.wasm" "${PUBLISH_TMP_DIR}/spacetime_module.wasm"
|
||||
chown -R "${RUN_AS_USER}:${RUN_AS_USER}" "${PUBLISH_TMP_DIR}"
|
||||
PUBLISH_ARGS=(
|
||||
|
||||
Reference in New Issue
Block a user