Skip to content

Commit 65b0092

Browse files
authored
Merge pull request #580 from morluto/agent/optimize-test-template-copy
perf(tests): hardlink immutable blobs in template copy
2 parents e2c264b + 26fc5af commit 65b0092

2 files changed

Lines changed: 98 additions & 3 deletions

File tree

tests/support/state.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import os
56
import shutil
67
import tempfile
78
from collections.abc import Callable
@@ -97,14 +98,46 @@ def publish_template(
9798

9899

99100
def copy_template(template: Path, destination: Path) -> Path:
100-
"""Copy an immutable template into a new mutable per-test directory."""
101+
"""Copy an immutable template into a new mutable per-test directory.
102+
103+
Blobs under ``blobs/`` are content-addressed and immutable, so they are
104+
hardlinked instead of copied. This avoids copying ~7 MB of blob data per
105+
test across 80+ composition tests — a significant I/O reduction.
106+
"""
101107

102108
template = Path(template)
103109
destination = Path(destination)
104110
if not template.is_dir():
105111
raise FileNotFoundError(f"template directory does not exist: {template}")
106112
if destination.exists():
107113
raise FileExistsError(f"mutable test state already exists: {destination}")
108-
destination.parent.mkdir(parents=True, exist_ok=True)
109-
shutil.copytree(template, destination)
114+
destination.mkdir(parents=True, exist_ok=True)
115+
116+
# Copy everything except the blobs directory with a normal recursive copy,
117+
# then hardlink the content-addressed blobs.
118+
for entry in os.scandir(template):
119+
src = Path(entry.path)
120+
dst = destination / entry.name
121+
if entry.name == "blobs":
122+
_hardlink_tree(src, dst)
123+
elif entry.is_dir():
124+
shutil.copytree(src, dst)
125+
else:
126+
shutil.copy2(src, dst)
110127
return destination
128+
129+
130+
def _hardlink_tree(src: Path, dst: Path) -> None:
131+
"""Recursively hardlink all files from *src* into *dst*."""
132+
133+
dst.mkdir(parents=True, exist_ok=True)
134+
for entry in os.scandir(src):
135+
s = Path(entry.path)
136+
d = dst / entry.name
137+
if entry.is_dir(follow_symlinks=False):
138+
_hardlink_tree(s, d)
139+
elif entry.is_file(follow_symlinks=False):
140+
os.link(s, d)
141+
else:
142+
# Fallback for any unusual file types.
143+
shutil.copy2(s, d)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Tests for copy_template hardlink optimization."""
2+
import os
3+
from pathlib import Path
4+
5+
from tests.support.state import copy_template
6+
7+
8+
def test_copy_template_hardlinks_blobs(tmp_path: Path) -> None:
9+
"""Blobs should be hardlinked (not copied) for I/O efficiency."""
10+
template = tmp_path / "template"
11+
template.mkdir()
12+
blob = template / "blobs" / "sha256" / "00" / "abc123"
13+
blob.parent.mkdir(parents=True)
14+
blob.write_bytes(b"blob content")
15+
(template / "metadata.sqlite3").write_bytes(b"database")
16+
17+
dest = tmp_path / "destination"
18+
copy_template(template, dest)
19+
20+
assert (dest / "blobs" / "sha256" / "00" / "abc123").read_bytes() == b"blob content"
21+
template_inode = os.stat(blob).st_ino
22+
dest_inode = os.stat(dest / "blobs" / "sha256" / "00" / "abc123").st_ino
23+
assert template_inode == dest_inode, "blob should be hardlinked"
24+
25+
template_meta = os.stat(template / "metadata.sqlite3").st_ino
26+
dest_meta = os.stat(dest / "metadata.sqlite3").st_ino
27+
assert template_meta != dest_meta, "metadata should be copied, not hardlinked"
28+
29+
30+
def test_copy_template_preserves_all_files(tmp_path: Path) -> None:
31+
"""All non-blob files should be present in the destination."""
32+
template = tmp_path / "template"
33+
template.mkdir()
34+
(template / "blobs").mkdir()
35+
blob = template / "blobs" / "sha256" / "00" / "def456"
36+
blob.parent.mkdir(parents=True)
37+
blob.write_bytes(b"blob")
38+
(template / "metadata.sqlite3").write_bytes(b"database")
39+
(template / "metadata.sqlite3-shm").write_bytes(b"shm")
40+
(template / "metadata.sqlite3-wal").write_bytes(b"wal")
41+
42+
dest = tmp_path / "destination"
43+
copy_template(template, dest)
44+
45+
for name in ("metadata.sqlite3", "metadata.sqlite3-shm", "metadata.sqlite3-wal"):
46+
assert (dest / name).exists(), f"{name} should be copied"
47+
48+
49+
def test_copy_template_raises_on_existing_destination(tmp_path: Path) -> None:
50+
"""copy_template should refuse to overwrite an existing destination."""
51+
template = tmp_path / "template"
52+
template.mkdir()
53+
(template / "blobs").mkdir()
54+
(template / "blobs" / "sha256" / "00" / "abc").parent.mkdir(parents=True)
55+
(template / "blobs" / "sha256" / "00" / "abc").write_bytes(b"blob")
56+
57+
dest = tmp_path / "destination"
58+
dest.mkdir()
59+
import pytest
60+
61+
with pytest.raises(FileExistsError):
62+
copy_template(template, dest)

0 commit comments

Comments
 (0)