Module khepri_machine

Khepri low-level API.

Behaviours: ra_machine.

Description

Khepri low-level API.

This module exposes the "low-level" API to the Khepri database and state machine. All functions in khepri are built on top of this module.

The API is divided into two parts:
  1. Functions to manipulate a simple set of tree nodes directly.
  2. Functions to perform transactional queries and updates.

The store ID

All functions require a store ID (store_id/0). The store ID corresponds to the name of the Ra cluster Khepri was started with.

See khepri for more details about Ra systems and clusters.

Direct manipulation on tree nodes

The API provides the following three functions:

All functions take a native path pattern. They do not accept Unix-like paths.

All functions return one of these tuples:

Transactional queries and updates

Transactions are handled by transaction/2 and transaction/3.

Both functions take an anonymous function. See khepri_tx for more details about those functions and in particular their restrictions.

The return value is whatever the anonymous function returns if it succeeded or the reason why it aborted, similar to what mnesia:transaction/1 returns.

Data Types

child_list_length()

child_list_length() = non_neg_integer()

Number of direct child nodes under a tree node.

child_list_version()

child_list_version() = pos_integer()

Number of changes made to the list of child nodes of a node (child nodes added or removed).

The child list version starts at 1 when a node is created. It is increased by 1 each time a child is added or removed. Changes made to existing nodes are not reflected in this version.

data()

data() = any()

Data stored in a node's payload.

keep_untils_map()

keep_untils_map() = #{khepri_path:path() => khepri_condition:keep_until()}

Internal index of the per-node keep_until conditions.

keep_untils_revidx()

keep_untils_revidx() = #{khepri_path:path() => #{khepri_path:path() => ok}}

Internal reverse index of the keep_until conditions. If node A depends on a condition on node B, then this reverse index will have a "node B => node A" entry.

machine_config()

machine_config() = #config{snapshot_interval = non_neg_integer()}

Configuration record, holding read-only or rarely changing fields.

node_props()

node_props() = #{data => data(), payload_version := payload_version(), child_list_version := child_list_version(), child_list_length := child_list_length(), child_nodes => #{khepri_path:node_id() => node_props()}}

Structure used to return properties, payload and child nodes for a specific node.

node_props_map()

node_props_map() = #{khepri_path:path() => node_props()}

Structure used to return a map of nodes and their associated properties, payload and child nodes.

This structure is used in the return value of all commands and queries.

operation_options()

operation_options() = #{expect_specific_node => boolean(), include_child_names => boolean()}

Options used in find_matching_nodes/3.

payload()

payload() = none | {data, data()}

All types of payload stored in the nodes of the tree structure.

Beside the absence of payload, the only type of payload supported is data.

payload_version()

payload_version() = pos_integer()

Number of changes made to the payload of a node.

The payload version starts at 1 when a node is created. It is increased by 1 each time the payload is added, modified or removed.

result()

result() = khepri:ok(node_props_map()) | khepri:error()

Return value of a command or query.

stat()

stat() = #{payload_version := payload_version(), child_list_version := child_list_version()}

Stats attached to each node in the tree structure.

state()

state() = #khepri_machine{config = khepri_machine:machine_config(), root = khepri_machine:tree_node(), keep_untils = khepri_machine:keep_untils_map(), keep_untils_revidx = khepri_machine:keep_untils_revidx(), metrics = any()}

State of this Ra state machine.

tree_node()

tree_node() = #node{stat = khepri_machine:stat(), payload = khepri_machine:payload(), child_nodes = #{khepri_path:component() := #node{stat = khepri_machine:stat(), payload = khepri_machine:payload(), child_nodes = #{khepri_path:component() := #node{}}}}}

A node in the tree structure.

Function Index

put/3Creates or modifies a specific tree node in the tree structure.
put/4Creates or modifies a specific tree node in the tree structure.
get/2Returns all tree nodes matching the path pattern.
get/3Returns all tree nodes matching the path pattern.
delete/2Deletes all tree nodes matching the path pattern.
transaction/2Runs a transaction and returns the result.
transaction/3Runs a transaction and returns the result.

Function Details

put/3

put(StoreId, PathPattern, Payload) -> Result

Creates or modifies a specific tree node in the tree structure.

Calling this function is the same as calling put(StoreId, PathPattern, Payload, #{}).

See also: put/4.

put/4

put(StoreId, PathPattern, Payload, Extra) -> Result

StoreId: the name of the Ra cluster.
PathPattern: the path (or path pattern) to the node to create or modify.
Payload: the payload to put in the specified node.
Extra: extra options such as keep_until conditions.

returns: an "ok" tuple with a map with one entry, or an "error" tuple.

Creates or modifies a specific tree node in the tree structure.

The path or path pattern must target a specific tree node.

When using a simple path, if the target node does not exists, it is created using the given payload. If the target node exists, it is updated with the given payload and its payload version is increased by one. Missing parent nodes are created on the way.

When using a path pattern, the behavior is the same. However if a condition in the path pattern is not met, an error is returned and the tree structure is not modified.

If the target node is modified, the returned structure in the "ok" tuple will have a single key corresponding to the path of the target node. That key will point to a map containing the properties and payload (if any) of the node before the modification.

If the target node is created , the returned structure in the "ok" tuple will have a single key corresponding to the path of the target node. That key will point to empty map, indicating there was no existing node (i.e. there was no properties or payload to return).

The payload must be one of the following form: Example:
%% Insert a node at `/foo/bar', overwriting the previous value.
Result = khepri_machine:put(
           ra_cluster_name, [foo, bar], ?DATA_PAYLOAD(new_value)),
 
%% Here is the content of `Result'.
{ok, #{[foo, bar] => #{data => old_value,
                       payload_version => 1,
                       child_list_version => 1,
                       child_list_length => 0}}} = Result.

get/2

get(StoreId, PathPattern) -> Result

Returns all tree nodes matching the path pattern.

Calling this function is the same as calling get(StoreId, PathPattern, #{}).

See also: get/3.

get/3

get(StoreId, PathPattern, Options) -> Result

StoreId: the name of the Ra cluster.
PathPattern: the path (or path pattern) to match against the nodes to retrieve.
Options: options to tune the tree traversal or the returned structure content.

returns: an "ok" tuple with a map with zero, one or more entries, or an "error" tuple.

Returns all tree nodes matching the path pattern.

The returned structure in the "ok" tuple will have a key corresponding to the path per node which matched the pattern. Each key will point to a map containing the properties and payload of that matching node.

Example:
%% Query the node at `/foo/bar'.
Result = khepri_machine:get(ra_cluster_name, [foo, bar]),
 
%% Here is the content of `Result'.
{ok, #{[foo, bar] => #{data => new_value,
                       payload_version => 2,
                       child_list_version => 1,
                       child_list_length => 0}}} = Result.

delete/2

delete(StoreId, PathPattern) -> Result

StoreId: the name of the Ra cluster.
PathPattern: the path (or path pattern) to match against the nodes to delete.

returns: an "ok" tuple with a map with zero, one or more entries, or an "error" tuple.

Deletes all tree nodes matching the path pattern.

The returned structure in the "ok" tuple will have a key corresponding to the path per node which was deleted. Each key will point to a map containing the properties and payload of that deleted node.

Example:
%% Delete the node at `/foo/bar'.
Result = khepri_machine:delete(ra_cluster_name, [foo, bar]),
 
%% Here is the content of `Result'.
{ok, #{[foo, bar] => #{data => new_value,
                       payload_version => 2,
                       child_list_version => 1,
                       child_list_length => 0}}} = Result.

transaction/2

transaction(StoreId, Fun) -> Ret

Runs a transaction and returns the result.

Calling this function is the same as calling transaction(StoreId, Fun, auto).

See also: transaction/3.

transaction/3

transaction(StoreId, Fun, ReadWrite) -> Ret

StoreId: the name of the Ra cluster.
Fun: an arbitrary anonymous function.

returns: {atomic, Result} with the return value of Fun, or {aborted, Reason} if the anonymous function was aborted.

Runs a transaction and returns the result.

Fun is an arbitrary anonymous function which takes no arguments.

The ReadWrite flag determines what the anonymous function is allowed to do and in which context it runs:

The result of Fun can be any term. That result is returned in an {atomic, Result} tuple.


Generated by EDoc