Support Lazily Ref Clone#10
Draft
samir-openai wants to merge 1 commit intocloudflare:mainfrom
Draft
Conversation
Collaborator
|
I like this: for the most part even big repos (Next.js full clone) take ~11-15s to clone all the refs+blobless, but… 1-2s is better, especially if you’re I/O constrained. let me know when you’re ready for a review. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a draft proposal for a lazy clone
lazily add-repo
The current implementation still requires has blocking loading time on the async ref only clone.
artifact-fs add-repo --async ...This allows a repo to be actively cloning refs via the daemon - but not block container/sandbox startup. This is good for cases where a repo may not be used by an agent at all ... Thus allowing agents to lazily interact with the git repo - even if the clone of refs itself takes a long time.
Summary
The key change is Block every single filesystem operation with a wrapper around
ArtifactFuse.Existing file operation code stays mostly unchanged.
This still requires a placeholder FUSE mount, because a process can only block on repo filesystem operations if ArtifactFS has mounted something at that path. A repo that is merely “hidden until ready” without a mount would cause operations to fail or see a normal empty directory, not block.
Behavior and state model
Important Behavioral Choice
Use this behavior:
artifact-fs add-repo --async ...returns after registration.Examples that block while preparation is active:
Minimal State Model
Add three async preparation states:
preparingreadyfailedRegistry additions:
remote_url TEXT NOT NULL DEFAULT ''prepared_gitdir INTEGER NOT NULL DEFAULT 0fetch_ref TEXT NOT NULL DEFAULT ''prepare_state TEXT NOT NULL DEFAULT ''prepare_error TEXT NOT NULL DEFAULT ''CLI and API
CLI/API
Keep existing blocking behavior unchanged:
Add async mode:
Keep generic prepared-gitdir compatibility, but as a thin variant:
Add retry:
Auth rules:
--asyncrejects HTTPS remotes with inline credentials.GIT_TERMINAL_PROMPT=0.GIT_SSH_COMMANDis already configured.Implementation design
FUSE Design
Add
internal/fusefs/ready_gate.go:Wait(ctx) errorMarkReady()MarkFailed(error)Reset()Add
internal/fusefs/gated_fs.go:gatedFileSystemimplementsfuseutil.FileSystem.ArtifactFuse.gate.Wait(ctx)before delegating.ForgetInodeBatchForgetReleaseDirHandleReleaseFileHandleDestroyStatFSThis file has method boilerplate, but the existing operation implementations remain clean and unchanged.
Update
MountRepoto support an optional gate:Behavior:
gate == nil: existing behavior.gate != nil: wrapNewArtifactFuse(...)ingatedFileSystem.Daemon Design
Keep the existing blocking path for normal repos.
For async repos:
add-repo --asyncpersists safe config withprepare_state=preparingand returns.syncRepossees the async repo.0.readyprepare_errorfailedartifact-fs prepare --name repo:preparingUse one in-memory
preparing map[RepoID]boolto dedupe daemon workers. A cross-process lock is optional and can be skipped in the simplified version unless multiple daemons are supported.Gitstore Changes
Add helpers:
CloneBloblessNonInteractive(ctx, cfg)FetchRefNonInteractive(ctx, cfg, ref)ValidatePreparedGitDir(ctx, cfg)PrepareFetchedBranch(ctx, cfg, ref)Keep the existing credential-helper path for blocking clone with inline credentials.
For async clone:
RemoteURL.read-tree HEADbehavior, but run it before publishing readiness.For prepared-gitdir:
Status
Keep status output simple:
Map states as:
prepare_state=preparing->state=preparingprepare_state=failed->state=failedstate=mountedstate=unmountedTests and assumptions
Tests
Add or adjust focused tests:
add-repo --asyncreturns without cloning.ReadyGatewaits, opens, fails, and resets.gatedFileSystemblocks/delegates user-facing operations through a fake filesystem.add-repotests continue to pass.go build ./cmd/artifact-fs go vet ./... go test ./...Assumptions
Proof output
Proof