Documentation
¶
Overview ¶
Package objstore is one object-storage bucket with compare-and-swap: conditional PUT (PutIfAbsent, PutIfMatch) and GET (GetIfChanged), the ETag with the body, paginated listing and batch delete. Each provider is its own package, so a binary links only the SDKs it uses: package aws/s3 (AWS S3, and R2, MinIO, Ceph and Hetzner, which speak the S3 API), package gcp/gcs (Google Cloud Storage) and package fs, a durable local store with chunked write-back. Each builds a Backend and hands it to Open.
Objects are meant to be written once and never mutated except by deletion; the conditional writes cover the few that must change. ListPrefixes returns the child prefixes under a prefix (S3's delimiter), so discovering a thousand top-level names costs one request, not one per object.
Store.WithKMSKeys names a KMS key per object; S3 (SSE-KMS) and GCS (kmsKeyName) encrypt it under that key.
Package wal is a write-ahead log on a Store, package cache a memory and disk cache in front of one, package rangeread a coalescing ranged reader over the cache, package lease a single-holder lease on one key.
Index ¶
- Constants
- Variables
- func IsUrgent(ctx context.Context) bool
- func KMSKey(ctx context.Context) string
- func OpErr(op, key string, err error) error
- func Urgent(ctx context.Context) context.Context
- func WithKMSKey(ctx context.Context, id string) context.Context
- func WithTimings(ctx context.Context, t *Timings) context.Context
- type Backend
- type Call
- type Config
- type KMSKeyFunc
- type Store
- func (s *Store) CheckConditionalWrites(ctx context.Context) error
- func (s *Store) Delete(ctx context.Context, key string) error
- func (s *Store) DeleteMany(ctx context.Context, keys ...string) error
- func (s *Store) DropBucket(ctx context.Context) error
- func (s *Store) EnsureBucket(ctx context.Context) error
- func (s *Store) Get(ctx context.Context, key string) ([]byte, error)
- func (s *Store) GetIfChanged(ctx context.Context, key, etag string) ([]byte, string, bool, error)
- func (s *Store) GetRange(ctx context.Context, key string, offset, length int64) ([]byte, error)
- func (s *Store) GetWithETag(ctx context.Context, key string) ([]byte, string, error)
- func (s *Store) KMS() bool
- func (s *Store) List(ctx context.Context, prefix string) ([]string, error)
- func (s *Store) ListPage(ctx context.Context, prefix, after string, limit int) ([]string, string, error)
- func (s *Store) ListPrefixes(ctx context.Context, prefix string) ([]string, error)
- func (s *Store) ListPrefixesPage(ctx context.Context, prefix, after string, limit int) ([]string, string, error)
- func (s *Store) Put(ctx context.Context, key string, data []byte) error
- func (s *Store) PutIfAbsent(ctx context.Context, key string, data []byte) (bool, error)
- func (s *Store) PutIfMatch(ctx context.Context, key string, data []byte, etag string) (bool, error)
- func (s *Store) WithBackend(wrap func(Backend) Backend) *Store
- func (s *Store) WithKMSKeys(fn KMSKeyFunc) *Store
- type Timings
Constants ¶
const MaxListPage = 1000
MaxListPage is the largest limit ListPage and ListPrefixesPage accept, S3's own page size.
Variables ¶
var ErrAccessDenied = errors.New("store: access denied")
ErrAccessDenied is wrapped by every error for a request the store refused (403), or for an object whose KMS key is disabled, deleted or out of reach.
var ErrConflict = errors.New("store: conditional write conflict, retry")
ErrConflict is a conditional write whose outcome the backend never decided: another conditional write on the same key was in flight (S3's 409 ConditionalRequestConflict). The object may or may not have been written; the caller retries. It is never a lost race, which is (false, nil).
var ErrInvalidKey = errors.New("store: invalid key")
ErrInvalidKey is wrapped by every error for a key a backend refuses to store (the file backend rejects empty elements, "." and "..", and its own reserved file names).
var ErrNotFound = errors.New("store: object not found")
ErrNotFound is wrapped by every error for a missing object or bucket.
var ErrRange = errors.New("store: invalid object range")
ErrRange is wrapped by every error for a range outside the object, or an offset or length that is not a valid range at all.
Functions ¶
func IsUrgent ¶ added in v0.5.0
IsUrgent reports whether ctx was marked Urgent. Backends consult it for their own fast paths (the file store writes an urgent object whole).
func KMSKey ¶ added in v0.5.0
KMSKey is the KMS key id a write under ctx carries, "" for none. Backends read it; so do backend wrappers (storetest.KMS).
func OpErr ¶ added in v0.5.0
OpErr attaches the operation and the key to every error leaving a backend. A bare SDK error names neither, so a failure surfaced three layers up — mid-compaction, mid-replay, inside a query's fan-out — says only that storage was unhappy about something. %w keeps ErrNotFound unwrappable by errors.Is. nil stays nil.
func Urgent ¶
Urgent returns ctx marked as latency-critical (a lease heartbeat, a log commit, a manifest swap). Requests under it:
- skip request pacing (Config.RequestsPerSecond);
- skip the write bound (Config.MaxInflightWrites);
- on the file backend, are written whole instead of written back in chunks, and so never wait for their own write-back.
func WithKMSKey ¶ added in v0.5.0
WithKMSKey returns ctx whose writes are encrypted server-side with KMS key id: S3 SSE-KMS with that key id, GCS kmsKeyName. On a Store with a KMSKeyFunc (WithKMSKeys) the function decides; it can read id with KMSKey.
Types ¶
type Backend ¶
type Backend interface {
Put(ctx context.Context, key string, data []byte) error
PutIfAbsent(ctx context.Context, key string, data []byte) (bool, error)
Get(ctx context.Context, key string) ([]byte, error)
GetRange(ctx context.Context, key string, offset, length int64) ([]byte, error)
GetWithETag(ctx context.Context, key string) ([]byte, string, error)
GetIfChanged(ctx context.Context, key, etag string) ([]byte, string, bool, error)
PutIfMatch(ctx context.Context, key string, data []byte, etag string) (bool, error)
ListPage(ctx context.Context, prefix, after string, limit int) ([]string, string, error)
ListPrefixesPage(ctx context.Context, prefix, after string, limit int) ([]string, string, error)
Delete(ctx context.Context, key string) error
DeleteMany(ctx context.Context, keys ...string) error
EnsureBucket(ctx context.Context) error
DropBucket(ctx context.Context) error
}
Backend is the object-store contract every provider package satisfies (fs, s3, gcs); storetest.Conformance runs against each. It may grow.
Every error a backend returns is wrapped by OpErr. A failed condition is (false, nil), a missing object wraps ErrNotFound, a bad range wraps ErrRange. The ok of a conditional write is meaningful only when err == nil, except that a backend may report (true, err) when the object is visible but not proven durable (the file backend, when the directory sync after the publish fails); a retry of the same write settles it. ETags are opaque. Listing is lexical, after is exclusive, next is the last key returned when more remain and "" otherwise. The file backend reserves terminal .lock and .tmp-* files; dot-prefixed directory elements remain valid so documented namespace names work.
type Call ¶
type Call int
Call is a kind of work the store does for a caller: the file store's system calls, and the write gate every backend shares.
const ( CallGate Call = iota // waiting for a write slot (Config.MaxInflightWrites) CallCreate // creating an object's temp file (and its directory) CallWrite // write(2) of an object's temp file CallWriteBack // sync_file_range of a chunk (writeBackChunk) CallFsync // fsync of an object's temp file CallLock // the key's stripe lock CallLink // rename(2) or link(2) into place CallDirSync // fsync of a directory after a publish CallRead // an object read, whole or ranged CallList // a listing's directory walk CallDelete // unlink(2) )
type Config ¶
type Config struct {
// RequestsPerSecond paces every request to the object store; 0 = no
// pacing. A burst past what the provider serves does not fail, it
// stalls: at ~900 requests/s against Hetzner Object Storage every
// in-flight request hung for 13-15 s, the lease renewal among them, and
// the namespace was fenced. Pacing below that keeps a prefetch burst
// and the heartbeat both moving.
RequestsPerSecond float64
// MaxInflightWrites bounds the object writes and deletes in flight at
// once, except those marked Urgent (lease heartbeats, log commits,
// manifest heads); 0 = 16, negative = unbounded. A background job
// publishes thousands of objects in a burst; without a bound they queue
// ahead of the write path's own small requests on the store.
MaxInflightWrites int
}
Config is what Open adds around any backend. Provider-specific settings (endpoint, credentials, bucket-wide server-side encryption) live in each provider package's own Config.
type KMSKeyFunc ¶ added in v0.5.0
KMSKeyFunc names the KMS key an object is written with; "" writes it under the bucket's own encryption policy. It runs on every Put, PutIfAbsent and PutIfMatch, before the write takes a slot.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is one bucket of write-once objects with the OCC primitives a log and a manifest build on (PutIfAbsent to append, PutIfMatch to swap).
func Open ¶ added in v0.5.0
Open returns a Store over b with cfg's pacing, write bound and KMS keys. The provider packages (fs, s3, gcs) each have an Open that builds the backend and calls this.
func (*Store) CheckConditionalWrites ¶ added in v0.5.0
CheckConditionalWrites proves the backend honours If-None-Match: a first PutIfAbsent of a fresh _probe/ifnonematch/ key must create it and a second must not. A store that ignores the header would let two log writers both win one sequence. Three requests; call it once before writing.
func (*Store) DeleteMany ¶
DeleteMany removes keys. Missing keys are not an error. Empty input is a no-op.
func (*Store) DropBucket ¶
DropBucket deletes every object and then the bucket itself (test teardown; the harness's fresh bucket per test must not outlive the test). A bucket that is already gone is not an error.
func (*Store) EnsureBucket ¶
EnsureBucket creates the bucket if missing (dev/test convenience).
func (*Store) GetIfChanged ¶
GetIfChanged conditionally reads one revision. Empty etag is unconditional. An unchanged object returns nil bytes, its ETag, true, nil. A changed object returns its complete bytes and ETag with false. Absence is ErrNotFound; failures never report unchanged. ETags are opaque backend values.
func (*Store) GetRange ¶
GetRange reads exactly length bytes starting at offset. Short/out-of-bounds ranges fail instead of returning a plausible partial index block.
func (*Store) GetWithETag ¶
GetWithETag returns the object and its ETag for conditional replacement.
func (*Store) KMS ¶ added in v0.5.0
KMS reports whether the backend applies per-object KMS keys (WithKMSKeys). The file backend does not: it ignores them and encrypts nothing.
func (*Store) ListPage ¶
func (s *Store) ListPage(ctx context.Context, prefix, after string, limit int) ([]string, string, error)
ListPage returns at most limit (1 to MaxListPage) keys under prefix, lexically after after. next is the last returned key when another page remains, otherwise empty.
func (*Store) ListPrefixes ¶
ListPrefixes returns the immediate child prefixes under prefix — S3's CommonPrefixes with Delimiter "/", each ending in "/", lexically sorted. Discovering top-level names costs one request per page of names instead of one key per object in the bucket. A prefix appears only if at least one object lives under it, on every backend.
func (*Store) ListPrefixesPage ¶
func (s *Store) ListPrefixesPage(ctx context.Context, prefix, after string, limit int) ([]string, string, error)
ListPrefixesPage is one page of ListPrefixes: at most limit (1 to MaxListPage) prefixes lexically after after, and the cursor as in ListPage.
func (*Store) PutIfAbsent ¶
PutIfAbsent writes key only if it does not already exist. Returns false when the key existed. This is the OCC primitive a write-ahead log builds on. See Backend for the one case where ok is true alongside an error.
func (*Store) PutIfMatch ¶
PutIfMatch replaces key only if its current ETag equals etag — compare-and- swap, the manifest-swap primitive. (false, nil) = precondition failed: the object changed under us, or no longer exists. See Backend for the one case where ok is true alongside an error.
func (*Store) WithBackend ¶
WithBackend returns a Store over wrap(s's backend) with s's KMS keys and write bound: the same bucket seen through a wrapper.
func (*Store) WithKMSKeys ¶ added in v0.5.0
func (s *Store) WithKMSKeys(fn KMSKeyFunc) *Store
WithKMSKeys returns s with every write asking fn for its object's KMS key. fn decides: its answer replaces any WithKMSKey on the write's context, which fn can read with KMSKey. The store encrypts and decrypts: reads need no key, and an object whose key is disabled or revoked fails with ErrAccessDenied. The file backend ignores keys (KMS reports false).
type Timings ¶
type Timings struct {
// contains filtered or unexported fields
}
Timings sums the wall time the store spent in each Call for the calls made under one context (WithTimings), and how many there were: a background job can log its own. Concurrent calls add up, so the sum can pass the span's wall time. The file store fills every Call; S3 only the gate.
Directories
¶
| Path | Synopsis |
|---|---|
|
aws
|
|
|
s3
Package s3 is the S3 backend for objstore.
|
Package s3 is the S3 backend for objstore. |
|
Package cache is a process's read path to immutable objects in a Store: a striped in-memory byte cache (ByteCache) with decoded values riding on their bytes, an optional disposable disk tier with 4 KiB block checksums (Disk), one gate bounding concurrent store GETs, and one singleflight across concurrent misses of a key (Cache).
|
Package cache is a process's read path to immutable objects in a Store: a striped in-memory byte cache (ByteCache) with decoded values riding on their bytes, an optional disposable disk tier with 4 KiB block checksums (Disk), one gate bounding concurrent store GETs, and one singleflight across concurrent misses of a key (Cache). |
|
Package fs is the file backend for objstore: one bucket as a directory of write-once files.
|
Package fs is the file backend for objstore: one bucket as a directory of write-once files. |
|
gcp
|
|
|
gcs
Package gcs is the Google Cloud Storage backend for objstore.
|
Package gcs is the Google Cloud Storage backend for objstore. |
|
Package lease is a single-holder lease on one object in a Store: leader election, or a single writer, on S3 or any store with conditional PUT.
|
Package lease is a single-holder lease on one object in a Store: leader election, or a single writer, on S3 or any store with conditional PUT. |
|
Package rangeread reads immutable objects by byte range through a cache.Cache: Plan unions and coalesces the extents of one read stage under gap, extra-byte and range-size limits, and Reader.FetchRanges runs the plan on bounded workers, publishing each requested child in the returned context (cache.Scoped) and caching it by its logical key.
|
Package rangeread reads immutable objects by byte range through a cache.Cache: Plan unions and coalesces the extents of one read stage under gap, extra-byte and range-size limits, and Reader.FetchRanges runs the plan on bounded workers, publishing each requested child in the returned context (cache.Scoped) and caching it by its logical key. |
|
Package storetest provides provider-free test helpers: a fault-injecting, metering wrapper around a Store, and Conformance, the suite every backend passes.
|
Package storetest provides provider-free test helpers: a fault-injecting, metering wrapper around a Store, and Conformance, the suite every backend passes. |
|
bucket
Package bucket opens a fresh test bucket on the file backend or, with OBJSTORE_TEST_S3, on S3.
|
Package bucket opens a fresh test bucket on the file backend or, with OBJSTORE_TEST_S3, on S3. |
|
Package wal is a write-ahead log in object storage: ordered, immutable pages sequenced by conditional PUT.
|
Package wal is a write-ahead log in object storage: ordered, immutable pages sequenced by conditional PUT. |