0.1.11: a provisioner brakes a withdrawal larger than its bound (hq to-be 45 Phase 2)

Issue 241 withdrew seven consumers in one pass; 0.1.10 refuses a file it
cannot read, and a file read whole that names nobody still withdrew
everybody. A pass that would withdraw more than withdrawAtOnce consumers
(1), or more than withdrawFraction (0.5) of those held, now withdraws
nothing and says so; while the mesh goes on not asking, one is let go every
releaseEveryMs (an hour). A consumer asked for again is kept.
This commit is contained in:
jochen
2026-10-06 12:15:45 +02:00
parent 1e45f1c95a
commit 8d70726038
4 changed files with 119 additions and 8 deletions
+55 -3
View File
@@ -185,7 +185,7 @@ test("provisioner removes nothing while its contributions file cannot be used (n
removed.push(p.as);
},
},
{ receives, everyMs: 20 },
{ receives, everyMs: 20, releaseEveryMs: 300 },
);
await waitFor(() => created.length === 2, 2000);
@@ -203,13 +203,65 @@ test("provisioner removes nothing while its contributions file cannot be used (n
await new Promise((r) => setTimeout(r, 100));
}
// The file read and saying so still withdraws: an explicit empty list removes both.
// The file read and saying so still withdraws — within the brake (to-be 45 Phase 2): two at once is
// over its bound, so nothing goes at first, then one at a time as the brake lets them go.
await writeFile(receives, doc([]));
await waitFor(() => removed.length === 2, 2000);
await new Promise((r) => setTimeout(r, 150));
assert.deepEqual(removed, [], "two consumers were withdrawn at once");
await waitFor(() => removed.length === 2, 3000);
assert.deepEqual([...removed].sort(), ["mesh_anchor_forge", "mesh_anchor_mail"]);
stop();
});
test("provisioner brakes a withdrawal of more than one consumer, and keeps one asked for again (to-be 45 Phase 2)", async () => {
const dir = await mkdtemp(join(tmpdir(), "prov-"));
const created: string[] = [];
const removed: string[] = [];
const receives = join(dir, "postgres-database.json");
const doc = (given: unknown[]): string =>
JSON.stringify({ contributions: 1, requirement: "postgres-database", given });
const seven: unknown[] = [];
for (let i = 1; i <= 7; i++) {
await writeFile(join(dir, `c${i}.secret`), `pw-${i}\n`);
seven.push({ from: `c${i}`, node: "anchor", as: `consumer${i}`, secret: join(dir, `c${i}.secret`) });
}
await writeFile(receives, doc(seven));
const said: string[] = [];
const was = console.error;
console.error = (...args: unknown[]) => said.push(args.join(" "));
const stop = runProvisioner(
"postgres-database",
{
async create(p) {
created.push(p.as);
},
async remove(p) {
removed.push(p.as);
},
},
{ receives, everyMs: 10, releaseEveryMs: 60_000 },
);
try {
await waitFor(() => created.length === 7, 2000);
// A file read whole that names nobody — the shape of 241 its first fix does not catch.
await writeFile(receives, doc([]));
await new Promise((r) => setTimeout(r, 200));
assert.deepEqual(removed, [], "a withdrawal of seven at once was not braked");
assert.ok(said.some((l) => l.includes("WITHDRAWAL BRAKED")), "the brake was not said");
// The file is right again: nothing was withdrawn, and nothing will be.
await writeFile(receives, doc(seven));
await new Promise((r) => setTimeout(r, 100));
assert.deepEqual(removed, []);
// One of seven goes as asked: within the bound.
await writeFile(receives, doc(seven.slice(1)));
await waitFor(() => removed.length === 1, 2000);
assert.deepEqual(removed, ["consumer1"]);
} finally {
stop();
console.error = was;
}
});
test("provisioner applies again what the backend no longer holds, and trusts memory without holds", async () => {
const dir = await mkdtemp(join(tmpdir(), "prov-holds-"));
await writeFile(join(dir, "webapp.secret"), "minted-pw\n");