Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
767545f4ab | ||
|
|
d95c49e548 | ||
|
|
7f9bf5eb90 | ||
|
|
65b0de4694 | ||
|
|
4dc9e9e23c | ||
|
|
b5fb5b1738 | ||
|
|
9f6fa1990f | ||
|
|
8904477f75 | ||
|
|
36d7cdd5db | ||
|
|
22ee2db479 |
@@ -17,8 +17,9 @@ pre-1.0
|
||||
|
||||
# Next Step
|
||||
|
||||
None: no implementation work is open. The cache design,
|
||||
https://git.eeqj.de/sneak/quak/issues/36, waits on sneak's review.
|
||||
None: no implementation work is open. The check of quak against a live Ente
|
||||
account, https://git.eeqj.de/sneak/quak/issues/187, needs an account only sneak
|
||||
has, so it waits on him.
|
||||
|
||||
Tagging and releases are decided by sneak alone, and happen only when he
|
||||
declares one.
|
||||
@@ -34,6 +35,10 @@ declares one.
|
||||
count those in flight. Each worker checks the backup's lock before it takes
|
||||
the next file, so once the lock is lost no further original starts.
|
||||
|
||||
- 2026-10-14: Next Step said the cache design, issue 36, waits on sneak's
|
||||
review, though that issue closed on 2026-10-03 (issue 194). It now names the
|
||||
issues open on the tracker.
|
||||
|
||||
- 2026-10-14: A backup whose lock is lost no longer dies with a stack trace
|
||||
(issue 184). `lockBackupDirectory` gives proper-lockfile an `onCompromised`
|
||||
that records why the lock was lost instead of throwing from its timer, and
|
||||
|
||||
@@ -397,19 +397,33 @@ describe("lib.backup()", () => {
|
||||
});
|
||||
|
||||
// A throw from `onProgress` fails the backup: `lib.backup()` rejects with
|
||||
// it. It rejects only once it has finished with every original it was
|
||||
// working on, so nothing is written into the backup after it has ended
|
||||
// and removed its lock. Here, with a content pool of eight, all eight
|
||||
// downloads wait at once. Photo 103's is let go first and answered 404,
|
||||
// and the line reporting that failure throws while the other seven wait.
|
||||
it("when onProgress throws, rejects only once the originals it was working on are in place", async () => {
|
||||
// it. From then on the backup starts no further original. It finishes the
|
||||
// ones it is working on, and rejects only once they are in place, so
|
||||
// nothing is written into the backup after it has ended and removed its
|
||||
// lock. Here the first five downloads wait. Photo 103's is let go first
|
||||
// and answered 404, and the line reporting that failure throws while the
|
||||
// other four wait. Only those five downloads are held: a backup that went
|
||||
// on would store the other three photos.
|
||||
it("when onProgress throws, starts no further original, and rejects once the originals it was working on are in place", async () => {
|
||||
const dir = mkdtempSync(join(root, "backup-"));
|
||||
const holding = holdingServer(103);
|
||||
const lib = await openLibrary(
|
||||
dir,
|
||||
holding.fetch,
|
||||
new RequestPools({ contentConcurrency: 8 }),
|
||||
);
|
||||
const downloads: number[] = [];
|
||||
const held: { id: number; release: () => void }[] = [];
|
||||
const fetch: typeof globalThis.fetch = async (input, init) => {
|
||||
const id = downloadOf(input);
|
||||
if (id !== undefined) {
|
||||
downloads.push(id);
|
||||
if (downloads.length <= 5) {
|
||||
await new Promise<void>((release) =>
|
||||
held.push({ id, release }),
|
||||
);
|
||||
}
|
||||
if (id === 103) {
|
||||
return new Response("not found", { status: 404 });
|
||||
}
|
||||
}
|
||||
return server(input, init);
|
||||
};
|
||||
const lib = await openLibrary(dir, fetch);
|
||||
const lines: string[] = [];
|
||||
try {
|
||||
const backup = lib.backup({
|
||||
@@ -432,12 +446,12 @@ describe("lib.backup()", () => {
|
||||
return err;
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(holding.held).toHaveLength(8), {
|
||||
await vi.waitFor(() => expect(held).toHaveLength(5), {
|
||||
interval: 5,
|
||||
timeout: 5000,
|
||||
});
|
||||
const at103 = holding.held.findIndex((h) => h.id === 103);
|
||||
holding.held.splice(at103, 1)[0]!.release();
|
||||
const at103 = held.findIndex((h) => h.id === 103);
|
||||
held.splice(at103, 1)[0]!.release();
|
||||
await vi.waitFor(
|
||||
() =>
|
||||
expect(
|
||||
@@ -445,14 +459,13 @@ describe("lib.backup()", () => {
|
||||
).toBe(true),
|
||||
{ interval: 5, timeout: 5000 },
|
||||
);
|
||||
for (const download of holding.held) download.release();
|
||||
for (const download of held) download.release();
|
||||
|
||||
expect(await rejection).toEqual(
|
||||
new Error("the progress output is closed"),
|
||||
);
|
||||
expect(storedWhenRejected).toEqual(
|
||||
PHOTO_IDS.filter((id) => id !== 103),
|
||||
);
|
||||
expect(downloads.sort()).toEqual([101, 102, 103, 104, 105]);
|
||||
expect(storedWhenRejected).toEqual([101, 102, 104, 105]);
|
||||
expect(lockedWhenRejected).toBe(false);
|
||||
} finally {
|
||||
await lib.close();
|
||||
|
||||
@@ -705,9 +705,10 @@ describe("a backup whose lock is lost", () => {
|
||||
};
|
||||
|
||||
// The backup downloads up to five originals at once, so all three are
|
||||
// under way when the lock is lost, and beach.jpg is stored. `quak backup`
|
||||
// prints why as one line, with no stack trace, and exits 1.
|
||||
it("stops quak backup before its next file when backup.lock is removed, and exits 1", async () => {
|
||||
// under way when the lock is lost, and beach.jpg is stored. The backup
|
||||
// then stops before it fetches ML data. `quak backup` prints why as one
|
||||
// line, with no stack trace, and exits 1.
|
||||
it("stops quak backup before it fetches ML data when backup.lock is removed, and exits 1", async () => {
|
||||
const result = backup(dir, {}, fetch);
|
||||
await heldRequested;
|
||||
rmSync(lockPath, { recursive: true });
|
||||
@@ -722,7 +723,6 @@ describe("a backup whose lock is lost", () => {
|
||||
"Fetching original beach.jpg (100)...\n" +
|
||||
"Fetching original IMG_0002.HEIC (300)...\n" +
|
||||
"Fetching original toast.mp4 (200)...\n" +
|
||||
"Fetching ML data...\n" +
|
||||
`quak: lost the lock on ${dir}: ENOENT: no such file or directory, stat '${lockPath}'\n`,
|
||||
);
|
||||
expect(downloads.sort()).toEqual([100, 200, 300]);
|
||||
|
||||
Reference in New Issue
Block a user