backupdrill 2.1.7 → 2.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/drill.d.ts +6 -0
- package/dist/drill.js +53 -17
- package/package.json +1 -1
package/dist/drill.d.ts
CHANGED
|
@@ -26,6 +26,12 @@ export interface DrillReport {
|
|
|
26
26
|
connString: string;
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
+
export interface SandboxTuning {
|
|
30
|
+
settings: string[];
|
|
31
|
+
/** docker --shm-size 的值;并行建索引走动态共享内存,要跟 maintenance_work_mem 一起长 */
|
|
32
|
+
shmSize: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function sandboxTuning(totalMemBytes: number): SandboxTuning;
|
|
29
35
|
/** post-data 第二遍恢复的结果:用户对象失败 vs Supabase 托管对象的沙箱预期跳过。 */
|
|
30
36
|
interface PostDataResult {
|
|
31
37
|
supabaseSkipped: number;
|
package/dist/drill.js
CHANGED
|
@@ -43,21 +43,60 @@ function sandboxImage(manifest) {
|
|
|
43
43
|
* 触因:105 表 / 12 GB dump 的演练跑了 2 小时,COPY 与建索引都在等 fsync、写整页 WAL、
|
|
44
44
|
* 64 MB 的排序内存反复溢磁盘。关掉持久性保证不影响演练结论(我们验证的是"能不能恢复出来",
|
|
45
45
|
* 不是"断电后还在不在"),但 COPY / 建索引通常快 2-5 倍,临时盘也少吃一半。
|
|
46
|
-
* wal_level=minimal 要求 max_wal_senders=0
|
|
46
|
+
* wal_level=minimal 要求 max_wal_senders=0。
|
|
47
|
+
*
|
|
48
|
+
* 内存相关的两项按**Docker 引擎能用的内存**推导,不写死:同一份 CLI 既跑在 8 GB 的 worker 上,
|
|
49
|
+
* 也跑在用户自己的笔记本上。比例取 Postgres 官方的经验值(shared_buffers 1/4、
|
|
50
|
+
* maintenance_work_mem 1/8),再夹在 [下限, 上限] 之间——下限保证 2 GB 小机器不比调参前差,
|
|
51
|
+
* 上限是因为超过 2 GB 的 shared_buffers 对一次性恢复没有边际收益,白占主机内存。
|
|
52
|
+
*
|
|
53
|
+
* 为什么问 Docker 而不是 os.totalmem():Mac / Windows 上的 Docker Desktop 跑在一台内存独立
|
|
54
|
+
* 配置的虚拟机里,宿主机 32 GB、虚拟机可能只有 2 GB;按宿主机算会把沙箱 Postgres 直接 OOM,
|
|
55
|
+
* 一份好备份被误判成演练失败(交叉审查 2026-09-12 用 1 GiB 限额复现)。`docker info` 的
|
|
56
|
+
* MemTotal 在 Linux 上就是宿主机内存,在 Docker Desktop 上是虚拟机内存,两边都是真正的预算。
|
|
47
57
|
*/
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
const MIB = 1024 * 1024;
|
|
59
|
+
function clampMib(bytes, minMib, maxMib) {
|
|
60
|
+
return Math.min(maxMib, Math.max(minMib, Math.floor(bytes / MIB)));
|
|
61
|
+
}
|
|
62
|
+
/** Docker 引擎可用内存(字节);问不到就返回 0,让 sandboxTuning 落到下限值,宁慢勿炸。 */
|
|
63
|
+
async function dockerMemoryBudget() {
|
|
64
|
+
try {
|
|
65
|
+
// 探测是可选的,绝不能卡住演练:守护进程假死时 5 秒后放弃,落到下限值继续跑
|
|
66
|
+
const { stdout } = await execFileAsync("docker", ["info", "--format", "{{.MemTotal}}"], {
|
|
67
|
+
timeout: 5_000,
|
|
68
|
+
});
|
|
69
|
+
const bytes = Number.parseInt(stdout.trim(), 10);
|
|
70
|
+
return Number.isFinite(bytes) && bytes > 0 ? bytes : 0;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function sandboxTuning(totalMemBytes) {
|
|
77
|
+
const sharedBuffersMib = clampMib(totalMemBytes / 4, 256, 2048);
|
|
78
|
+
const maintenanceWorkMemMib = clampMib(totalMemBytes / 8, 256, 1024);
|
|
79
|
+
return {
|
|
80
|
+
settings: [
|
|
81
|
+
"fsync=off",
|
|
82
|
+
"synchronous_commit=off",
|
|
83
|
+
"full_page_writes=off",
|
|
84
|
+
"wal_level=minimal",
|
|
85
|
+
"max_wal_senders=0",
|
|
86
|
+
`maintenance_work_mem=${maintenanceWorkMemMib}MB`,
|
|
87
|
+
"max_wal_size=4GB",
|
|
88
|
+
"checkpoint_timeout=30min",
|
|
89
|
+
`shared_buffers=${sharedBuffersMib}MB`,
|
|
90
|
+
],
|
|
91
|
+
// Docker 默认 /dev/shm 只有 64 MB;并行建索引(pgvector 的 HNSW 尤其)走动态共享内存,
|
|
92
|
+
// 64 MB 会撞 "No space left on device" 把好备份误判失败(交叉审查 2026-09-12 复现)。
|
|
93
|
+
// 给 maintenance_work_mem 的两倍、至少 1 GB;tmpfs 只按实际用量占内存,给大不吃亏。
|
|
94
|
+
shmSize: `${Math.max(1024, maintenanceWorkMemMib * 2)}m`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
59
97
|
async function startEphemeralPostgres(image) {
|
|
60
98
|
log.step(`Starting ephemeral ${image}…`);
|
|
99
|
+
const tuning = sandboxTuning(await dockerMemoryBudget());
|
|
61
100
|
const { stdout } = await execFileAsync("docker", [
|
|
62
101
|
"run",
|
|
63
102
|
"-d",
|
|
@@ -68,13 +107,10 @@ async function startEphemeralPostgres(image) {
|
|
|
68
107
|
"POSTGRES_DB=postgres",
|
|
69
108
|
"-p",
|
|
70
109
|
"127.0.0.1:0:5432", // 随机主机端口,避免撞端口
|
|
71
|
-
|
|
72
|
-
// 尤其)走动态共享内存,64 MB 会撞 "No space left on device" 把好备份误判失败(交叉审查
|
|
73
|
-
// 2026-09-12 复现)。pgvector 官方 docker 说明就要求调大 shm。
|
|
74
|
-
"--shm-size=1g",
|
|
110
|
+
`--shm-size=${tuning.shmSize}`,
|
|
75
111
|
image,
|
|
76
112
|
"postgres",
|
|
77
|
-
...
|
|
113
|
+
...tuning.settings.flatMap((kv) => ["-c", kv]),
|
|
78
114
|
]);
|
|
79
115
|
const containerId = stdout.trim();
|
|
80
116
|
// postgres 镜像把数据目录声明成 VOLUME → 每次 run 都建一个匿名卷。生产实测 `--rm` **没有**把它
|
package/package.json
CHANGED