client

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Overview

Package client provides a standalone HTTP client for the Cachew cache server.

Index

Constants

View Source
const BundleURLHeader = "X-Cachew-Bundle-Url"

BundleURLHeader is the response header on /snapshot.tar.zst that, when present, points at a delta bundle that brings the snapshot up to the mirror's current HEAD.

View Source
const ETagKey = "ETag"

ETagKey is the HTTP header key used to store the ETag.

View Source
const SnapshotCommitHeader = "X-Cachew-Snapshot-Commit"

SnapshotCommitHeader is the response header on /snapshot.tar.zst that contains the mirror's HEAD SHA at the time the snapshot was generated. An empty value means the snapshot was served cold (no associated mirror) and the caller should freshen the working tree itself.

Variables

View Source
var ErrNotModified = errors.New("not modified")

ErrNotModified is returned when an If-None-Match precondition is satisfied, indicating the resource has not changed since the supplied ETag. Over HTTP this corresponds to 304 Not Modified.

View Source
var ErrPreconditionFailed = errors.New("precondition failed")

ErrPreconditionFailed is returned when an If-Match precondition is not met. Over HTTP this corresponds to 412 Precondition Failed.

View Source
var ErrRangeNotSatisfiable = errors.New("range not satisfiable")

ErrRangeNotSatisfiable is returned when a Range precondition cannot be satisfied against the stored object. Over HTTP this corresponds to 416 Range Not Satisfiable.

View Source
var ErrStatsUnavailable = errors.New("stats unavailable")

ErrStatsUnavailable is returned when a cache backend cannot provide statistics.

Functions

func Archive

func Archive(ctx context.Context, w io.Writer, baseDir string, includePaths []string, excludePatterns []string, threads int) error

Archive writes a tar+zstd stream of the given paths to w. Each entry in includePaths is relative to baseDir and must exist. Exclude patterns use tar's --exclude syntax. threads controls zstd parallelism; 0 uses all CPU cores.

func Extract

func Extract(ctx context.Context, r io.Reader, directory string, threads int) error

Extract decompresses a zstd+tar stream from r into directory, preserving file permissions, ownership, and symlinks. threads controls zstd parallelism; 0 uses all CPU cores.

func FormatETag added in v0.3.0

func FormatETag(etag string) (string, error)

FormatETag formats a raw ETag value as a strong HTTP ETag.

func NewHTTPClient

func NewHTTPClient(headerFunc HeaderFunc) *http.Client

NewHTTPClient creates an *http.Client that attaches headerFunc headers to every outgoing request. Useful for callers that need to talk to non-API endpoints (e.g. /git/) with the same auth as the cache client.

The returned client is instrumented with otelhttp so each outgoing request produces a child span and propagates the W3C traceparent header. When no tracer provider is configured, otelhttp falls back to a no-op tracer, so this is cost-free for callers that have not opted in to tracing.

func ParallelGet

func ParallelGet(ctx context.Context, c RangeReader, key Key, dst io.WriterAt, chunkSize int64, concurrency int) error

ParallelGet downloads an object in chunkSize-byte chunks with up to concurrency requests in flight, writing each chunk at its offset via dst.WriteAt. dst may block writes to apply backpressure (e.g. a bounded reordering buffer feeding a stream): chunk dispatch never runs more than 2*concurrency chunks past the oldest incomplete chunk, so a dst window of 2*concurrency*chunkSize is never exceeded and blocked writes always drain.

Chunks are pinned to the discovery response's ETag so a mid-download rewrite is rejected rather than spliced. No ETag, a range-ignoring backend, an object that fits in the first chunk, or concurrency 1 all fall back to a single full read. On error dst is left partially written and must be discarded by the caller.

func RawETagFromHeader added in v0.3.0

func RawETagFromHeader(etag string) (string, error)

RawETagFromHeader extracts a raw ETag value from a strong HTTP ETag header.

func ValidateNamespace

func ValidateNamespace(name string) error

ValidateNamespace checks that a namespace name is valid.

func ValidateRawETag added in v0.3.0

func ValidateRawETag(etag string) error

ValidateRawETag verifies that etag is an unquoted cache ETag value.

Types

type CacheWriter

type CacheWriter interface {
	io.WriteCloser
	// Abort discards the in-progress write and releases resources.
	// The provided error is recorded as the cause of cancellation.
	// The object MUST NOT be made available in the cache after Abort.
	Abort(err error) error
}

CacheWriter extends io.WriteCloser with the ability to abort an in-progress cache write. Exactly one of Close or Abort must be called.

Close commits the data to the cache. Abort discards the in-progress write, ensuring the object is never made visible in the cache. Both methods are idempotent after the first call.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is an HTTP client for a Cachew cache server. Its method set mirrors the cache.Cache interface, so it can be used as the transport for a remote cache backend.

func New

func New(baseURL string, headerFunc HeaderFunc) *Client

New creates a Client against the given base URL (e.g. "http://localhost:8080"). If headerFunc is non-nil, its returned headers are added to every outgoing request.

func NewWithHTTPClient

func NewWithHTTPClient(baseURL string, httpClient *http.Client) *Client

NewWithHTTPClient creates a Client against baseURL using the supplied *http.Client. Callers are responsible for configuring authentication on the supplied client (e.g. via a custom RoundTripper).

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL returns the cachew server root URL this client targets.

func (*Client) Close

func (c *Client) Close() error

Close releases resources held by the client.

func (*Client) Create

func (c *Client) Create(ctx context.Context, key Key, headers http.Header, ttl time.Duration, opts ...RequestOption) (CacheWriter, error)

Create stores a new object in the cache server. The returned CacheWriter must be closed to commit the upload. Call Abort instead of Close to discard the in-progress write and ensure the object is never made visible.

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, key Key) error

Delete removes an object from the cache server.

func (*Client) EnsureGitRefs

func (c *Client) EnsureGitRefs(ctx context.Context, repoURL string, request EnsureGitRefsRequest) (EnsureGitRefsResponse, error)

EnsureGitRefs asks the cachew server to ensure its local mirror of repoURL satisfies the request before the caller fetches. The server synchronously fetches from upstream if any requested ref is missing/stale or any requested commit is absent from its object database.

Use this before issuing a git fetch/clone against cachew when fresh refs or specific commits are required and the default ref-check rate-limit window would otherwise allow stale data to be served.

func (*Client) HTTP

func (c *Client) HTTP() *http.Client

HTTP returns the underlying HTTP client, for callers needing to talk to non-API endpoints with the same auth configuration.

func (*Client) Invalidate added in v0.3.0

func (c *Client) Invalidate(ctx context.Context, key Key) error

Invalidate evicts a stale local copy from the cache server.

func (*Client) ListNamespaces

func (c *Client) ListNamespaces(ctx context.Context) ([]string, error)

ListNamespaces requests the namespace list from the server.

func (*Client) Namespace

func (c *Client) Namespace(namespace Namespace) *Client

Namespace returns a derived client that targets the given namespace.

func (*Client) Open

func (c *Client) Open(ctx context.Context, key Key, opts ...RequestOption) (io.ReadCloser, http.Header, error)

Open retrieves an object from the cache server. Accepts optional [RequestOption]s such as IfNoneMatch for conditional requests.

func (*Client) OpenGitBundle

func (c *Client) OpenGitBundle(ctx context.Context, bundleURL string) (io.ReadCloser, error)

OpenGitBundle fetches the bundle pointed at by a BundleURL returned in a previous GitSnapshot response. The caller is responsible for writing the body to a file and applying it via `git pull`/`git fetch`.

func (*Client) OpenGitLFSSnapshot

func (c *Client) OpenGitLFSSnapshot(ctx context.Context, repoURL string) (*GitSnapshot, error)

OpenGitLFSSnapshot fetches the LFS-object snapshot for repoURL. Returns os.ErrNotExist when the server has no LFS snapshot cached.

func (*Client) OpenGitSnapshot

func (c *Client) OpenGitSnapshot(ctx context.Context, repoURL string) (*GitSnapshot, error)

OpenGitSnapshot fetches a working-tree snapshot for repoURL from cachew. The caller is responsible for extracting the returned zstd-compressed tarball (e.g. via the snapshot package). Returns os.ErrNotExist when the server has no snapshot available.

func (*Client) OpenGitSnapshotParallel added in v0.3.0

func (c *Client) OpenGitSnapshotParallel(ctx context.Context, repoURL string, chunkSize int64, concurrency int) (*GitSnapshot, error)

OpenGitSnapshotParallel downloads the working-tree snapshot for repoURL with up to concurrency concurrent range requests of chunkSize bytes each. It returns as soon as the freshen metadata (Commit, BundleURL) is available, with the download continuing in the background as the caller reads Body, so extraction overlaps the transfer.

The caller must Close the returned GitSnapshot, which cancels any in-flight download. A concurrency of 1 streams a single plain request with no buffering; a server without range support falls back to a single full download. Returns os.ErrNotExist when the server has no snapshot.

func (*Client) Restore

func (c *Client) Restore(ctx context.Context, key Key, baseDir string, opts ...RestoreOption) (bool, error)

Restore downloads the archive stored under key and extracts it into baseDir. Returns (false, nil) on cache miss so callers can populate baseDir and then Save.

func (*Client) Save

func (c *Client) Save(ctx context.Context, key Key, baseDir string, paths []string, opts ...SaveOption) error

Save archives the given paths within baseDir and uploads the tar+zstd stream under key. Any existing object at key is overwritten.

func (*Client) Stat

func (c *Client) Stat(ctx context.Context, key Key, opts ...RequestOption) (http.Header, error)

Stat retrieves headers for an object from the cache server. Accepts optional [RequestOption]s such as IfNoneMatch for conditional requests.

func (*Client) Stats

func (c *Client) Stats(ctx context.Context) (Stats, error)

Stats retrieves cache statistics from the server.

func (*Client) String

func (c *Client) String() string

String describes the client.

type EnsureGitRefsRequest

type EnsureGitRefsRequest struct {
	Refs    map[string]string `json:"refs,omitempty"`
	Commits []string          `json:"commits,omitempty"`
}

EnsureGitRefsRequest specifies what the caller wants present on the server's mirror. At least one of Refs or Commits must be non-empty.

Refs maps each required ref (e.g. "refs/heads/main") to the expected SHA; an empty SHA means "require the ref to exist at any SHA". Commits lists individual commit SHAs that must exist in the mirror's object database, regardless of which ref points at them.

type EnsureGitRefsResponse

type EnsureGitRefsResponse struct {
	Refs           map[string]string `json:"refs,omitempty"`
	MissingCommits []string          `json:"missing_commits,omitempty"`
	Fetched        bool              `json:"fetched"`
}

EnsureGitRefsResponse is the response returned by EnsureGitRefs.

Refs contains the resolved local SHA for each requested ref (empty if the ref is still missing on the server after the fetch). MissingCommits lists the requested commits that are still absent from the server's object database. Fetched reports whether the server performed an upstream fetch.

type GitSnapshot

type GitSnapshot struct {
	Body      io.ReadCloser
	Headers   http.Header
	Commit    string
	BundleURL string
}

GitSnapshot is a streaming response from OpenGitSnapshot.

Body is the zstd-compressed tarball; the caller must Close it. Commit holds the mirror's HEAD SHA at snapshot time (empty for cold serves). BundleURL, if non-empty, identifies a delta bundle that brings the snapshot up to the mirror's current HEAD; it can be passed to OpenGitBundle.

func (*GitSnapshot) Close

func (s *GitSnapshot) Close() error

Close releases the underlying response body.

type HTTPStatusError

type HTTPStatusError struct {
	StatusCode int
}

HTTPStatusError is returned when the server responds with an unexpected HTTP status code. Callers can use errors.As to inspect the status code.

func (*HTTPStatusError) Error

func (e *HTTPStatusError) Error() string

func (*HTTPStatusError) Is

func (e *HTTPStatusError) Is(target error) bool

Is allows errors.Is to match HTTPStatusError against sentinel errors.

type HeaderFunc

type HeaderFunc func() http.Header

HeaderFunc returns headers to attach to each outgoing request.

type Key

type Key [32]byte

Key represents a unique identifier for a cached object.

func HashFiles

func HashFiles(patterns ...string) (Key, error)

HashFiles returns a Key derived from the contents of all regular files matched by the given glob patterns. Patterns use doublestar syntax, so ** matches any number of path segments (e.g. "**/go.sum"). Matches are deduplicated and sorted by path, and each file's path and contents are folded into the digest so that content or path changes invalidate the key. Directories and non-regular matches are skipped. Returns an error if no regular files match any pattern, to avoid silently producing a constant key on typos.

func NewKey

func NewKey(s string) Key

NewKey returns the SHA256 of s.

func ParseKey

func ParseKey(key string) (Key, error)

ParseKey from its hex-encoded string form.

func (*Key) MarshalText

func (k *Key) MarshalText() ([]byte, error)

func (*Key) String

func (k *Key) String() string

func (*Key) UnmarshalText

func (k *Key) UnmarshalText(text []byte) error

type Namespace

type Namespace string

Namespace identifies a logical partition within a cache or metadata store. Valid names start with an alphanumeric character and contain only alphanumerics, hyphens, and underscores.

const DefaultNamespace Namespace = "default"

DefaultNamespace is used when a namespace is not explicitly specified.

func ParseNamespace

func ParseNamespace(name string) (Namespace, error)

ParseNamespace validates and returns a Namespace from a plain string.

func (*Namespace) String

func (n *Namespace) String() string

func (*Namespace) UnmarshalText

func (n *Namespace) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler with validation.

type RangeOutcome

type RangeOutcome int

RangeOutcome classifies how a Range request should be answered.

const (
	// RangeFull indicates the full representation should be served (no Range,
	// an unmatched If-Range, or an unsupported/invalid specifier).
	RangeFull RangeOutcome = iota
	// RangePartial indicates a single satisfiable byte range.
	RangePartial
	// RangeNotSatisfiable indicates the range lies outside the object.
	RangeNotSatisfiable
)

type RangeReader

type RangeReader interface {
	Open(ctx context.Context, key Key, opts ...RequestOption) (io.ReadCloser, http.Header, error)
}

RangeReader is the subset of cache operations ParallelGet needs: a conditional, Range-capable Open. Both *Client and cache.Cache satisfy it.

type RequestOption

type RequestOption func(*RequestOptions)

RequestOption configures conditional request parameters.

func IfMatch

func IfMatch(etag string) RequestOption

IfMatch sets the If-Match precondition.

func IfNoneMatch

func IfNoneMatch(etag string) RequestOption

IfNoneMatch sets the If-None-Match precondition.

func IfRange

func IfRange(etag string) RequestOption

IfRange sets the If-Range precondition: the Range is only honoured when etag matches the stored ETag, otherwise the full representation is served.

func Range

func Range(start, end int64) RequestOption

Range requests a single half-open byte range [start, end) from Open. A negative end means "to the end of the object" (its Content-Length). For example Range(0, 500) requests the first 500 bytes and Range(0, -1) the whole object. Open returns the matching bytes with a Content-Range header, or ErrRangeNotSatisfiable if the range lies outside the object.

func WithETag added in v0.3.0

func WithETag(etag string) RequestOption

WithETag sets the raw ETag value to store on Create.

type RequestOptions

type RequestOptions struct {
	// IfMatch is the If-Match precondition. Evaluation fails with
	// ErrPreconditionFailed if the stored ETag does not match.
	IfMatch string
	// IfNoneMatch is the If-None-Match precondition. Evaluation reports
	// ErrNotModified when the stored ETag matches.
	IfNoneMatch string
	// Range is a raw HTTP Range header value (e.g. "bytes=0-499"). Only a
	// single byte range is supported; multi-range or invalid specifiers are
	// ignored and the full representation is served.
	Range string
	// IfRange gates Range on the stored ETag: the range is only applied when
	// IfRange matches the stored ETag, otherwise the full representation is
	// served. Only the entity-tag form is supported.
	IfRange string
	// ETag is the raw ETag value to store on Create. It is formatted as a
	// strong HTTP ETag when carried over HTTP or stored in cache metadata.
	ETag string
	// ETagSet reports whether ETag was explicitly configured.
	ETagSet bool
}

RequestOptions holds conditional-request parameters. It is the single representation shared by the client wire protocol, the cache backends, and the server handlers.

func NewRequestOptions

func NewRequestOptions(opts ...RequestOption) RequestOptions

NewRequestOptions applies opts and returns the resulting RequestOptions.

func (RequestOptions) Check

func (o RequestOptions) Check(etag string) error

Check evaluates the preconditions against the stored ETag. It returns ErrNotModified for a satisfied If-None-Match, ErrPreconditionFailed for a failed If-Match, or nil when all preconditions pass.

func (RequestOptions) IfRangeMisses added in v0.3.0

func (o RequestOptions) IfRangeMisses(etag string) bool

IfRangeMisses reports whether a requested Range is dropped because the If-Range validator does not match etag, in which case the full representation is served in place of the range.

func (RequestOptions) ResolveRange

func (o RequestOptions) ResolveRange(size int64, etag string) (start, length int64, outcome RangeOutcome)

ResolveRange evaluates the Range/If-Range options against an object of the given size and ETag. On RangePartial it returns the [start, start+length) window to serve.

type RestoreOption

type RestoreOption interface {
	// contains filtered or unexported methods
}

RestoreOption configures Restore.

type SaveOption

type SaveOption interface {
	// contains filtered or unexported methods
}

SaveOption configures Save.

func WithExclude

func WithExclude(patterns ...string) SaveOption

WithExclude adds tar --exclude patterns applied during Save.

func WithExtraHeaders

func WithExtraHeaders(h http.Header) SaveOption

WithExtraHeaders merges additional headers into the upload request.

func WithTTL

func WithTTL(d time.Duration) SaveOption

WithTTL sets the TTL on the uploaded object. Zero (the default) uses the server default.

type SaveRestoreOption

type SaveRestoreOption interface {
	SaveOption
	RestoreOption
}

SaveRestoreOption configures both Save and Restore.

func WithZstdThreads

func WithZstdThreads(n int) SaveRestoreOption

WithZstdThreads sets zstd parallelism. Zero (the default) uses all CPU cores.

type Stats

type Stats struct {
	// Objects is the number of objects currently in the cache.
	Objects int64 `json:"objects"`
	// Size is the total size of all objects in the cache in bytes.
	Size int64 `json:"size"`
	// Capacity is the maximum size of the cache in bytes (0 if unlimited).
	Capacity int64 `json:"capacity"`
}

Stats contains health and usage statistics for a cache.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL