wasm_snapshot_store (wasm v0.3.0)

View Source

A directory of snapshot images, keyed by what makes one valid.

Shaped on wasm_code_cache, which is the existing precedent for a build artifact kept on disk and looked up by identity: a flat directory named by app env, absent meaning off, one file per key, write to a temporary name and rename, and a total size past which the oldest go. The policy itself is wasm_file_cache, shared with that module.

application:set_env(wasm, snapshot_dir, "/var/cache/wasm/images").

Off unless that is set, and the directory is as trusted as your release. That is the same concession wasm_code_cache makes, and it carries further here: a cache entry is code, which a release already trusts, while an image is guest state laid into a live runtime. What bounds the damage is that every field is validated on the way in and a table slot may name only a function the module has, which is containment rather than authentication.

What the key covers, and the one thing it deliberately does not

An image is a runtime after init() ran against a particular environment, so the key has to cover the environment and not only the module. It is the module hash, the format's own ABI, the adapter's version, and the adapter's compatibility key -- and that last one is where a preopened directory, an argument list or a set of environment variables has to be accounted for. An adapter that leaves it undefined cannot be cached, because there would be nothing distinguishing two images captured from different worlds.

It does not cover the OTP release, the emulator flavour or the machine's architecture, all three of which wasm_code_cache:key/6 includes. That is the difference between the two artifacts rather than an oversight: a .beam is genuinely specific to an emulator, while an image is bytes, integers and indices, and one that would not load on another machine could not be moved.

Summary

Functions

Where images live, or undefined, which means the store is off.

The key an image is filed under.

The image filed under this key, for a module the caller names.

The cap in force, in bytes.

Remove every image, and every half-written one.

File an image, or do nothing.

Functions

dir()

-spec dir() -> undefined | file:filename().

Where images live, or undefined, which means the store is off.

key/4

-spec key(binary(), binary(), term(), pos_integer()) -> binary() | undefined.

The key an image is filed under.

undefined when the adapter supplied no compatibility key, which the caller must read as "do not cache this" rather than as a key of its own.

lookup/2

-spec lookup(binary() | undefined, wasm:module_()) -> {ok, wasm:snapshot()} | miss.

The image filed under this key, for a module the caller names.

Every failure is a miss. A corrupt file, one written by another build, one naming another module: none of them is an error, because the caller's answer to all of them is the same and it is to capture instead. wasm_code_cache states that contract for its own reads; this one keeps it.

max_bytes()

-spec max_bytes() -> non_neg_integer().

The cap in force, in bytes.

Resolved on every store through application:get_env/3, so a change is in force from the next one and there is nothing cached to invalidate at boot. It is reported rather than left implicit because a setting nobody can read back is a setting nobody can tell is being used. wasm_jit's resolve_max_heap_words and wasm_jit:compile_limits/0 exist for the same reason.

A value that cannot be a size -- negative, a float, an atom -- answers the default and warns once. Taking it literally would mean a cap of 0 deleting every image on the next store.

purge()

-spec purge() -> ok.

Remove every image, and every half-written one.

For tests, and for a release that wants a clean start -- and for an operator who has just lowered max_snapshot_dir_bytes and wants the directory to shrink now rather than at the next store.

store/3

-spec store(binary() | undefined, wasm:snapshot(), term()) -> ok.

File an image, or do nothing.

Answers ok whatever happens, because a store that failed is a miss next time and there is nothing a caller could usefully do about it. An unwritable directory should not fail a worker that has a perfectly good image in memory.