Documentation
¶
Overview ¶
Package sync implements Fossil's multi-round sync and clone protocol.
The client side drives convergence: Sync opens a session against a Transport, exchanges xfer cards in rounds until both sides agree on the set of blobs, then returns a SyncResult. Clone creates a new repo file and populates it from the remote.
The server side is stateless per round: HandleSync processes one request and produces one response. Use XferHandler to embed sync in an http.ServeMux alongside operational routes like /healthz.
Observability is injected via the Observer interface — pass nil for zero-cost no-ops, or supply an implementation (e.g. OTelObserver from leaf/telemetry) for traces, metrics, and structured logs.
Index ¶
- Constants
- Variables
- func HandleSync(ctx context.Context, r *repo.Repo, req *xfer.Message) (*xfer.Message, error)
- func HandleSyncWithOpts(ctx context.Context, r *repo.Repo, req *xfer.Message, opts HandleOpts) (*xfer.Message, error)
- func PostXfer(ctx context.Context, client *http.Client, ...) (*http.Response, error)
- func RetryableFault(err error) error
- func ServeHTTP(ctx context.Context, addr string, r *repo.Repo, h HandleFunc) error
- func WithRemoteAddr(ctx context.Context, addr string) context.Context
- func XferHandler(r *repo.Repo, h HandleFunc) http.HandlerFunc
- type BuggifyChecker
- type CkinLockFail
- type CkinLockReq
- type CloneOpts
- type CloneResult
- type HTTPTransport
- type HandleEnd
- type HandleFunc
- type HandleOpts
- type HandleStart
- type MergeBaseQuery
- type MockTransport
- type NamedHorizon
- type Observer
- type PeerAddr
- type PhantomStallError
- type RefusedError
- type RoundStats
- type SeedMismatch
- type SeedReport
- type SessionEnd
- type SessionStart
- type StatusError
- type SyncOpts
- type SyncResult
- func Deepen(ctx context.Context, r *repo.Repo, peer Transport, target string, ...) (*SyncResult, error)
- func FetchCone(ctx context.Context, r *repo.Repo, peer Transport, next *cone.Cone, ...) (*SyncResult, error)
- func FetchRequiredArtifacts(ctx context.Context, r *repo.Repo, peer Transport, roots []string, ...) (*SyncResult, error)
- func FetchRequiredFiles(ctx context.Context, r *repo.Repo, peer Transport, names []string, ...) (*SyncResult, error)
- func Sync(ctx context.Context, r *repo.Repo, t Transport, opts SyncOpts) (result *SyncResult, err error)
- type SyncedTable
- type TableSyncEnd
- type TableSyncStart
- type Transport
- type TransportFault
Constants ¶
const ( // MaxExchangeAttempts is how many times one round is sent before the // clone or sync gives up, the first attempt included. MaxExchangeAttempts = 4 // ExchangeRetryBaseDelay is the wait before the second attempt. It // doubles before each further attempt, capped at ExchangeRetryMaxDelay. ExchangeRetryBaseDelay = 250 * time.Millisecond // ExchangeRetryMaxDelay caps the backoff between attempts. ExchangeRetryMaxDelay = 2 * time.Second )
Bounds on retrying a single exchange round. A clone of a large repository is a conversation of many rounds spanning minutes, so one transient drop anywhere in it used to discard the whole transfer (#185). Retrying is bounded so a peer that is genuinely gone still fails promptly: the delays below sum to under two seconds before the last attempt.
const ( // DefaultMaxSend is the default byte budget per round for file payloads. DefaultMaxSend = 250000 // MaxRounds caps the number of sync rounds before giving up. MaxRounds = 100 // MaxGimmeBase is the minimum gimme cap per round. MaxGimmeBase = 200 )
const DefaultCkinLockTimeout = 60 * time.Second
DefaultCkinLockTimeout is the default duration after which a ci-lock expires.
const DefaultCloneBatchBytes = 16_000_000
DefaultCloneBatchBytes bounds the wire bytes a single clone round emits.
A clone batch is bounded by output size, never by artifact count. A count cannot adapt to artifact size: 200 tiny control artifacts and 200 large blobs produce wildly different messages, and neither corresponds to what the transport can carry. §8.2 leaves the bound entirely to the server ("clients MUST NOT infer count or size from range"), so the only real constraint is that the response stays a size a server can hold and that a repository drains in rounds proportional to its bytes, not its artifacts.
emitCloneBatch now retransmits stored content per §7.2 (issue #98) rather than the fully expanded artifacts it used to send, so bytesSent tracks the same quantity canonical's 5,000,000 max-download bounds: repository bytes, not the 80x-220x-larger expanded form. This constant was chosen before that fix, against the expanded-bytes regime documented in issue #88 where a smaller value pushed fossil- and sqlite-sized corpora past MaxRounds -- that constraint is gone now that wire bytes and repository bytes are the same quantity. Retuning this value toward canonical's constant is issue #109's territory (server pagination/budget accounting), not this fix's; it is left at its prior value here to avoid colliding with that work in flight. 16,000,000 stays a safe, if generous, upper bound either way: it only widens how much stored content one round may carry.
This bounds an ordinary round at the budget plus the artifact that crosses it, and emitCloneBatch caps that sum at xfer.MaxDecompressedBytes so the client can always decode a round (issue #109). The one exception is a single artifact larger than that bound: it is sent alone, in a round of its own, to make progress -- so a repository holding one 2 GB artifact still emits a 2 GB round, but such an artifact is unclonable regardless. Measured worst case is ~25 MB across the three repositories above, since the largest single artifacts are 9.4 MB (libfossil) and 17.0 MB (sqlite). Against the count bound's unbounded 44.9 MB that is a large reduction, and concurrent clones multiply it.
const SettingPartialMirror = "partial-mirror"
SettingPartialMirror is the boolean repository setting that lets a sparse store serve clones its cone does not cover (§5.3).
Variables ¶
var ErrNoDeepenPoint = errors.New("horizon: no common ancestor of the horizon and the target to deepen to")
ErrNoDeepenPoint is returned by Deepen when no common ancestor of the store's C and the target can be established, here or by the origin: no ancestor of C reaches the target, so no deepen does, and C never moves to a check-in that is not its ancestor (draft-fossil-horizon-00 §5.8). The message names the remedy.
var ErrNoSeedToVerify = errors.New("horizon: the store holds no seed to verify")
ErrNoSeedToVerify is returned by VerifySeed for a store that is not a structural horizon holding a seed.
var ErrRedirectDowngrade = errors.New("sync: cannot redirect from HTTPS to HTTP")
ErrRedirectDowngrade reports a redirect from HTTPS to plain HTTP. Following it would resend the request, login card included, in cleartext.
var ErrRedirectLimit = errors.New("sync: redirect limit exceeded")
ErrRedirectLimit reports a twentieth redirect.
Functions ¶
func HandleSync ¶
HandleSync processes an incoming xfer request and produces a response. Stateless per-round — the client drives convergence.
func HandleSyncWithOpts ¶
func HandleSyncWithOpts(ctx context.Context, r *repo.Repo, req *xfer.Message, opts HandleOpts) (*xfer.Message, error)
HandleSyncWithOpts processes an incoming xfer request with optional fault injection. Used by DST harness; production callers use HandleSync. A request opts.MaxStoredBytes refused returns its reply, the one error card, with a *RefusedError carrying that card's text; the request was rolled back, so nothing from it is stored.
func PostXfer ¶ added in v0.11.1
func PostXfer(ctx context.Context, client *http.Client, target, contentType, loginCookie string, body []byte) (*http.Response, error)
PostXfer POSTs body to target and follows redirects as fossil's http_exchange does (src/http.c), not as net/http does. net/http turns a 301 or 302 into a GET without the body, follows a downgrade from HTTPS to HTTP, and stops after 10. Fossil re-POSTs the same body for 301, 302, 307 and 308, strips a trailing /xfer from the new location, refuses a downgrade, and fails on the twentieth redirect.
The client's own redirect policy is replaced for these requests only, so a caller's client is used as given but cannot loosen these rules. A redirect without a Location is returned as is, for the caller to report its status.
loginCookie, when not empty, goes in the x-f-l-c cookie of every request, as fossil rebuilds a cookie login for each redirect target (§4, §6.5).
func RetryableFault ¶ added in v0.8.0
RetryableFault tags an error raised while bytes were still crossing the wire, so the round loop may resend the same request. Transports outside this package use it for their read stage, where a short read means the reply was cut off rather than malformed; a failure raised while interpreting a reply that arrived whole must never be tagged.
func ServeHTTP ¶
ServeHTTP starts an HTTP server that accepts Fossil xfer requests. Blocks until ctx is cancelled. Stock fossil clone/sync can connect.
func WithRemoteAddr ¶ added in v0.14.0
WithRemoteAddr returns ctx carrying addr, the address a sync request came from, for the server's receipt (fossil's REMOTE_ADDR). A host:port address is recorded as the host alone.
func XferHandler ¶
func XferHandler(r *repo.Repo, h HandleFunc) http.HandlerFunc
XferHandler returns an http.HandlerFunc that decodes Fossil xfer requests, dispatches to the HandleFunc, and encodes the response. Use this to compose a custom mux when you need additional routes (e.g. /healthz) alongside xfer.
Types ¶
type BuggifyChecker ¶
BuggifyChecker is an optional fault injection interface. Pass nil in production — implementations should be nil-safe.
type CkinLockFail ¶
CkinLockFail reports that another client holds the check-in lock.
type CkinLockReq ¶
CkinLockReq requests a server-side check-in lock.
type CloneOpts ¶
type CloneOpts struct {
StoreCone *cone.Cone // nil is dense; local policy, never synchronized
User string // Credentials for clone auth (also default admin user)
Password string
Version int // Protocol version (default 3)
Env *simio.Env // nil defaults to RealEnv
Observer Observer // nil defaults to no-op
Buggify BuggifyChecker // fault injection for DST (nil = no faults)
// PullPhantoms, when set, makes Clone fetch the artifacts the clone
// rounds left missing with a follow-up pull session, and fail with a
// *PhantomStallError if any are still missing after it. That pull
// departs from Section 8.2 of the sync draft, which sends no clone,
// pull or push card after clone_seqno 0, so it is off by default.
PullPhantoms bool
// SeedKey, when set, is the origin's public key: an SSH public key line
// or an OpenPGP fingerprint. The clone keeps it as its horizon-seed-key
// setting and verifies every structural seed against it
// (draft-fossil-horizon-00 §5.2.4); empty accepts an unsigned seed.
SeedKey string
// Origin names the peer the clone addresses, keying the horizon cursor
// a converged horizon clone records (draft-fossil-horizon-00 §6.2.6) as
// SyncOpts.Origin keys a sync's. Empty keeps none.
Origin string
// Named, when set, asks for the store's horizon by name rather than by
// hash (draft-fossil-horizon-00 §6.1, §6.2.7); StoreCone then gives the
// path members alone and must carry no horizon. The store records only
// the check-in the name resolves to.
Named *NamedHorizon
}
CloneOpts configures a clone operation.
type CloneResult ¶
type CloneResult struct {
Elided int // artifacts deliberately absent under the store cone
ConeServed int // replies acknowledging the store cone (sparse-cone §6.3); the rest were filtered locally
HorizonServed int // replies serving the store's horizon at either level (horizon §6.2.5); the rest were classified locally
// StructuralServed counts the replies that served the structural level,
// and HorizonFallback reports a structural clone that kept the content
// horizon instead: its origin did not serve the structural level with a
// seed in the first reply, or the seed failed its provenance (§5.1,
// §5.2.4, §6.3).
StructuralServed int
HorizonFallback bool
Rounds int
BlobsRecvd int
ArtifactsLinked int // Manifests crosslinked into event table
ProjectCode string
ServerCode string
Messages []string // Informational messages from server, and a horizon-fallback: remedy (§6.3)
// HorizonHistoryURL is the interface outside the protocol at which the
// origin says history below a horizon can be queried (horizon-served
// URL, §6.2.5), the last a reply named; "" when none did.
HorizonHistoryURL string
// HorizonResolved is the check-in a clone's Named horizon resolved to and
// HorizonSteps the steps back it took, fewer than its Depth when
// history ran out; HorizonResolvedLocally reports the clone resolved it
// itself, its origin having answered no horizon-resolved (§6.3).
HorizonResolved string
HorizonSteps uint32
HorizonResolvedLocally bool
}
CloneResult reports what happened during a clone.
func Clone ¶
func Clone(ctx context.Context, path string, t Transport, opts CloneOpts) (r *repo.Repo, result *CloneResult, err error)
Clone performs a full repository clone from a remote Fossil server. It creates a new repository at path, runs the clone protocol until convergence, and returns the opened repo and a result summary. On error, the partially-created repo file is removed. Artifacts the clone rounds leave missing stay phantoms unless opts.PullPhantoms is set.
type HTTPTransport ¶
type HTTPTransport struct {
URL string // repo root, e.g. "http://localhost:8080"
// contains filtered or unexported fields
}
HTTPTransport speaks Fossil's HTTP /xfer protocol. Fossil routes to /xfer based on Content-Type: application/x-fossil, NOT the URL path. URL should be the repo root (e.g. "http://localhost:8080").
func (*HTTPTransport) PeerAddr ¶ added in v0.14.0
func (t *HTTPTransport) PeerAddr() string
PeerAddr returns the IP address of the server the last exchange reached.
type HandleEnd ¶
type HandleEnd struct {
CardsProcessed int
FilesSent int
FilesReceived int
// SparseClassified counts the artifacts a cone request's keep-set
// discovery read and classified (§6.2); zero for a dense request.
SparseClassified int
Err error
}
HandleEnd describes the result of a server-side sync request.
type HandleFunc ¶
HandleFunc is the server-side sync handler signature. Transport listeners call this with decoded requests and write back the response.
type HandleOpts ¶
type HandleOpts struct {
// PartialMirror permits clone requests beyond this store's cone, as the
// SettingPartialMirror repository setting does for every caller.
PartialMirror bool
Buggify BuggifyChecker // nil in production.
Observer Observer // nil defaults to no-op.
ContentCache *content.Cache // nil = no caching; rid-keyed, so not across a rollback
Clock simio.Clock // time for the reply's timestamp comment and receipt; nil = real time
// RemoteAddr is the address the request came from, recorded with what it
// pushes (fossil's REMOTE_ADDR). "" takes the one WithRemoteAddr put in
// the context, if any.
RemoteAddr string
// MaxStoredBytes, when positive, is the most bytes one artifact this
// request pushes may occupy in blob.content, as stored (compressed, and
// as a delta when it arrives as one). A host whose storage caps a single
// value sets it: an artifact over it refuses the request whole, naming
// the artifact, instead of failing at the storage layer. 0 stores any
// size. Clone and pull replies are not limited. The HandleSync a
// ServeHTTP or XferHandler caller usually passes applies no budget.
MaxStoredBytes int
}
HandleOpts configures optional behavior for HandleSync.
type HandleStart ¶
type HandleStart struct {
Operation string // "sync" or "clone"
ProjectCode string
RemoteAddr string
}
HandleStart describes the beginning of a server-side sync request.
type MergeBaseQuery ¶ added in v0.15.0
MergeBaseQuery is one merge-base question (§6.2.4), every check-in named by full hash: in a merge the primary is the check-in merged in, and the secondaries the checkout's version and each plain merge pending in it. The zero value asks nothing.
type MockTransport ¶
MockTransport replays canned responses for testing.
type NamedHorizon ¶ added in v0.16.0
type NamedHorizon struct {
Name string
Depth uint32
Rule cone.HorizonRule
Level cone.HorizonDepth
}
NamedHorizon is a horizon asked for by name: the origin resolves Name, tip, a symbolic name or a full check-in hash, and steps back Depth check-ins along primary parents (§6.2.7); a peer that does not resolve it serves densely and the clone resolves it at the content horizon (§6.3).
type Observer ¶
type Observer interface {
// Client-side session lifecycle.
Started(ctx context.Context, info SessionStart) context.Context
RoundStarted(ctx context.Context, round int) context.Context
RoundCompleted(ctx context.Context, round int, stats RoundStats)
Completed(ctx context.Context, info SessionEnd, err error)
// Per-error recording — called on individual protocol errors.
Error(ctx context.Context, err error)
// Server-side request lifecycle.
HandleStarted(ctx context.Context, info HandleStart) context.Context
HandleCompleted(ctx context.Context, info HandleEnd)
// Table sync lifecycle.
TableSyncStarted(ctx context.Context, info TableSyncStart)
TableSyncCompleted(ctx context.Context, info TableSyncEnd)
}
Observer receives lifecycle callbacks during sync and clone operations. A single Observer instance may be shared across multiple concurrent sessions. Pass nil for no-op default.
Performance: nopObserver implements all methods as empty functions. The only cost on the hot path is one indirect call per invocation (~2ns).
type PeerAddr ¶ added in v0.14.0
type PeerAddr struct {
// contains filtered or unexported fields
}
PeerAddr records the IP address of the peer an HTTP request connected to. The zero value is ready to use and safe for concurrent use.
type PhantomStallError ¶
type PhantomStallError struct {
Count int // phantom UUIDs still outstanding when the clone stopped
Sample []string // up to phantomStallSampleSize lexicographically-smallest of those UUIDs, for diagnosis
}
PhantomStallError reports a clone that stopped making progress while phantom blobs — content referenced but never received — were still outstanding. It is returned only when CloneOpts.PullPhantoms is set and phantoms remain after the follow-up pull; a default Clone ends at clone_seqno 0 (sync-protocol-00 Section 8.2) and does not look for them. Count is programmatically reachable so a caller can act on it without parsing the message.
func (*PhantomStallError) Error ¶
func (e *PhantomStallError) Error() string
type RefusedError ¶ added in v0.17.0
type RefusedError struct {
Message string
}
RefusedError is returned, beside the reply, by a HandleSyncWithOpts request that HandleOpts.MaxStoredBytes refused. Message is the reply's one error card, so a host whose storage aborts on the request's rollback can report the refusal without parsing the reply.
func (*RefusedError) Error ¶ added in v0.17.0
func (e *RefusedError) Error() string
Error returns Message.
type RoundStats ¶
type RoundStats struct {
FilesSent int
FilesReceived int
GimmesSent int
IgotsSent int
BytesSent int64
BytesReceived int64
}
RoundStats reports per-round activity for observer callbacks. Value type — no allocations on the nop path.
type SeedMismatch ¶ added in v0.15.0
type SeedMismatch struct {
Checkin, Tag, Seeded, Derived string
}
SeedMismatch is one tag at one entry of K on which the seed and the derivation disagree. Seeded and Derived spell the tag as a T card does, its sign then its value ("*red", "+"), and are "" where it is absent.
type SeedReport ¶ added in v0.15.0
type SeedReport struct {
Checked int
Mismatches []SeedMismatch
}
SeedReport is what VerifySeed found: how many of the seed's T cards it checked, and every tag at an entry where the seed and the derivation over real artifacts disagree.
func VerifySeed ¶ added in v0.15.0
func VerifySeed(ctx context.Context, r *repo.Repo, peer Transport, opts SyncOpts) (SeedReport, error)
VerifySeed is the verification mode of draft-fossil-horizon-00 §5.9: trusting the seed becomes a choice with a known price. It deepens the store to the content depth at the same C through peer, which fetches every manifest below C and so the real tag artifacts and primary-parent chains the seed summarized, and discards the seed (§5.8). The store, now deriving from real artifacts alone, is compared with what the seed asserted at each entry. The price is every manifest below C, and the store stays a content-horizon store, which holds no seed; a transfer that fails puts the structural cone and the seed back.
type SessionEnd ¶
type SessionEnd struct {
Operation string
Rounds int
FilesSent, FilesRecvd int
UVFilesSent, UVFilesRecvd int
UVGimmesSent int
BytesSent, BytesRecvd int64
ProjectCode string
Errors []string
}
SessionEnd describes the result of a sync or clone operation.
type SessionStart ¶
type SessionStart struct {
Operation string // "sync" or "clone"
Push, Pull bool
UV bool
ProjectCode string
PeerID string // identifies the leaf agent instance
}
SessionStart describes the beginning of a sync or clone operation.
type StatusError ¶ added in v0.8.0
type StatusError struct {
Code int // HTTP status code
Status string // HTTP status line, e.g. "503 Service Unavailable"
}
StatusError reports a non-2xx HTTP reply from a sync peer. It is raised instead of decoding the body, because an error page is not an xfer reply and reporting it as a decode failure hides what actually happened.
func (*StatusError) Error ¶ added in v0.8.0
func (e *StatusError) Error() string
func (*StatusError) RetryableTransportFault ¶ added in v0.8.0
func (e *StatusError) RetryableTransportFault() bool
RetryableTransportFault treats 5xx, 429 and 408 as worth another attempt: each says the peer could not serve this request now, not that the request was wrong. Every other status — 4xx above all — is the peer rejecting the request itself, which repeating cannot fix and would only obscure.
type SyncOpts ¶
type SyncOpts struct {
Push, Pull bool // enable push/pull directions (at least one must be true)
ProjectCode string // repo's project-code — sent to identify the repository
ServerCode string // server-code from a previous session (cookie-like, speeds up sync)
User string // login user — empty means unauthenticated "nobody" sync
Password string // login password
PeerID string // identifies this leaf agent instance (for observability)
MaxSend int // byte budget per round for file payloads (0 defaults to DefaultMaxSend)
UV bool // enable unversioned file sync (wiki, forum, attachments)
XTableSync bool // enable extension table sync (peer_registry, etc.) — only between EdgeSync peers
Private bool // enable private artifact sync
Env *simio.Env // nil defaults to RealEnv
Buggify BuggifyChecker // nil in production — used by DST for fault injection
Observer Observer // nil defaults to no-op
CkinLock *CkinLockReq // nil = no lock requested
ContentCache *content.Cache // nil = no caching; rid-keyed, so not across a rollback
// Origin names the peer this session addresses, keying the horizon
// cursor a horizon store keeps per origin (draft-fossil-horizon-00
// §6.2.6). Empty keeps no cursor: every request announces in full.
Origin string
// MergeBaseOf, when set, asks the origin for the merge base it names
// (draft-fossil-horizon-00 §6.2.4), once, in the first request; it needs
// Pull, and one or two secondaries. The answer is in the result.
MergeBaseOf MergeBaseQuery
}
SyncOpts configures a sync session.
type SyncResult ¶
type SyncResult struct {
Elided int // artifacts deliberately absent under the store cone
// ConeServed counts the replies whose peer acknowledged applying the
// store cone (pragma cone-served, sparse-cone §6.3). The other rounds of
// a sparse store were served densely and filtered locally.
ConeServed int
// HorizonServed counts the replies whose origin served the store's
// horizon at either level (pragma horizon-served, horizon §6.2.5). The
// other rounds were served densely and classified locally (§6.3).
HorizonServed int
// StructuralServed counts the replies that served the structural level.
// HorizonFallback reports a structural store that fell back to the
// content horizon for good: its origin did not serve the structural
// level with a seed in the first reply, or the seed failed its
// provenance (§5.1, §5.2.4, §6.3).
StructuralServed int
HorizonFallback bool
Rounds, FilesSent, FilesRecvd int
UVFilesSent, UVFilesRecvd int
UVGimmesSent int
BytesSent, BytesRecvd int64
ArtifactsLinked int
Errors []string
CkinLockFail *CkinLockFail // nil = no conflict
// HorizonHistoryURL is the interface outside the protocol at which the
// origin says history below a horizon can be queried (horizon-served
// URL, §6.2.5), the last a reply named; "" when none did.
HorizonHistoryURL string
// AvoidSubmanifests reports that this exchange received pragma
// avoid-submanifests (draft-fossil-submanifests-00 §7.2). Sync makes no
// commit; this observation does not bind a later local commit.
AvoidSubmanifests bool
// MergeBaseAnswered reports that the origin answered MergeBaseOf, and
// MergeBase is its answer: the base's hash, or "" when the two share no
// ancestor.
MergeBaseAnswered bool
MergeBase string
}
SyncResult reports what happened during a sync.
func Deepen ¶ added in v0.15.0
func Deepen(ctx context.Context, r *repo.Repo, peer Transport, target string, opts SyncOpts) (*SyncResult, error)
Deepen moves the store's horizon toward target, the full hash of a check-in the store needs history down to (draft-fossil-horizon-00 §5.8), and synchronizes at the new C' through peer. C' is an ancestor of C, so K only grows: target itself when it is one, else a common ancestor of C and target, found over the graph the store holds or asked of the origin (§6.2.4). The rule, the depth and the path members stay. FetchCone does the rest: a structural store discards the seed for C before it takes the one for C', and a failed transfer puts both back.
func FetchCone ¶ added in v0.14.0
func FetchCone(ctx context.Context, r *repo.Repo, peer Transport, next *cone.Cone, opts SyncOpts) (*SyncResult, error)
FetchCone applies lasting store policy and fetches newly wanted elision with ordinary pull. The caller supplies its configured peer and credentials; this helper neither discovers endpoints nor starts a background transfer. A failed transfer leaves the widened intent in place so an ordinary retry can heal it.
Moving a structural horizon discards the held seed before anything else (draft-fossil-horizon-00 §5.8): the seed for the old C, applied at a later date than the new seed's propagation, would shadow it. A transfer that then fails puts the old cone and seed back, as zit's deepen does, since a store left structural without a seed derives no branch state for K at all.
func FetchRequiredArtifacts ¶ added in v0.14.0
func FetchRequiredArtifacts( ctx context.Context, r *repo.Repo, peer Transport, roots []string, opts SyncOpts, ) (*SyncResult, error)
FetchRequiredArtifacts resolves the S/B closure of explicitly named check-ins or sub-manifests. It does not change the store cone, discover a peer, or keep credentials. Callers must release their SQL and worktree locks before calling.
All network work precedes one atomic store. Storage-delta bases are verified in memory and only structurally reachable artifacts are retained; unsolicited file payloads and inventory never enter the repository. At most MaxRounds exchanges, maxRequiredArtifacts names and maxRequiredBytes staged bytes are permitted. A caller retries its local operation once, revalidating its state.
func FetchRequiredFiles ¶ added in v0.14.0
func FetchRequiredFiles( ctx context.Context, r *repo.Repo, peer Transport, names []string, opts SyncOpts, ) (*SyncResult, error)
FetchRequiredFiles recovers only explicitly named file payloads. The caller derives these names from a fresh operation-specific covered file set; this does not authorize widening the store cone or ordinary inventory sync.
type SyncedTable ¶
SyncedTable caches a table definition along with its metadata.
type TableSyncEnd ¶
TableSyncEnd describes the result of a table sync operation.
type TableSyncStart ¶
TableSyncStart describes the beginning of a table sync operation.
type Transport ¶
type Transport interface {
Exchange(ctx context.Context, request *xfer.Message) (*xfer.Message, error)
}
Transport sends an xfer request and returns the response. Implementations handle encoding (the request Message is already decoded), network I/O, and decoding. The xfer.Message uses zlib-compressed payloads internally — see xfer.Encode and xfer.Decode for wire format details.
Built-in implementations: HTTPTransport (Fossil HTTP /xfer protocol), MockTransport (canned responses for testing). The leaf agent adds NATSTransport for NATS-based peer-to-peer sync.
type TransportFault ¶ added in v0.8.0
type TransportFault interface {
error
// RetryableTransportFault reports whether sending the same request
// again may succeed. It is true only when no reply was interpreted, so
// a resend cannot mask a defect in one that was.
RetryableTransportFault() bool
}
TransportFault is implemented by an error that classifies itself for the retry decision. A Transport knows which stage of the exchange failed — whether bytes were still crossing the wire or a reply had already arrived and was being interpreted — and that distinction cannot be recovered reliably from the error's shape afterwards, since a truncated zlib body and a truncated wire read both surface as io.ErrUnexpectedEOF.
It is optional in the same way [FramedTransport] is: a Transport that does not implement it has its errors classified by inspection instead, which is deliberately conservative — anything not recognisably a network fault is treated as fatal.
Source Files
¶
- auth.go
- ckin_lock.go
- client.go
- client_tablesync.go
- clone.go
- config_groups.go
- doc.go
- handler.go
- handler_tablesync.go
- handler_uv.go
- horizon_cursor.go
- horizon_deepen.go
- horizon_memo.go
- horizon_mergebase.go
- horizon_name.go
- horizon_pushcheck.go
- horizon_seedsign.go
- horizon_structural.go
- horizon_verify.go
- links.go
- netfault_std.go
- nethttp_std.go
- observer.go
- receipt.go
- receipt_trace.go
- receive_budget.go
- receive_pass.go
- redirect.go
- retry.go
- seed_basis.go
- serve_http.go
- session.go
- sparse.go
- sparse_clone_carry.go
- sparse_clone_walk.go
- sparse_fetch.go
- sparse_policy.go
- sparse_required_fetch.go
- sparse_required_local.go
- sparse_walk.go
- sparse_walk_tree.go
- stubs.go
- submanifest.go
- transport.go
- transport_http.go
- unjudged.go
- version.go
- xdelete.go