jepsen.fs-cache

Some systems Jepsen tests are expensive or time-consuming to set up. They might involve lengthy compilation processes, large packages which take a long time to download, or allocate large files on initial startup.

Other systems require state which persists from run to run–for instance, there might be an expensive initial cluster join process, and you might want to perform that process once, save the cluster’s state to disk before testing, and for subsequent tests redeploy that state to nodes to skip the cluster join.

This namespace provides a persistent cache, stored on the control node’s filesystem, which is suitable for strings, data, or files. It also provides a basic locking mechanism.

Cached values are referred to by logical paths: a vector of strings, keywords, numbers, booleans, etc; see the Encode protocol for details. For instance, a cache path could be any of

:hi 1 true :foodb :license :key :foodb “1523a6b”

Cached paths can be stored and retrieved in several ways. Each storage type has a pair of functions: (cache/save-string! path “foo”) writes “foo” to path, and (cache/load-string path) returns the stored value as a string.

  • As strings (save-string!, load-string)
  • As EDN data (save-edn!, load-edn)
  • As raw File objects (save-file!, load-file)
  • As remote files (save-remote!, deploy-remote!)

In general, writers are (save-format! from-source to-path), and return from-source, allowing them to be used transparently for side effects. Readers are (load-format path), and return nil if the value is uncached. You can use (cached? path) to distinguish between a missing cache value and a present nil.

Writes to cache are atomic: a temporary file will be written to first, then renamed into its final cache location.

You can acquire locks on any cache path, whether it exists or not, using (locking path ...).

atomic-move!

(atomic-move! f1 f2)

Attempts to move a file atomically, even across filesystems.

cached?

(cached? path)

Do we have the given path cached?

clear!

(clear!)(clear! path)

Clears the entire cache, or a specified path.

deploy-remote!

(deploy-remote! cache-path remote-path)

Deploys a cached path to the given remote path (a string). Deletes remote path first. Creates parents if necessary. Returns remote path.

dir

Top-level cache directory.

dir-prefix

What string do we prefix to directories in cache path filenames, to distinguish /foo from /foo/bar?

Encode

protocol

members

encode-path-component

(encode-path-component component)

Encodes datatypes to strings which can be used in filenames. Use escape to escape slashes.

escape

(escape string)

Escapes slashes in filenames.

file

(file path)

The local File backing a given path, whether or not it exists.

file!

(file! path)

Like file, but ensures parents exist.

file-prefix

What string do we prefix to files in cache path filenames, to distinguish /foo from /foo/bar?

fs-path

(fs-path path)

Takes a cache path, and returns a sequence of filenames for that path.

load-edn

(load-edn path)

Reads the given cache path as an EDN structure, returning data. Returns nil if file does not exist.

load-file

(load-file path)

The local File backing a given path. Returns nil if no file exists.

load-string

(load-string path)

Returns the cached value for a given path as a string, or nil if uncached.

locking

macro

(locking path & body)

Acquires a lock for a particular cache path, and evaluates body. Helpful for reducing concurrent evaluation of expensive cache misses.

locks

save-edn!

(save-edn! data path)

Writes the given data structure to an EDN file. Returns data.

save-file!

(save-file! file path)

Caches a File object to the given path. Returns file.

save-remote!

(save-remote! remote-path cache-path)

Caches a remote path (a string) to a cache path by SCPing it down. Returns remote-path.

save-string!

(save-string! string path)

Caches the given string to a cache path. Returns string.

write-atomic!

macro

(write-atomic! [tmp-sym final] & body)

Writes a file atomically. Takes a binding form and a body, like so

(write-atomic tmp-file (io/file “final.txt”) (write! tmp-file))

Creates a temporary file, and binds it to tmp-file. Evals body, presumably modifying tmp-file in some way. If body terminates normally, renames tmp-file to final-file. Ensures temp file is cleaned up.