Bedrock.DataPlane.Materializer.Basalt.MultiVersionConcurrencyControl (bedrock v0.5.2)

View Source

Multi-Version Concurrency Control (MVCC) is a concurrency control method that allows for multiple versions of a key to exist in the same table. This module provides an implementation of MVCC for Basalt.

Summary

Functions

Apply a single transaction to the given table, atomically. Returns :ok if the transaction was applied successfully.

Apply a series of transactions to the given table and return the id of the last transaction applied. Transaction IDs must be ever-increasing. Though each transaction is applied atomically, it's possible that reads will be interleaved between them.

Lookup the value for the given key/version. The exact version, or the next- oldest will be returned. Values for the key newer than the given version will not be considered. If no suitable keys are found, then {:error, :not_found} will be returned.

Store a key/value pair in the table, at the given version. If the key already exists, then nothing will happen. This value will be returned by subsequent calls to lookup/3, but will never be returned as part of a snapshot. This is useful for caching values that are (possibly expensive) to retrieve from permanent storage.

Get the last transaction version performed on the table. If no transaction has been performed then nil is returned.

Get the oldest possible transaction that can be read by the system. All transactions prior to this will have been coalesced.

Purge all keys/versions (and values) that are older than the given version.

Build a new transaction that encompasses only the latest writes for each key in the table, using the latest version as the cutoff. Since the latest transaction version is updated atomically alongside the transaction values, it's guaranteed that the generated transaction will include all writes that have been applied up until that point, and none that have been applied after.

Types

t()

@opaque t()

Functions

apply_one_transaction!(mvcc, encoded_transaction)

@spec apply_one_transaction!(mvcc :: t(), Bedrock.DataPlane.Transaction.encoded()) ::
  :ok

Apply a single transaction to the given table, atomically. Returns :ok if the transaction was applied successfully.

apply_transactions!(mvcc, encoded_transactions)

@spec apply_transactions!(
  mvcc :: t(),
  transactions :: [Bedrock.DataPlane.Transaction.encoded()]
) :: Bedrock.version()
@spec apply_transactions!(t(), [Bedrock.DataPlane.Transaction.encoded()]) :: :ok

Apply a series of transactions to the given table and return the id of the last transaction applied. Transaction IDs must be ever-increasing. Though each transaction is applied atomically, it's possible that reads will be interleaved between them.

If any of the transactions fails to apply, an exception will be raised.

close(mvcc)

@spec close(pkv :: t()) :: :ok

fetch(mvcc, key, version)

@spec fetch(mvcc :: t(), Bedrock.key(), Bedrock.version()) ::
  {:ok, Bedrock.value()} | {:error, :not_found}
@spec fetch(t(), Bedrock.key(), Bedrock.version()) :: Bedrock.value() | :not_found

Lookup the value for the given key/version. The exact version, or the next- oldest will be returned. Values for the key newer than the given version will not be considered. If no suitable keys are found, then {:error, :not_found} will be returned.

This is useful for providing a consistent view of the data at a given point in the transaction timeline.

insert_read(mvcc, key, version, value)

@spec insert_read(
  mvcc :: t(),
  Bedrock.key(),
  Bedrock.version(),
  Bedrock.value() | nil
) :: :ok
@spec insert_read(t(), Bedrock.key(), Bedrock.version(), Bedrock.value() | nil) :: :ok

Store a key/value pair in the table, at the given version. If the key already exists, then nothing will happen. This value will be returned by subsequent calls to lookup/3, but will never be returned as part of a snapshot. This is useful for caching values that are (possibly expensive) to retrieve from permanent storage.

No error checking is performed on the version.

new(otp_name, version)

@spec new(otp_name :: atom(), Bedrock.version()) :: t()

newest_version(mvcc)

@spec newest_version(mvcc :: t()) :: Bedrock.version() | nil

Get the last transaction version performed on the table. If no transaction has been performed then nil is returned.

oldest_version(mvcc)

@spec oldest_version(mvcc :: t()) :: Bedrock.version() | nil

Get the oldest possible transaction that can be read by the system. All transactions prior to this will have been coalesced.

purge_keys_newer_than_version(mvcc, version)

@spec purge_keys_newer_than_version(mvcc :: t(), Bedrock.version()) :: :ok
@spec purge_keys_newer_than_version(t(), Bedrock.version()) :: :ok

purge_keys_older_than_version(mvcc, version)

@spec purge_keys_older_than_version(mvcc :: t(), Bedrock.version()) ::
  {:ok, n_purged :: pos_integer()}
@spec purge_keys_older_than_version(t(), Bedrock.version()) :: :ok

Purge all keys/versions (and values) that are older than the given version.

transaction_at_version(mvcc, version)

@spec transaction_at_version(
  mvcc :: t(),
  version :: :latest | Bedrock.version()
) :: Bedrock.DataPlane.Transaction.encoded() | nil
@spec transaction_at_version(t(), :latest | Bedrock.version()) ::
  Bedrock.DataPlane.Transaction.encoded() | nil

Build a new transaction that encompasses only the latest writes for each key in the table, using the latest version as the cutoff. Since the latest transaction version is updated atomically alongside the transaction values, it's guaranteed that the generated transaction will include all writes that have been applied up until that point, and none that have been applied after.

Returns a transaction tuple. If no transactions have been performed then nil is returned.