1/**
2 * Push admission (src/Git/RepoObject.ts `pushPermitsFor`): the permit a
3 * push takes from the isolate-wide memory gate must reflect what it can
4 * hold at once — a spilled push never its body, an in-memory push never
5 * the caches' ceilings (DESIGN §21.2, §22.5).
6 */
7import {
8 BACKPRESSURE_BYTES,
9 RETAIN_BYTES,
10} from "@/Git/Store/StreamingSource.ts";
11import {
12 MAX_PACK_BYTES,
13 PUSH_MEMORY_BUDGET_MB,
14 pushPermitsFor,
15 STAGE_BATCH_BYTES,
16} from "@/Git/RepoObject.ts";
17import { describe, expect, test } from "alchemy-test";
18
19const MiB = 1024 * 1024;
20const spilled = Math.ceil(
21 (RETAIN_BYTES + BACKPRESSURE_BYTES + STAGE_BATCH_BYTES) / MiB,
22);
23
24describe("pushPermitsFor", { tags: ["unit", "local"] }, () => {
25 test("a spilled push is charged its working set, never its body", () => {
26 expect(pushPermitsFor(MAX_PACK_BYTES + 1)).toBe(spilled);
27 expect(pushPermitsFor(400 * MiB)).toBe(spilled);
28 expect(pushPermitsFor(undefined)).toBe(spilled);
29 expect(pushPermitsFor(Number.NaN)).toBe(spilled);
30 expect(spilled).toBeLessThan(PUSH_MEMORY_BUDGET_MB);
31 });
32
33 test("an in-memory push is charged by its size; tiny pushes stay cheap", () => {
34 expect(pushPermitsFor(0)).toBe(1);
35 expect(pushPermitsFor(1024)).toBe(3);
36 expect(pushPermitsFor(MAX_PACK_BYTES)).toBe(2 * (MAX_PACK_BYTES / MiB) + 1);
37 // Several small pushes fit alongside one large one.
38 expect(spilled + 4 * pushPermitsFor(1024)).toBeLessThanOrEqual(
39 PUSH_MEMORY_BUDGET_MB,
40 );
41 });
42});