Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@ file is the human-readable summary of what changed and why.

### Sandbox

- **Creating a sandbox is near-instant and no longer costs 8 GB** — a new
sandbox is meant to start as a copy-on-write clone of the shared disk image,
costing no space until the guest writes something. On macOS the clone never
happened: the copy fell back to writing the whole 8 GB image out byte for
byte, filling in the empty space the image leaves sparse. Creating or
resetting a sandbox spent that time writing gigabytes, then the guest booted
while the disk was still catching up and had to compete for it — which is why
a sandbox's first start could crawl and sometimes gave up with "sandbox
server did not become healthy" before finishing. The clone now happens for
real: creation is immediate, a sandbox costs roughly nothing on disk until it
is used, and first start takes about as long as every start after it.
([#293](https://github.com/quickbeard/codev-code/pull/293))
- **Work sessions reach the model on WSL's mirrored networking** — on Windows,
a sandbox's model traffic crosses from the WSL guest to a relay on the
Windows host, and the guest found the host by assuming WSL's default NAT
Expand Down
28 changes: 25 additions & 3 deletions packages/codev-sandbox/src/vz/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,8 @@ export function createVzProvider(options: VzProviderOptions): SandboxProvider {
await writeImageMarker(dirs.base, asset)
}
// The pristine unpacked disk stays untouched; the instance runs a
// copy-on-write clone (APFS clonefile via FICLONE, plain copy elsewhere),
// so reset is just re-cloning.
await copyFile(join(dirs.image, VZ_DISK_FILE), dirs.disk, constants.COPYFILE_FICLONE)
// copy-on-write clone, so reset is just re-cloning.
await cloneDisk(join(dirs.image, VZ_DISK_FILE), dirs.disk, platform)
}

// A custom image is used from where it lies; otherwise the published one
Expand Down Expand Up @@ -309,6 +308,29 @@ export function createVzProvider(options: VzProviderOptions): SandboxProvider {
}
}

// A copy-on-write clone of the pristine disk, falling back to a byte copy.
//
// `copyFile`'s COPYFILE_FICLONE is a no-op on macOS: libuv opens the
// destination and calls fcopyfile(), and only clonefile(2) — which takes paths
// and requires the destination not to exist — can share extents. So every
// provision wrote the whole image out instead: ~9s and a full 8 GiB per
// sandbox for a disk that should cost nothing, and it expands the source's
// sparse holes into real zeroes. Worse, the VM boots straight after, so the
// guest competes with gigabytes of write-back for the disk it is reading —
// that contention, not the image itself, is what pushed first boot past the
// 60s start budget. `cp -c` is the syscall; it fails outright rather than
// silently copying when the clone cannot happen, which is exactly the signal
// the fallback needs.
async function cloneDisk(source: string, destination: string, platform: NodeJS.Platform) {
await rm(destination, { force: true })
if (platform === "darwin") {
const cloned = await runCommand("cp", ["-c", source, destination]).catch(() => null)
if (cloned?.code === 0) return
await rm(destination, { force: true })
}
await copyFile(source, destination, constants.COPYFILE_FICLONE)
}

async function defaultMacosVersion() {
const result = await runCommand("sw_vers", ["-productVersion"]).catch(() => null)
if (!result || result.code !== 0) return null
Expand Down
Loading