Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/restore-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,9 @@ async function countRowsInDumpOrThrow(dumpPath: string, signal?: AbortSignal): P
try {
const listFile = join(listDir, "table-data.list");
await writeFile(listFile, dataEntries.join("\n") + "\n");
// 建临时目录、写清单的这两步里也可能被叫停。abort 事件已经发过,后面挂的监听收不到,
// 不在这里再看一眼,起来的 pg_restore 就会把整份 dump 读完。这一眼和下面挂监听在同一拍里,中间没有空档
if (signal?.aborted) return null;
return await new Promise((resolve) => {
// 和 pgRestoreOutput 一样钉 C locale
const proc = spawn(bin, ["--data-only", `--use-list=${listFile}`, "--file=-", dumpPath], {
Expand Down
31 changes: 30 additions & 1 deletion test/drill-row-counts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { createHash } from "node:crypto";
import { readFileSync, mkdtempSync, rmSync } from "node:fs";
import { readFileSync, mkdtempSync, rmSync, mkdirSync, writeFileSync, readdirSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { drillDump, verifyRestored } from "../dist/drill.js";
Expand Down Expand Up @@ -118,6 +118,35 @@ test(
const stopped = countRowsInDump(dumpPath, stop.signal);
stop.abort();
assert.equal(await stopped, null);
// 在建临时目录到起 pg_restore 之间叫停:abort 事件发在挂监听之前,也不能再起 pg_restore。
// 时机靠真 AbortSignal 卡不准,用一个"临时目录一建出来就算已叫停"的 signal 把它钉死;
// 包一层 pg_restore 记下每次调用,看有没有起 --data-only 那一遍
const scratch = join(tmp, "scratch");
mkdirSync(scratch);
const calls = join(tmp, "pg_restore.calls");
const wrapper = join(tmp, "pg_restore-wrapper.sh");
writeFileSync(wrapper, `#!/bin/sh\necho "$@" >> '${calls}'\nexec '${pgRestore}' "$@"\n`, { mode: 0o755 });
const lateStop = {
get aborted() {
return readdirSync(scratch).some((name) => name.startsWith("backupdrill-rowcount-"));
},
addEventListener() {},
removeEventListener() {},
};
const savedEnv = { TMPDIR: process.env.TMPDIR, BACKUPDRILL_PG_RESTORE: process.env.BACKUPDRILL_PG_RESTORE };
process.env.TMPDIR = scratch;
process.env.BACKUPDRILL_PG_RESTORE = wrapper;
try {
assert.equal(await countRowsInDump(dumpPath, lateStop), null);
} finally {
for (const [name, value] of Object.entries(savedEnv)) {
if (value === undefined) delete process.env[name];
else process.env[name] = value;
}
}
const invoked = readFileSync(calls, "utf8");
assert.match(invoked, /--list/, "the wrapper was used");
assert.doesNotMatch(invoked, /--data-only/, "no pg_restore started after the caller stopped");
// 临时目录建不了:返回 null(演练判"数不出来"),不能抛出去变成没人接的 rejection
const savedTmp = process.env.TMPDIR;
process.env.TMPDIR = join(tmp, "no-such-dir");
Expand Down
Loading