Documentation
¶
Overview ¶
Package local is a storage.Storage on the local filesystem, for development and single-host deployments: objects are files under one root directory, streamed to and from disk.
Layout ¶
A key is not used as a path. Each object is stored at
<root>/objects/<h[0:2]>/<h[2:4]>/<h>
where h is the hex SHA-256 of the key. Joining the key to the root would break the storage contract on real filesystems: keys that differ only in case collide on macOS and Windows, "a" and "a/b" cannot both exist (a file cannot also be a directory), a segment may exceed the filesystem's name limit, and Windows reserves names such as "CON". Hashing makes every valid key its own file, and no key can reach outside the root.
A file holds the object's bytes followed by a trailer: a JSON header (a format version, the key, content type, metadata, modification time, and a SHA-256 ETag), its length, and a magic number. Open serves the bytes before the trailer. A reader ignores header fields it does not know, so a later version may add fields and still share a root with this one; the version changes only for a change older readers cannot read.
Writes ¶
Put streams into a temporary file, never holding the object in memory, flushes it to disk, makes every directory on the object's path durable (below), renames it into place, and flushes the directory: the rename is atomic, so a reader sees the old object or the new one, never part of one, and on Linux and macOS a Put that returned survives a crash. Delete flushes its directory too. Windows has no directory flush a process can request, so there writes and deletes are atomic but their durability is the filesystem's.
Durability is a fact about the shared root, not about one store: a directory that exists may have been created by another store (another process) that has not flushed its entry yet, or failed to. So a store trusts nothing it has not flushed itself. The root is created on the first Put, in a parent directory that must exist; its entry there is flushed before the marker file <root>/.durable is written, and a store that finds no marker flushes the parent itself. Inside the root, a store flushes the entry of every directory on an object's path, whoever created it, before its first Put into that directory.
Readers ¶
An open reader keeps the version it opened while a Put replaces the object or a Delete removes it, on every platform. On Windows the store opens objects with delete sharing and replaces and deletes them with POSIX semantics (NTFS on Windows 10 1709 or later; on a filesystem without them, FAT say, an open reader blocks both until it is closed).
Temporary files ¶
Each store writes its temporary files in its own work directory, <root>/tmp/w-*, and holds a lock on the directory's owner file for as long as the store exists. The owner file is created and locked as owner.new and only then renamed to owner, so every owner file is locked from the moment it has that name. A failed Put removes its temporary file; a process killed mid-Put cannot, but its lock ends with it, so a store's first Put removes the work directories whose owner file it can lock: those of stores that are gone. A directory without an owner file is kept (its store may be setting it up; if it died doing so, it left an empty directory), and so is a live store's, however long its Puts wait on their sources: no age or clock is involved. On a platform without file locks (flock, or LockFileEx on Windows) nothing is swept.
Index ¶
- type Option
- type Store
- func (*Store) BoundedWrites()
- func (s *Store) Delete(ctx context.Context, key string) error
- func (s *Store) List(ctx context.Context, prefix string, fn func(storage.ObjectInfo) error) error
- func (s *Store) Open(ctx context.Context, key string) (io.ReadCloser, storage.ObjectInfo, error)
- func (s *Store) Put(ctx context.Context, key string, r io.Reader, opts storage.PutOptions) (storage.ObjectInfo, error)
- func (s *Store) Root() string
- func (s *Store) Stat(ctx context.Context, key string) (storage.ObjectInfo, error)
- func (s *Store) URL(ctx context.Context, key string, opts storage.URLOptions) (string, error)
- func (s *Store) UploadURL(ctx context.Context, key string, opts storage.UploadURLOptions) (storage.UploadRequest, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Option ¶
type Option func(*Store)
Option configures a Store.
func WithClock ¶
WithClock sets the clock that stamps ObjectInfo.ModTime (default time.Now), for tests that assert on it.
func WithFirstPut ¶
WithFirstPut sets a function called with the root on the store's first Put (framework.New uses it to warn about an ephemeral root in production).
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a storage.Storage under a root directory. It is safe for concurrent use, including by several processes sharing the root.
func New ¶
New returns a store under root. root is made absolute, so the store does not follow a later change of working directory. Nothing is created until the first Put.
func (*Store) BoundedWrites ¶
func (*Store) BoundedWrites()
BoundedWrites implements storage.BoundedWriter: a Put publishes with one rename after checking its context, within the call, so nothing it wrote is published after it returns.
func (*Store) List ¶
List implements storage.Lister. Objects are stored by the hash of their key, so it reads every object file's trailer: its cost is the whole store, whatever the prefix. A damaged file (one that is not a readable object) is skipped and reported to WithWarn, so one bad file does not stop every cleanup that lists.
func (*Store) Open ¶
func (s *Store) Open(ctx context.Context, key string) (io.ReadCloser, storage.ObjectInfo, error)
Open implements storage.Storage. The object is read from disk as the caller reads it.
func (*Store) Put ¶
func (s *Store) Put(ctx context.Context, key string, r io.Reader, opts storage.PutOptions) (storage.ObjectInfo, error)
Put implements storage.Storage.
func (*Store) URL ¶
URL implements storage.Storage with the store's presign.Signer (WithURLs): a public URL for a key under its public prefix, or a signed one. Without a Signer it returns storage.ErrUnsupported (after validating its arguments).
func (*Store) UploadURL ¶
func (s *Store) UploadURL(ctx context.Context, key string, opts storage.UploadURLOptions) (storage.UploadRequest, error)
UploadURL implements storage.DirectUploader with the store's presign.Signer (WithURLs): a signed PUT that presign.Handler stores. Without a Signer it returns storage.ErrUnsupported (after validating its arguments).