Documentation
¶
Overview ¶
Package blob defines a read-write, path-addressed object store with conditional writes -- the seam the dynamic tier uses to hold BIBFRAME grains in S3-compatible storage. It is the fuller sibling of storage.Sink: Sink stays write-only for build pipelines; Store adds Get/List/Delete and ETag-based optimistic concurrency for read-modify-write editorial publishing. Implementations here are stdlib-only (local directory, memory); cloud stores (S3/R2/MinIO) implement the same interface in their own packages so their SDKs never reach the core.
Index ¶
- Variables
- func PutStream(ctx context.Context, s Store, path string, r io.Reader, opts PutOptions) (string, error)
- func ReaderAt(ctx context.Context, s Store, path string) (io.ReaderAt, int64, string, error)
- func SinkOf(s Store) storage.Sink
- func ValidatePath(path string) error
- type DirStore
- func (d *DirStore) Delete(ctx context.Context, path string) error
- func (d *DirStore) Get(ctx context.Context, path string) ([]byte, string, error)
- func (d *DirStore) GetRange(ctx context.Context, path string, offset, length int64) ([]byte, error)
- func (d *DirStore) List(ctx context.Context, prefix string) iter.Seq2[Entry, error]
- func (d *DirStore) Put(ctx context.Context, path string, data []byte, opts PutOptions) (string, error)
- func (d *DirStore) PutStream(ctx context.Context, path string, r io.Reader, opts PutOptions) (string, error)
- type Entry
- type MemStore
- func (m *MemStore) Delete(ctx context.Context, path string) error
- func (m *MemStore) Get(ctx context.Context, path string) ([]byte, string, error)
- func (m *MemStore) GetRange(ctx context.Context, path string, offset, length int64) ([]byte, error)
- func (m *MemStore) List(ctx context.Context, prefix string) iter.Seq2[Entry, error]
- func (m *MemStore) Put(ctx context.Context, path string, data []byte, opts PutOptions) (string, error)
- type PutOptions
- type RangeReader
- type Signer
- type Store
- type StreamPutter
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("blob: not found")
ErrNotFound reports that no object exists at the requested path.
var ErrPreconditionFailed = errors.New("blob: precondition failed")
ErrPreconditionFailed reports that a conditional Put lost its race: the object's current state does not satisfy the request's IfMatch/IfNoneMatch. Callers recover by re-reading the object and retrying.
var ErrReadOnly = errors.New("blob: store is read-only")
ErrReadOnly reports a write attempted against a read-only store.
Functions ¶
func PutStream ¶
func PutStream(ctx context.Context, s Store, path string, r io.Reader, opts PutOptions) (string, error)
PutStream writes r to path through the store's streaming capability when present, and falls back to buffering the reader into a plain Put (memory stores hold the payload either way; wrappers without the capability keep their Put semantics, e.g. read-only rejection).
func ReaderAt ¶ added in v0.27.0
ReaderAt adapts one object to io.ReaderAt: ranged reads through the store's RangeReader capability when present, else one whole-object Get held in memory. The returned size is the object's current size; callers that cache derived state should key it by the returned ETag.
func SinkOf ¶
SinkOf adapts a Store to the write-only storage.Sink so existing build-side call sites (grain and artifact writers) can target any Store unchanged. Writes are buffered and stored unconditionally on Close.
func ValidatePath ¶
ValidatePath rejects paths that are empty, absolute, or contain empty, ".", or ".." segments, so no Store implementation can be walked outside its root.
Types ¶
type DirStore ¶
type DirStore struct {
// contains filtered or unexported fields
}
DirStore is a Store backed by a local directory tree -- the dev/test and self-host default. ETags are the sha256 of the object's content. Conditional writes are enforced under a process-local mutex, which makes them exact within one process and best-effort against concurrent external writers; production multi-writer deployments use an object store with native conditional PUTs.
Computed ETags are cached against each file's (mtime, size), so List stats rather than reads files whose cache entry is current -- the same best-effort stance as the CAS: an external writer that preserves both mtime and size can serve a stale tag until the next content read.
func (*DirStore) GetRange ¶ added in v0.27.0
GetRange implements the RangeReader capability: [offset, offset+length) of the file, clamped to its end.
func (*DirStore) List ¶
List yields entries under prefix in lexicographic path order. Prefixes are plain string prefixes over slash-separated paths (as in object stores), not directory boundaries. The walk is scoped to the deepest directory the prefix names, files whose cached ETag is current are stat'd rather than read, and a file deleted mid-walk is skipped instead of truncating the listing.
func (*DirStore) Put ¶
func (d *DirStore) Put(ctx context.Context, path string, data []byte, opts PutOptions) (string, error)
Put writes the object subject to opts' preconditions, creating parent directories as needed. The write is a temp-file rename, so readers never observe a partial object.
func (*DirStore) PutStream ¶
func (d *DirStore) PutStream(ctx context.Context, path string, r io.Reader, opts PutOptions) (string, error)
PutStream implements blob.StreamPutter: the payload streams straight into the temp file (hashed as it copies), so only the copy buffer is held in memory. The copy runs outside the store lock; preconditions are checked and the rename performed under it, same as Put.
type MemStore ¶
type MemStore struct {
// contains filtered or unexported fields
}
MemStore is an in-memory Store with exact conditional-write semantics -- the reference implementation for tests and local development.
func (*MemStore) GetRange ¶ added in v0.27.0
GetRange implements the RangeReader capability: [offset, offset+length) of the object, clamped to its end.
type PutOptions ¶
type PutOptions struct {
IfMatch string
IfNoneMatch bool
ContentType string
ContentEncoding string
}
PutOptions carries the preconditions and metadata for a Put. IfMatch and IfNoneMatch are mutually exclusive: IfMatch (non-empty) requires the object to exist with exactly that ETag (a missing object fails the precondition); IfNoneMatch requires that no object exists at the path (create-only). ContentType is advisory; stores without metadata ignore it.
ContentEncoding is advisory in the same way, and describes the bytes actually stored: "gzip" means the object holds a gzip stream that a client is expected to decompress transparently, with ContentType naming what it decompresses to. It matters because a presigned URL hands the object straight to a browser (see export.Service.DownloadURL), so the metadata, not the serving code, is what tells that browser how to read it.
type RangeReader ¶ added in v0.27.0
type RangeReader interface {
GetRange(ctx context.Context, path string, offset, length int64) ([]byte, error)
}
RangeReader is an optional Store capability: read [offset, offset+length) of an object without fetching the whole blob (range-served vocabulary index artifacts). Implementations return exactly length bytes unless the range runs past the object's end, where they return the available prefix (io.ReaderAt semantics come from the ReaderAt adapter, not from GetRange itself). Reads of a missing object return ErrNotFound.
type Signer ¶
type Signer interface {
SignedGetURL(ctx context.Context, path string, ttl time.Duration) (string, error)
}
Signer is an optional Store capability: a time-limited, unauthenticated download URL for an object (S3 presigned GETs). Stores without a native equivalent simply do not implement it; callers must type-assert and fall back to serving the bytes themselves.
type Store ¶
type Store interface {
// Get returns the object's content and current ETag, or ErrNotFound.
Get(ctx context.Context, path string) (data []byte, etag string, err error)
// Put writes the object subject to opts' preconditions and returns the
// new ETag. Violated preconditions return ErrPreconditionFailed.
Put(ctx context.Context, path string, data []byte, opts PutOptions) (etag string, err error)
// List yields entries whose path starts with prefix, in lexicographic
// path order.
List(ctx context.Context, prefix string) iter.Seq2[Entry, error]
// Delete removes the object, or returns ErrNotFound.
Delete(ctx context.Context, path string) error
}
Store is a path-addressed object store. Paths are relative, slash-separated, and must not contain empty, ".", or ".." segments. ETags are opaque strings that change whenever an object's content changes; equal content need not yield equal ETags across different Store implementations.
type StreamPutter ¶
type StreamPutter interface {
PutStream(ctx context.Context, path string, r io.Reader, opts PutOptions) (etag string, err error)
}
StreamPutter is an optional Store capability: write an object from a reader without holding the whole payload in memory ( full-corpus exports and vocabulary snapshot installs are output-sized). DirStore streams into its temp file; the S3 store spools to a local temp file for a seekable upload body. Use the PutStream helper rather than type-asserting directly.