Slap.SlateDB.ObjectStore (slap_slatedb v0.2.0)

Copy Markdown View Source

Plain objects in the store a database lives in: small objects with conditional writes (get/3, put/4), such as leases (see Slap.Cluster.Strategy.ObjectLease), and large ones streamed in and out (upload/4, download/3), such as file bodies. Every call takes the :timeout option (see Slap.SlateDB); for a stream it bounds each step.

{:ok, store} = Slap.SlateDB.ObjectStore.open("_cluster", store: {:url, "s3://bucket/prefix"})
{:ok, version} = Slap.SlateDB.ObjectStore.put(store, "leases/0", "a", mode: :create)
{:ok, {"a", ^version}} = Slap.SlateDB.ObjectStore.get(store, "leases/0")
{:error, :conflict} = Slap.SlateDB.ObjectStore.put(store, "leases/0", "b", mode: :create)
{:ok, _} = Slap.SlateDB.ObjectStore.put(store, "leases/0", "b", mode: {:update, version})

Conditional updates ({:update, version}, an If-Match PUT) need a store that supports them: S3 and S3-compatible stores such as RustFS, Azure, GCS, and :memory. The local file system only supports :create, and returns {:error, :unsupported} for an update.

An atom error means the store refused a condition or does not support it; %Slap.SlateDB.Error{} means the operation failed.

Summary

Types

t()

An object's version: opaque, compare it only for equality.

Functions

Deletes an object. Deleting one that does not exist is not an error.

Opens the object at key for reading. Returns {:ok, {chunks, size, version}}, where chunks is a lazy stream of binaries (reading it raises Slap.SlateDB.Error if the store fails) and size is the size of the whole object, or {:ok, nil} if there is no object.

The object's body and version, or {:ok, nil} if there is none.

The keys under prefix, in no particular order.

Opens store (as for Slap.SlateDB.open/2). Keys are relative to path within it (and to the prefix of a store URL). The :store option is required.

Writes an object. :mode is

Writes the object at key from chunks, an enumerable of binaries, and returns {:ok, version}. Nothing is visible at key until the whole body is written.

Types

t()

@opaque t()

version()

@opaque version()

An object's version: opaque, compare it only for equality.

Functions

delete(object_store, key, opts \\ [])

@spec delete(t(), String.t(), keyword()) :: :ok | {:error, Slap.SlateDB.Error.t()}

Deletes an object. Deleting one that does not exist is not an error.

download(object_store, key, opts \\ [])

@spec download(t(), String.t(), keyword()) ::
  {:ok, {Enumerable.t(), non_neg_integer(), version()} | nil}
  | {:error, Slap.SlateDB.Error.t()}

Opens the object at key for reading. Returns {:ok, {chunks, size, version}}, where chunks is a lazy stream of binaries (reading it raises Slap.SlateDB.Error if the store fails) and size is the size of the whole object, or {:ok, nil} if there is no object.

range: {first, last} reads only the bytes from first to last, inclusive, as an HTTP range does. A last past the end of the object reads to its end. A range that starts past the end returns an error whose form depends on the store, so check the range against the object's size first.

get(object_store, key, opts \\ [])

@spec get(t(), String.t(), keyword()) ::
  {:ok, {binary(), version()} | nil} | {:error, Slap.SlateDB.Error.t()}

The object's body and version, or {:ok, nil} if there is none.

list(object_store, prefix, opts \\ [])

@spec list(t(), String.t(), keyword()) ::
  {:ok, [String.t()]} | {:error, Slap.SlateDB.Error.t()}

The keys under prefix, in no particular order.

open(path, opts)

@spec open(String.t(), keyword()) :: {:ok, t()} | {:error, Slap.SlateDB.Error.t()}

Opens store (as for Slap.SlateDB.open/2). Keys are relative to path within it (and to the prefix of a store URL). The :store option is required.

A :memory store is new and empty each time it is opened.

put(object_store, key, body, opts \\ [])

@spec put(t(), String.t(), binary(), keyword()) ::
  {:ok, version()} | {:error, :conflict | :unsupported | Slap.SlateDB.Error.t()}

Writes an object. :mode is:

  • :overwrite (default) - unconditionally.
  • :create - only if there is no object at key.
  • {:update, version} - only if the object is still at version.

Returns {:ok, version}, {:error, :conflict} when the condition does not hold, or {:error, :unsupported} when the store cannot check it.

upload(store, key, chunks, opts \\ [])

@spec upload(t(), String.t(), Enumerable.t(), keyword()) ::
  {:ok, version()} | {:error, Slap.SlateDB.Error.t()}

Writes the object at key from chunks, an enumerable of binaries, and returns {:ok, version}. Nothing is visible at key until the whole body is written.

A body smaller than one part (5 MiB) is written with a single PUT; a larger one as a multipart upload, a few parts at a time, which is aborted if chunks raises or the upload fails. A process that dies during an upload leaves its parts behind until the store expires them (S3 needs a lifecycle rule for incomplete multipart uploads).