Skip to content
File

Blob: test/pack-indexer.resolve.ref.worker.test.ts

typescript847 lines
1import { describe, expect, it } from "vitest";
2import { env } from "cloudflare:workers";
3import { buildPack, buildAppendOnlyDelta, buildCopyPrefixDelta } from "./util/git-pack";
4import { createTestCacheContext } from "./util/pack-first";
5import { uniqueRepoId } from "./util/test-helpers";
6import {
7 makeActiveCatalogRow,
8 makeCountSubrequest,
9 makeLimiter,
10 makeTracingLimiter,
11 packIndexerLog as log,
12} from "./util/pack-indexer.helpers";
13 
14import { scanPack, resolveDeltasAndWriteIdx } from "@/worker/git/pack/indexer";
15import { computeOid, encodeGitObject } from "@/worker/git/core/objects";
16import { bytesToHex } from "@/worker/common/hex";
17import { findOidIndex } from "@/worker/git/object-store/idxView";
18import { packIndexKey } from "@/worker/keys";
19 
20describe("resolveDeltasAndWriteIdx REF_DELTA", () => {
21 it("resolves REF_DELTA with external base from the provided active catalog snapshot", async () => {
22 const baseBlobPayload = new TextEncoder().encode("external base content\n");
23 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
24 const basePack = await buildPack([{ type: "blob", payload: baseBlobPayload }]);
25 
26 const basePackKey = "test/ref-delta-base.pack";
27 await env.REPO_BUCKET.put(basePackKey, basePack);
28 const baseHead = await env.REPO_BUCKET.head(basePackKey);
29 
30 const baseScan = await scanPack({
31 env,
32 packKey: basePackKey,
33 packSize: baseHead!.size,
34 limiter: makeLimiter(),
35 countSubrequest: () => {},
36 log,
37 });
38 const repoId = uniqueRepoId();
39 const baseResolve = await resolveDeltasAndWriteIdx({
40 env,
41 packKey: basePackKey,
42 packSize: baseHead!.size,
43 limiter: makeLimiter(),
44 countSubrequest: () => {},
45 log,
46 scanResult: baseScan,
47 repoId,
48 });
49 
50 const suffix = new TextEncoder().encode("extra data\n");
51 const delta = buildAppendOnlyDelta(baseBlobPayload, suffix);
52 const thinPack = await buildPack([{ type: "ref-delta", baseOid: baseBlob.oid, delta }]);
53 
54 const thinPackKey = "test/ref-delta-thin.pack";
55 await env.REPO_BUCKET.put(thinPackKey, thinPack);
56 const thinHead = await env.REPO_BUCKET.head(thinPackKey);
57 
58 const thinScan = await scanPack({
59 env,
60 packKey: thinPackKey,
61 packSize: thinHead!.size,
62 limiter: makeLimiter(),
63 countSubrequest: () => {},
64 log,
65 });
66 
67 const cacheCtx = createTestCacheContext("http://localhost/test");
68 const thinResolve = await resolveDeltasAndWriteIdx({
69 env,
70 packKey: thinPackKey,
71 packSize: thinHead!.size,
72 limiter: makeLimiter(),
73 countSubrequest: () => {},
74 log,
75 scanResult: thinScan,
76 activeCatalog: [
77 makeActiveCatalogRow({
78 packKey: basePackKey,
79 packBytes: baseHead!.size,
80 objectCount: baseScan.objectCount,
81 idxBytes: baseResolve.idxBytes,
82 }),
83 ],
84 cacheCtx,
85 repoId,
86 });
87 
88 expect(thinScan.table.resolved[0]).toBe(1);
89 expect(thinResolve.objectCount).toBe(1);
90 
91 const expectedPayload = new Uint8Array(baseBlobPayload.length + suffix.length);
92 expectedPayload.set(baseBlobPayload, 0);
93 expectedPayload.set(suffix, baseBlobPayload.length);
94 const expectedOid = await computeOid("blob", expectedPayload);
95 expect(bytesToHex(thinScan.table.oids.subarray(0, 20))).toBe(expectedOid);
96 });
97 
98 it("rejects a thin REF_DELTA when the external base is missing", async () => {
99 const fakeBaseOid = "ab".repeat(20);
100 const delta = buildAppendOnlyDelta(
101 new TextEncoder().encode("base\n"),
102 new TextEncoder().encode("next\n")
103 );
104 const thinPack = await buildPack([{ type: "ref-delta", baseOid: fakeBaseOid, delta }]);
105 
106 const packKey = "test/ref-delta-missing-base.pack";
107 await env.REPO_BUCKET.put(packKey, thinPack);
108 const head = await env.REPO_BUCKET.head(packKey);
109 
110 const scanResult = await scanPack({
111 env,
112 packKey,
113 packSize: head!.size,
114 limiter: makeLimiter(),
115 countSubrequest: () => {},
116 log,
117 });
118 
119 const repoId = uniqueRepoId();
120 const cacheCtx = createTestCacheContext("http://localhost/test");
121 await expect(
122 resolveDeltasAndWriteIdx({
123 env,
124 packKey,
125 packSize: head!.size,
126 limiter: makeLimiter(),
127 countSubrequest: () => {},
128 log,
129 scanResult,
130 repoId,
131 cacheCtx,
132 })
133 ).rejects.toThrow(/could not be resolved/);
134 
135 const idxObj = await env.REPO_BUCKET.get(packIndexKey(packKey));
136 expect(idxObj).toBeNull();
137 });
138 
139 it("prefers the explicit active catalog snapshot over stale memoized pack-catalog state", async () => {
140 const baseBlobPayload = new TextEncoder().encode("external base content\n");
141 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
142 const basePack = await buildPack([{ type: "blob", payload: baseBlobPayload }]);
143 
144 const basePackKey = "test/ref-delta-stale-memo-base.pack";
145 await env.REPO_BUCKET.put(basePackKey, basePack);
146 const baseHead = await env.REPO_BUCKET.head(basePackKey);
147 
148 const baseScan = await scanPack({
149 env,
150 packKey: basePackKey,
151 packSize: baseHead!.size,
152 limiter: makeLimiter(),
153 countSubrequest: () => {},
154 log,
155 });
156 const repoId = uniqueRepoId();
157 const baseResolve = await resolveDeltasAndWriteIdx({
158 env,
159 packKey: basePackKey,
160 packSize: baseHead!.size,
161 limiter: makeLimiter(),
162 countSubrequest: () => {},
163 log,
164 scanResult: baseScan,
165 repoId,
166 });
167 
168 const suffix = new TextEncoder().encode("active catalog wins\n");
169 const thinPack = await buildPack([
170 {
171 type: "ref-delta",
172 baseOid: baseBlob.oid,
173 delta: buildAppendOnlyDelta(baseBlobPayload, suffix),
174 },
175 ]);
176 
177 const thinPackKey = "test/ref-delta-stale-memo-thin.pack";
178 await env.REPO_BUCKET.put(thinPackKey, thinPack);
179 const thinHead = await env.REPO_BUCKET.head(thinPackKey);
180 
181 const thinScan = await scanPack({
182 env,
183 packKey: thinPackKey,
184 packSize: thinHead!.size,
185 limiter: makeLimiter(),
186 countSubrequest: () => {},
187 log,
188 });
189 
190 const cacheCtx = createTestCacheContext("http://localhost/test");
191 cacheCtx.memo = {
192 ...(cacheCtx.memo || {}),
193 packCatalog: [],
194 };
195 
196 const thinResolve = await resolveDeltasAndWriteIdx({
197 env,
198 packKey: thinPackKey,
199 packSize: thinHead!.size,
200 limiter: makeLimiter(),
201 countSubrequest: () => {},
202 log,
203 scanResult: thinScan,
204 activeCatalog: [
205 makeActiveCatalogRow({
206 packKey: basePackKey,
207 packBytes: baseHead!.size,
208 objectCount: baseScan.objectCount,
209 idxBytes: baseResolve.idxBytes,
210 }),
211 ],
212 cacheCtx,
213 repoId,
214 });
215 
216 expect(thinResolve.objectCount).toBe(1);
217 expect(thinScan.table.resolved[0]).toBe(1);
218 expect(cacheCtx.memo?.packCatalog?.map((row) => row.packKey)).toEqual([basePackKey]);
219 });
220 
221 it("resolves same-pack REF_DELTA chains whose bases appear later in the pack", async () => {
222 const baseBlobPayload = new TextEncoder().encode("base\n");
223 const midSuffix = new TextEncoder().encode("mid\n");
224 const finalSuffix = new TextEncoder().encode("final\n");
225 
226 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
227 
228 const midPayload = new Uint8Array(baseBlobPayload.length + midSuffix.length);
229 midPayload.set(baseBlobPayload, 0);
230 midPayload.set(midSuffix, baseBlobPayload.length);
231 const midOid = await computeOid("blob", midPayload);
232 
233 const finalPayload = new Uint8Array(midPayload.length + finalSuffix.length);
234 finalPayload.set(midPayload, 0);
235 finalPayload.set(finalSuffix, midPayload.length);
236 const finalOid = await computeOid("blob", finalPayload);
237 
238 const midDelta = buildAppendOnlyDelta(baseBlobPayload, midSuffix);
239 const finalDelta = buildAppendOnlyDelta(midPayload, finalSuffix);
240 
241 const packBytes = await buildPack([
242 { type: "ref-delta", baseOid: midOid, delta: finalDelta },
243 { type: "ref-delta", baseOid: baseBlob.oid, delta: midDelta },
244 { type: "blob", payload: baseBlobPayload },
245 ]);
246 
247 const packKey = "test/resolve-forward-ref-chain.pack";
248 await env.REPO_BUCKET.put(packKey, packBytes);
249 const head = await env.REPO_BUCKET.head(packKey);
250 
251 const scanResult = await scanPack({
252 env,
253 packKey,
254 packSize: head!.size,
255 limiter: makeLimiter(),
256 countSubrequest: () => {},
257 log,
258 });
259 
260 const repoId = uniqueRepoId();
261 await resolveDeltasAndWriteIdx({
262 env,
263 packKey,
264 packSize: head!.size,
265 limiter: makeLimiter(),
266 countSubrequest: () => {},
267 log,
268 scanResult,
269 repoId,
270 });
271 
272 expect(scanResult.table.resolved.reduce((sum, value) => sum + value, 0)).toBe(3);
273 expect(bytesToHex(scanResult.table.oids.subarray(0, 20))).toBe(finalOid);
274 expect(bytesToHex(scanResult.table.oids.subarray(20, 40))).toBe(midOid);
275 expect(bytesToHex(scanResult.table.oids.subarray(40, 60))).toBe(baseBlob.oid);
276 });
277 
278 it("uses an existing idx to map same-pack REF_DELTA bases during refs-only backfill", async () => {
279 const baseBlobPayload = new TextEncoder().encode("base\n");
280 const midSuffix = new TextEncoder().encode("mid\n");
281 const finalSuffix = new TextEncoder().encode("final\n");
282 
283 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
284 
285 const midPayload = new Uint8Array(baseBlobPayload.length + midSuffix.length);
286 midPayload.set(baseBlobPayload, 0);
287 midPayload.set(midSuffix, baseBlobPayload.length);
288 const midOid = await computeOid("blob", midPayload);
289 
290 const finalPayload = new Uint8Array(midPayload.length + finalSuffix.length);
291 finalPayload.set(midPayload, 0);
292 finalPayload.set(finalSuffix, midPayload.length);
293 const finalOid = await computeOid("blob", finalPayload);
294 
295 const packBytes = await buildPack([
296 {
297 type: "ref-delta",
298 baseOid: midOid,
299 delta: buildAppendOnlyDelta(midPayload, finalSuffix),
300 },
301 {
302 type: "ref-delta",
303 baseOid: baseBlob.oid,
304 delta: buildAppendOnlyDelta(baseBlobPayload, midSuffix),
305 },
306 { type: "blob", payload: baseBlobPayload },
307 ]);
308 
309 const packKey = "test/resolve-ref-chain-existing-idx.pack";
310 await env.REPO_BUCKET.put(packKey, packBytes);
311 const head = await env.REPO_BUCKET.head(packKey);
312 
313 const initialScan = await scanPack({
314 env,
315 packKey,
316 packSize: head!.size,
317 limiter: makeLimiter(),
318 countSubrequest: () => {},
319 log,
320 });
321 const repoId = uniqueRepoId();
322 const initialResolve = await resolveDeltasAndWriteIdx({
323 env,
324 packKey,
325 packSize: head!.size,
326 limiter: makeLimiter(),
327 countSubrequest: () => {},
328 log,
329 scanResult: initialScan,
330 repoId,
331 });
332 
333 const midOidIndex = findOidIndex(initialResolve.idxView, midOid);
334 expect(midOidIndex).toBeGreaterThanOrEqual(0);
335 expect(midOidIndex).not.toBe(1);
336 expect(initialResolve.idxView.offsets[midOidIndex]).toBe(initialScan.table.offsets[1]);
337 
338 const backfillScan = await scanPack({
339 env,
340 packKey,
341 packSize: head!.size,
342 limiter: makeLimiter(),
343 countSubrequest: () => {},
344 log,
345 });
346 
347 const backfillResolve = await resolveDeltasAndWriteIdx({
348 env,
349 packKey,
350 packSize: head!.size,
351 limiter: makeLimiter(),
352 countSubrequest: () => {},
353 log,
354 scanResult: backfillScan,
355 repoId,
356 activeCatalog: [],
357 cacheCtx: createTestCacheContext("http://localhost/test"),
358 writeIdx: false,
359 existingIdxView: initialResolve.idxView,
360 });
361 
362 expect(backfillResolve.idxBytes).toBe(0);
363 expect(backfillScan.table.resolved.reduce((sum, value) => sum + value, 0)).toBe(3);
364 expect(bytesToHex(backfillScan.table.oids.subarray(0, 20))).toBe(finalOid);
365 expect(bytesToHex(backfillScan.table.oids.subarray(20, 40))).toBe(midOid);
366 expect(bytesToHex(backfillScan.table.oids.subarray(40, 60))).toBe(baseBlob.oid);
367 });
368 
369 it("uses another duplicate idx row for identity same-pack REF_DELTA backfill", async () => {
370 const baseBlobPayload = new TextEncoder().encode("duplicate oid base\n");
371 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
372 const identityDelta = buildAppendOnlyDelta(baseBlobPayload, new Uint8Array(0));
373 
374 const packBytes = await buildPack([
375 { type: "ref-delta", baseOid: baseBlob.oid, delta: identityDelta },
376 { type: "blob", payload: baseBlobPayload },
377 ]);
378 
379 const packKey = "test/resolve-ref-identity-existing-idx.pack";
380 await env.REPO_BUCKET.put(packKey, packBytes);
381 const head = await env.REPO_BUCKET.head(packKey);
382 
383 const initialScan = await scanPack({
384 env,
385 packKey,
386 packSize: head!.size,
387 limiter: makeLimiter(),
388 countSubrequest: () => {},
389 log,
390 });
391 const repoId = uniqueRepoId();
392 const initialResolve = await resolveDeltasAndWriteIdx({
393 env,
394 packKey,
395 packSize: head!.size,
396 limiter: makeLimiter(),
397 countSubrequest: () => {},
398 log,
399 scanResult: initialScan,
400 repoId,
401 });
402 
403 expect(findOidIndex(initialResolve.idxView, baseBlob.oid)).toBe(0);
404 
405 const backfillScan = await scanPack({
406 env,
407 packKey,
408 packSize: head!.size,
409 limiter: makeLimiter(),
410 countSubrequest: () => {},
411 log,
412 });
413 
414 const backfillResolve = await resolveDeltasAndWriteIdx({
415 env,
416 packKey,
417 packSize: head!.size,
418 limiter: makeLimiter(),
419 countSubrequest: () => {},
420 log,
421 scanResult: backfillScan,
422 repoId,
423 activeCatalog: [],
424 cacheCtx: createTestCacheContext("http://localhost/test"),
425 writeIdx: false,
426 existingIdxView: initialResolve.idxView,
427 });
428 
429 expect(backfillResolve.idxBytes).toBe(0);
430 expect(backfillScan.table.resolved.reduce((sum, value) => sum + value, 0)).toBe(2);
431 expect(bytesToHex(backfillScan.table.oids.subarray(0, 20))).toBe(baseBlob.oid);
432 expect(bytesToHex(backfillScan.table.oids.subarray(20, 40))).toBe(baseBlob.oid);
433 });
434 
435 it("falls back to an external base for target-pack identity REF_DELTA duplicates", async () => {
436 const baseBlobPayload = new TextEncoder().encode("external duplicate oid base\n");
437 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
438 const repoId = uniqueRepoId();
439 
440 const basePackKey = "test/ref-delta-identity-external-base.pack";
441 const basePack = await buildPack([{ type: "blob", payload: baseBlobPayload }]);
442 await env.REPO_BUCKET.put(basePackKey, basePack);
443 const baseHead = await env.REPO_BUCKET.head(basePackKey);
444 const baseScan = await scanPack({
445 env,
446 packKey: basePackKey,
447 packSize: baseHead!.size,
448 limiter: makeLimiter(),
449 countSubrequest: () => {},
450 log,
451 });
452 const baseResolve = await resolveDeltasAndWriteIdx({
453 env,
454 packKey: basePackKey,
455 packSize: baseHead!.size,
456 limiter: makeLimiter(),
457 countSubrequest: () => {},
458 log,
459 scanResult: baseScan,
460 repoId,
461 });
462 
463 const activeCatalog = [
464 makeActiveCatalogRow({
465 packKey: basePackKey,
466 packBytes: baseHead!.size,
467 objectCount: baseScan.objectCount,
468 idxBytes: baseResolve.idxBytes,
469 }),
470 ];
471 const identityDelta = buildAppendOnlyDelta(baseBlobPayload, new Uint8Array(0));
472 const targetPack = await buildPack([
473 { type: "ref-delta", baseOid: baseBlob.oid, delta: identityDelta },
474 { type: "ref-delta", baseOid: baseBlob.oid, delta: identityDelta },
475 ]);
476 
477 const targetPackKey = "test/ref-delta-identity-external-target.pack";
478 await env.REPO_BUCKET.put(targetPackKey, targetPack);
479 const targetHead = await env.REPO_BUCKET.head(targetPackKey);
480 const initialScan = await scanPack({
481 env,
482 packKey: targetPackKey,
483 packSize: targetHead!.size,
484 limiter: makeLimiter(),
485 countSubrequest: () => {},
486 log,
487 });
488 const initialResolve = await resolveDeltasAndWriteIdx({
489 env,
490 packKey: targetPackKey,
491 packSize: targetHead!.size,
492 limiter: makeLimiter(),
493 countSubrequest: () => {},
494 log,
495 scanResult: initialScan,
496 activeCatalog,
497 cacheCtx: createTestCacheContext("http://localhost/test"),
498 repoId,
499 });
500 
501 const backfillScan = await scanPack({
502 env,
503 packKey: targetPackKey,
504 packSize: targetHead!.size,
505 limiter: makeLimiter(),
506 countSubrequest: () => {},
507 log,
508 });
509 const backfillResolve = await resolveDeltasAndWriteIdx({
510 env,
511 packKey: targetPackKey,
512 packSize: targetHead!.size,
513 limiter: makeLimiter(),
514 countSubrequest: () => {},
515 log,
516 scanResult: backfillScan,
517 activeCatalog,
518 cacheCtx: createTestCacheContext("http://localhost/test"),
519 repoId,
520 writeIdx: false,
521 existingIdxView: initialResolve.idxView,
522 });
523 
524 expect(backfillResolve.idxBytes).toBe(0);
525 expect(backfillScan.table.resolved.reduce((sum, value) => sum + value, 0)).toBe(2);
526 expect(bytesToHex(backfillScan.table.oids.subarray(0, 20))).toBe(baseBlob.oid);
527 expect(bytesToHex(backfillScan.table.oids.subarray(20, 40))).toBe(baseBlob.oid);
528 });
529 
530 it("tries older external duplicate bases when the newest duplicate points back to the target pack", async () => {
531 const targetPayload = new TextEncoder().encode("target prefix\n");
532 const duplicateSuffix = new TextEncoder().encode("duplicate suffix\n");
533 const duplicatePayload = new Uint8Array(targetPayload.length + duplicateSuffix.length);
534 duplicatePayload.set(targetPayload, 0);
535 duplicatePayload.set(duplicateSuffix, targetPayload.length);
536 
537 const targetOid = await computeOid("blob", targetPayload);
538 const duplicateOid = await computeOid("blob", duplicatePayload);
539 const repoId = uniqueRepoId();
540 
541 const olderPackKey = "test/ref-delta-cycle-older-base.pack";
542 const olderPack = await buildPack([{ type: "blob", payload: duplicatePayload }]);
543 await env.REPO_BUCKET.put(olderPackKey, olderPack);
544 const olderHead = await env.REPO_BUCKET.head(olderPackKey);
545 const olderScan = await scanPack({
546 env,
547 packKey: olderPackKey,
548 packSize: olderHead!.size,
549 limiter: makeLimiter(),
550 countSubrequest: () => {},
551 log,
552 });
553 const olderResolve = await resolveDeltasAndWriteIdx({
554 env,
555 packKey: olderPackKey,
556 packSize: olderHead!.size,
557 limiter: makeLimiter(),
558 countSubrequest: () => {},
559 log,
560 scanResult: olderScan,
561 repoId,
562 });
563 const olderRow = makeActiveCatalogRow({
564 packKey: olderPackKey,
565 packBytes: olderHead!.size,
566 objectCount: olderScan.objectCount,
567 idxBytes: olderResolve.idxBytes,
568 });
569 
570 const targetPackKey = "test/ref-delta-cycle-target.pack";
571 const targetPack = await buildPack([
572 {
573 type: "ref-delta",
574 baseOid: duplicateOid,
575 delta: buildCopyPrefixDelta(duplicatePayload, targetPayload.length),
576 },
577 ]);
578 await env.REPO_BUCKET.put(targetPackKey, targetPack);
579 const targetHead = await env.REPO_BUCKET.head(targetPackKey);
580 const targetScan = await scanPack({
581 env,
582 packKey: targetPackKey,
583 packSize: targetHead!.size,
584 limiter: makeLimiter(),
585 countSubrequest: () => {},
586 log,
587 });
588 const targetResolve = await resolveDeltasAndWriteIdx({
589 env,
590 packKey: targetPackKey,
591 packSize: targetHead!.size,
592 limiter: makeLimiter(),
593 countSubrequest: () => {},
594 log,
595 scanResult: targetScan,
596 activeCatalog: [olderRow],
597 cacheCtx: createTestCacheContext("http://localhost/test"),
598 repoId,
599 });
600 const targetRow = makeActiveCatalogRow({
601 packKey: targetPackKey,
602 packBytes: targetHead!.size,
603 objectCount: targetScan.objectCount,
604 idxBytes: targetResolve.idxBytes,
605 });
606 
607 const newerPackKey = "test/ref-delta-cycle-newer-duplicate.pack";
608 const newerPack = await buildPack([
609 {
610 type: "ref-delta",
611 baseOid: targetOid,
612 delta: buildAppendOnlyDelta(targetPayload, duplicateSuffix),
613 },
614 ]);
615 await env.REPO_BUCKET.put(newerPackKey, newerPack);
616 const newerHead = await env.REPO_BUCKET.head(newerPackKey);
617 const newerScan = await scanPack({
618 env,
619 packKey: newerPackKey,
620 packSize: newerHead!.size,
621 limiter: makeLimiter(),
622 countSubrequest: () => {},
623 log,
624 });
625 const newerResolve = await resolveDeltasAndWriteIdx({
626 env,
627 packKey: newerPackKey,
628 packSize: newerHead!.size,
629 limiter: makeLimiter(),
630 countSubrequest: () => {},
631 log,
632 scanResult: newerScan,
633 activeCatalog: [targetRow, olderRow],
634 cacheCtx: createTestCacheContext("http://localhost/test"),
635 repoId,
636 });
637 const newerRow = makeActiveCatalogRow({
638 packKey: newerPackKey,
639 packBytes: newerHead!.size,
640 objectCount: newerScan.objectCount,
641 idxBytes: newerResolve.idxBytes,
642 });
643 
644 const backfillScan = await scanPack({
645 env,
646 packKey: targetPackKey,
647 packSize: targetHead!.size,
648 limiter: makeLimiter(),
649 countSubrequest: () => {},
650 log,
651 });
652 const backfillResolve = await resolveDeltasAndWriteIdx({
653 env,
654 packKey: targetPackKey,
655 packSize: targetHead!.size,
656 limiter: makeLimiter(),
657 countSubrequest: () => {},
658 log,
659 scanResult: backfillScan,
660 activeCatalog: [newerRow, olderRow],
661 cacheCtx: createTestCacheContext("http://localhost/test"),
662 repoId,
663 writeIdx: false,
664 existingIdxView: targetResolve.idxView,
665 });
666 
667 expect(backfillResolve.idxBytes).toBe(0);
668 expect(backfillScan.table.resolved.reduce((sum, value) => sum + value, 0)).toBe(1);
669 expect(bytesToHex(backfillScan.table.oids.subarray(0, 20))).toBe(targetOid);
670 });
671 
672 it("wakes deferred OFS_DELTA children after an external-base REF_DELTA resolves", async () => {
673 const baseBlobPayload = new TextEncoder().encode("base\n");
674 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
675 const basePack = await buildPack([{ type: "blob", payload: baseBlobPayload }]);
676 
677 const basePackKey = "test/ref-delta-ofs-base.pack";
678 await env.REPO_BUCKET.put(basePackKey, basePack);
679 const baseHead = await env.REPO_BUCKET.head(basePackKey);
680 
681 const baseScan = await scanPack({
682 env,
683 packKey: basePackKey,
684 packSize: baseHead!.size,
685 limiter: makeLimiter(),
686 countSubrequest: () => {},
687 log,
688 });
689 const repoId = uniqueRepoId();
690 const baseResolve = await resolveDeltasAndWriteIdx({
691 env,
692 packKey: basePackKey,
693 packSize: baseHead!.size,
694 limiter: makeLimiter(),
695 countSubrequest: () => {},
696 log,
697 scanResult: baseScan,
698 repoId,
699 });
700 
701 const midSuffix = new TextEncoder().encode("mid\n");
702 const finalSuffix = new TextEncoder().encode("final\n");
703 
704 const midPayload = new Uint8Array(baseBlobPayload.length + midSuffix.length);
705 midPayload.set(baseBlobPayload, 0);
706 midPayload.set(midSuffix, baseBlobPayload.length);
707 
708 const finalPayload = new Uint8Array(midPayload.length + finalSuffix.length);
709 finalPayload.set(midPayload, 0);
710 finalPayload.set(finalSuffix, midPayload.length);
711 const finalOid = await computeOid("blob", finalPayload);
712 
713 const packBytes = await buildPack([
714 {
715 type: "ref-delta",
716 baseOid: baseBlob.oid,
717 delta: buildAppendOnlyDelta(baseBlobPayload, midSuffix),
718 },
719 {
720 type: "ofs-delta",
721 baseIndex: 0,
722 delta: buildAppendOnlyDelta(midPayload, finalSuffix),
723 },
724 ]);
725 
726 const packKey = "test/resolve-ref-delta-ofs-dependent.pack";
727 await env.REPO_BUCKET.put(packKey, packBytes);
728 const head = await env.REPO_BUCKET.head(packKey);
729 
730 const scanResult = await scanPack({
731 env,
732 packKey,
733 packSize: head!.size,
734 limiter: makeLimiter(),
735 countSubrequest: () => {},
736 log,
737 });
738 
739 await resolveDeltasAndWriteIdx({
740 env,
741 packKey,
742 packSize: head!.size,
743 limiter: makeLimiter(),
744 countSubrequest: () => {},
745 log,
746 scanResult,
747 activeCatalog: [
748 makeActiveCatalogRow({
749 packKey: basePackKey,
750 packBytes: baseHead!.size,
751 objectCount: baseScan.objectCount,
752 idxBytes: baseResolve.idxBytes,
753 }),
754 ],
755 cacheCtx: createTestCacheContext("http://localhost/test"),
756 repoId,
757 });
758 
759 expect(scanResult.table.resolved.reduce((sum, value) => sum + value, 0)).toBe(2);
760 expect(bytesToHex(scanResult.table.oids.subarray(20, 40))).toBe(finalOid);
761 });
762 
763 it("uses the caller cache context for thin-pack external-base reads", async () => {
764 const baseBlobPayload = new TextEncoder().encode("external base content\n");
765 const baseBlob = await encodeGitObject("blob", baseBlobPayload);
766 const basePackKey = "test/ref-delta-budget-base.pack";
767 const basePack = await buildPack([{ type: "blob", payload: baseBlobPayload }]);
768 await env.REPO_BUCKET.put(basePackKey, basePack);
769 const baseHead = await env.REPO_BUCKET.head(basePackKey);
770 
771 const baseScan = await scanPack({
772 env,
773 packKey: basePackKey,
774 packSize: baseHead!.size,
775 limiter: makeLimiter(),
776 countSubrequest: () => {},
777 log,
778 });
779 
780 const repoId = uniqueRepoId();
781 const baseResolve = await resolveDeltasAndWriteIdx({
782 env,
783 packKey: basePackKey,
784 packSize: baseHead!.size,
785 limiter: makeLimiter(),
786 countSubrequest: () => {},
787 log,
788 scanResult: baseScan,
789 repoId,
790 });
791 
792 const suffix = new TextEncoder().encode("with shared limiter\n");
793 const thinPackKey = "test/ref-delta-budget-thin.pack";
794 const thinPack = await buildPack([
795 {
796 type: "ref-delta",
797 baseOid: baseBlob.oid,
798 delta: buildAppendOnlyDelta(baseBlobPayload, suffix),
799 },
800 ]);
801 await env.REPO_BUCKET.put(thinPackKey, thinPack);
802 const thinHead = await env.REPO_BUCKET.head(thinPackKey);
803 
804 const labels: string[] = [];
805 const limiter = makeTracingLimiter(labels);
806 const counter = { count: 0 };
807 const cacheCtx = createTestCacheContext("http://localhost/test", 10);
808 cacheCtx.memo = {
809 ...(cacheCtx.memo || {}),
810 limiter,
811 };
812 
813 const thinScan = await scanPack({
814 env,
815 packKey: thinPackKey,
816 packSize: thinHead!.size,
817 limiter,
818 countSubrequest: makeCountSubrequest(counter),
819 log,
820 });
821 
822 await resolveDeltasAndWriteIdx({
823 env,
824 packKey: thinPackKey,
825 packSize: thinHead!.size,
826 limiter,
827 countSubrequest: makeCountSubrequest(counter),
828 log,
829 scanResult: thinScan,
830 activeCatalog: [
831 makeActiveCatalogRow({
832 packKey: basePackKey,
833 packBytes: baseHead!.size,
834 objectCount: baseScan.objectCount,
835 idxBytes: baseResolve.idxBytes,
836 }),
837 ],
838 cacheCtx,
839 repoId,
840 });
841 
842 expect(labels).toContain("r2:get-pack-idx");
843 expect(labels).toContain("r2:get-range");
844 expect(cacheCtx.memo?.subreqBudget).toBeLessThan(10);
845 });
846});