The block store protocol defines the methods necessary for implementing a block storage backend. The interface is simple enough to map to many different kinds of storage, and the library comes with basic in-memory and file-based stores.
There are a few questions you need to answer if you decide to implement a block store.
Will your store return loaded or lazy blocks? The former requires pulling the block data into memory when fetching the block, while the latter only fetches the data when the block's content is read.
Different choices make sense for different backends; for example, a persistent store backed by cloud storage can usually depend on the data being there later, so lazy blocks are much more efficient. For a store based on a remote cache, it would be better to pull the block content on fetch because the data might be evicted before the block is read.
The block store methods are intended to be asynchronous, to enable efficient
usage in concurrent environments. This project uses the excellent
manifold library for an adaptable async
framework. If there's no underlying asynchronous system to tie into in the
storage backend, you can just wrap the synchronous calls in
manifold.deferred/future
to put them on a thread-pool.
Blocks support Clojure metadata, so any additional information the store needs to communicate can be added to the blocks as metadata. This intentionally doesn't affect block equality or semantics, but can be useful for introspection.
For example, a common piece of useful metadata is a URI for the concrete location of the block's data in the backing store.
Block stores must implement five methods.
The list
method enumerates the blocks contained in the store as a
Manifold stream,
ordered by the blocks' multihash identifier.
A few simple query criteria are supported to return ranges of blocks whose id uses a specific algorithm, or falls before or after some given hex markers. The implementation of this method must return at least the blocks which match the query options, and should optimize the results by omitting unmatched blocks when possible.
In practice, when this method is called the store should spin up an asynchronous
process (usually a thread via d/future
) that interrogates the storage layer
and emits the resulting blocks on an output stream, which is returned to the
caller. This stream may be closed preemptively if the consumer is done, which
should cleanly terminate the listing process.
If the listing encounters an exception, the error should be placed on the stream and the stream should be closed to indicate no further blocks will be coming. Consumers must handle exceptions propagated on the stream in this fashion.
The stat
method is used to return metadata about a block if the store contains
it. This should return a deferred which yields the info, or nil
if the store
does not contain the requested block.
This is very similar to get
, but returns a regular Clojure map instead of
a block. This map should have the same :id
, :size
, and :stored-at
values
as well as the same metadata a block returned from get
would. Conceptually,
this is similar to the HEAD
vs GET
verbs in HTTP.
This distinction is useful for stores which return loaded blocks, like a cache -
using stat
allows the caller to avoid the extra IO for block content which
will not be used.
This method fetches a block from the store. This should return a deferred which yields the block, or nil if not present.
Putting a block persists it into the store. This should return a deferred which yields the stored block. If the store already contains this block, then the implementation should avoid re-storing or other data transfer to the storage layer, and return the already-stored block directly.
Deleting a block removes it from the store. This should return a deferred which yields true if the block was stored, false if it was not.
Stores may optionally add support for the ErasableStore
protocol, which
provides an efficient mechanism for completely removing the backend data. If not
implemented, the erase!
function will fall back to listing and deleting all
blocks.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close