fabric/store/backend

The store port: what a backend implements to keep Fabric’s runs.

Most applications use fabric/store.in_memory, fabric/store.directory or fabric_postgres; this module is for writing another backend, and fabric/store/conformance checks one.

A run is one encoded record (versioned JSON, see fabric docs) with a revision. A backend supplies three functions over encoded records, and Fabric writes only through insert and compare_and_set, so two owners can never both advance the same revision.

The backend contract:

Leases. A leased backend (LeasedBackend) keeps, next to each run’s record, a lease: an owner and an expiry judged by the backend’s own clock. Several nodes sharing one database use it to agree on which node drives a run; the lease never authorizes a write, which the revision alone decides. Its contract, over the one above:

fabric/store/conformance.checks checks a leased backend against this contract; fabric/store/conformance.memory is one kept in memory, for tests.

Types

A run’s latest record in a leased backend, with its revision and lease.

pub type Current {
  Current(revision: Int, record: String, holder: Holder)
}

Constructors

  • Current(revision: Int, record: String, holder: Holder)

Who holds a run’s lease in a leased backend.

pub type Holder {
  Free
  Held(owner: String, live: Bool)
}

Constructors

  • Free
  • Held(owner: String, live: Bool)

    live: the lease’s expiry, judged by the backend’s clock, is still ahead.

What a write in a leased backend does to the run’s lease, in the same atomic step as the record (see Leases). ttl is in milliseconds, from the backend’s clock.

pub type Lease {
  Hold(owner: String)
  Claim(owner: String, ttl: Int)
  Seize(owner: String, ttl: Int)
  Release
}

Constructors

  • Hold(owner: String)

    Only while owner holds the lease, live or expired; the lease is unchanged.

  • Claim(owner: String, ttl: Int)

    Only while the lease is free, held by owner, or expired; owner then holds it for ttl.

  • Seize(owner: String, ttl: Int)

    Whoever holds the lease; owner then holds it for ttl.

  • Release

    Whoever holds the lease; it is then free.

The functions of a leased backend over encoded records (see Leases).

pub type LeasedBackend {
  LeasedBackend(
    now: fn() -> Result(Int, StoreError),
    get: fn(String) -> Result(Current, StoreError),
    insert: fn(String, String, Lease) -> Result(Nil, StoreError),
    compare_and_set: fn(String, Int, String, Lease) -> Result(
      Nil,
      StoreError,
    ),
    renew: fn(String, List(String), Int) -> Result(
      List(String),
      StoreError,
    ),
    claim_expired: fn(String, Int, Int) -> Result(
      List(String),
      StoreError,
    ),
    claim_ready: fn(String, Int, Int) -> Result(
      List(String),
      StoreError,
    ),
  )
}

Constructors

  • LeasedBackend(
      now: fn() -> Result(Int, StoreError),
      get: fn(String) -> Result(Current, StoreError),
      insert: fn(String, String, Lease) -> Result(Nil, StoreError),
      compare_and_set: fn(String, Int, String, Lease) -> Result(
        Nil,
        StoreError,
      ),
      renew: fn(String, List(String), Int) -> Result(
        List(String),
        StoreError,
      ),
      claim_expired: fn(String, Int, Int) -> Result(
        List(String),
        StoreError,
      ),
      claim_ready: fn(String, Int, Int) -> Result(
        List(String),
        StoreError,
      ),
    )

    Arguments

    now

    UTC Unix milliseconds from the backend’s lease/discovery clock.

    insert

    insert(run, record, lease).

    compare_and_set

    compare_and_set(run, expected, record, lease).

    renew

    renew(owner, runs, ttl): returns the runs renewed, those whose lease owner holds live.

    claim_expired

    claim_expired(owner, ttl, limit): returns the runs claimed.

    claim_ready

    Claims free runs with a changed dependency, due observation or deadline. Atomically save the observed key, dependency revision and backend claim time with its lease. Claims change no execution revision or retention age.

What a backend function reports. Unavailable is any failure other than the outcomes the contract names; its outcome is unknown for a write.

pub type StoreError {
  NotFound
  AlreadyExists
  Conflict(current: Int)
  LeaseRefused(holder: Holder)
  Unavailable(reason: String)
}

Constructors

  • NotFound
  • AlreadyExists
  • Conflict(current: Int)

    The expected revision is no longer current.

  • LeaseRefused(holder: Holder)

    A leased backend refused the write’s lease condition (see Leases): holder is the run’s lease as the backend found it. Nothing was written.

  • Unavailable(reason: String)
pub type Stored {
  Stored(revision: Int, record: String)
}

Constructors

  • Stored(revision: Int, record: String)
✨ Search Document