View Source ProcessHub.Storage.RemoteManifest behaviour (ProcessHub v0.7.0)

Behaviour for off-cluster declared-list storage.

Experimental

The remote manifest is part of the experimental declared-children feature and may change in future releases.

A hub configured with auto_recovery: [remote_manifest: {module, opts}] ships every declared-list version to module asynchronously after the local commit (leader-only, retried with backoff, coalescing superseded versions) and consults it on boot: the higher version wins, whichever side holds it. The remote copy is what survives the loss of every cluster disk — it is a backup consulted at the edges, never a synchronous dependency of any command.

Built-in adapters: ProcessHub.Storage.RemoteManifest.LocalPath (dependency-free filesystem path) and ProcessHub.Storage.RemoteManifest.S3 (behind the optional :ex_aws_s3 dependency). Any module implementing this behaviour can be configured instead; the shared contract test suite under test/support verifies an implementation.

Contract

  • store/4 MUST NOT overwrite a stored copy whose version is higher than the one being written, on backends that can express the check — a stale leader must not clobber a newer copy. Writing the version already stored is an idempotent :ok.
  • fetch/2 returns the stored {version, blob}, :not_found when the backend holds no copy for the hub, or {:error, reason} when it cannot tell.
  • info/1 returns a descriptive map for diagnostics.
  • The optional validate_config/1 runs at hub start; returning {:error, reason} fails the start with a clear error (e.g. a missing optional dependency).

The blob is opaque to the adapter; it stores and returns it byte-identical.

Summary

Callbacks

Returns the stored manifest for hub_id.

Returns a descriptive map about the adapter and its target.

Stores blob as the manifest for hub_id at version.

Optional start-time configuration check; an error fails hub start.

Functions

Decodes a blob returned by an adapter back into a manifest map, validating its shape. Format acceptance is the reader's decision, not the codec's.

Encodes a declared-list manifest into the blob adapters store and return.

Validates a remote_manifest: configuration value at hub start.

Callbacks

@callback fetch(hub_id :: atom(), opts :: keyword()) ::
  {:ok, {version :: pos_integer(), blob :: binary()}}
  | :not_found
  | {:error, term()}

Returns the stored manifest for hub_id.

@callback info(opts :: keyword()) :: map()

Returns a descriptive map about the adapter and its target.

Link to this callback

store(hub_id, version, blob, opts)

View Source
@callback store(
  hub_id :: atom(),
  version :: pos_integer(),
  blob :: binary(),
  opts :: keyword()
) :: :ok | {:error, term()}

Stores blob as the manifest for hub_id at version.

MUST refuse (with :ok, treating it as superseded, or {:error, term}) to replace a stored copy carrying a higher version where the backend can express the check.

Link to this callback

validate_config(opts)

View Source (optional)
@callback validate_config(opts :: keyword()) :: :ok | {:error, term()}

Optional start-time configuration check; an error fails hub start.

Functions

@spec decode(binary()) :: {:ok, map()} | {:error, :malformed_remote_manifest}

Decodes a blob returned by an adapter back into a manifest map, validating its shape. Format acceptance is the reader's decision, not the codec's.

@spec encode(map()) :: binary()

Encodes a declared-list manifest into the blob adapters store and return.

@spec validate({module(), keyword()} | nil) :: :ok | {:error, term()}

Validates a remote_manifest: configuration value at hub start.

Returns :ok for nil. For {module, opts} the module must be loadable and implement the behaviour; its own validate_config/1 runs when exported, so an adapter whose optional dependency is absent fails with an error naming it.