| 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 | */ |
| 7 | import { |
| 8 | BACKPRESSURE_BYTES, |
| 9 | RETAIN_BYTES, |
| 10 | } from "@/Git/Store/StreamingSource.ts"; |
| 11 | import { |
| 12 | MAX_PACK_BYTES, |
| 13 | PUSH_MEMORY_BUDGET_MB, |
| 14 | pushPermitsFor, |
| 15 | STAGE_BATCH_BYTES, |
| 16 | } from "@/Git/RepoObject.ts"; |
| 17 | import { describe, expect, test } from "alchemy-test"; |
| 18 | |
| 19 | const MiB = 1024 * 1024; |
| 20 | const spilled = Math.ceil( |
| 21 | (RETAIN_BYTES + BACKPRESSURE_BYTES + STAGE_BATCH_BYTES) / MiB, |
| 22 | ); |
| 23 | |
| 24 | describe("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 | }); |