Behaviours: ra_machine
.
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.
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.
get/2
and get/3
: returns all tree node matching the given
path pattern.put/3
and put/4
: updates a single specific tree node.delete/2
: removes all tree node matching the given path
pattern.All functions take a native path pattern. They do not accept Unix-like paths.
All functions return one of these tuples:{ok, NodePropsMap}
where NodePropsMap
is a node_props_map/0
:
{error, Reason}
if an error occured. In the case, no modifications to
the tree was performed.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.
mnesia:transaction/1
returns.
child_list_length() = non_neg_integer()
Number of direct child nodes under a tree node.
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() = any()
Data stored in a node's payload.
keep_untils_map() = #{khepri_path:path() => khepri_condition:keep_until()}
Internal index of the per-node keep_until conditions.
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() = #config{snapshot_interval = non_neg_integer()}
Configuration record, holding read-only or rarely changing fields.
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.
khepri_utils:flat_struct_to_tree/1
which may
construct fake node props without them.data
entry in this structure.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() = #{expect_specific_node => boolean(), include_child_names => boolean()}
Options used in find_matching_nodes/3
.
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() = 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() = khepri:ok(node_props_map()) | khepri:error()
Return value of a command or query.
stat() = #{payload_version := payload_version(), child_list_version := child_list_version()}
Stats attached to each node in the tree structure.
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() = #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.
put/3 | Creates or modifies a specific tree node in the tree structure. |
put/4 | Creates or modifies a specific tree node in the tree structure. |
get/2 | Returns all tree nodes matching the path pattern. |
get/3 | Returns all tree nodes matching the path pattern. |
delete/2 | Deletes all tree nodes matching the path pattern. |
transaction/2 | Runs a transaction and returns the result. |
transaction/3 | Runs a transaction and returns the result. |
put(StoreId, PathPattern, Payload) -> Result
StoreId = khepri:store_id()
PathPattern = khepri_path:pattern()
Payload = payload()
Result = result()
Creates or modifies a specific tree node in the tree structure.
Calling this function is the same as callingput(StoreId, PathPattern, Payload, #{})
.
See also: put/4.
put(StoreId, PathPattern, Payload, Extra) -> Result
StoreId = khepri:store_id()
PathPattern = khepri_path:pattern()
Payload = payload()
Extra = #{keep_until => keep_untils_map()}
Result = 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:?NO_PAYLOAD
, meaning there will be no payload attached to the
node?DATA_PAYLOAD(Term)
to store any type of term in the node%% 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(StoreId, PathPattern) -> Result
StoreId = khepri:store_id()
PathPattern = khepri_path:pattern()
Result = result()
Returns all tree nodes matching the path pattern.
Calling this function is the same as callingget(StoreId, PathPattern, #{})
.
See also: get/3.
get(StoreId, PathPattern, Options) -> Result
StoreId = khepri:store_id()
PathPattern = khepri_path:pattern()
Options = operation_options()
Result = 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(StoreId, PathPattern) -> Result
StoreId = khepri:store_id()
PathPattern = khepri_path:pattern()
Result = 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(StoreId, Fun) -> Ret
StoreId = khepri:store_id()
Fun = khepri_tx:tx_fun()
Ret = Atomic | Aborted
Atomic = {atomic, khepri_tx:tx_fun_result()}
Aborted = khepri_tx:tx_abort()
Runs a transaction and returns the result.
Calling this function is the same as callingtransaction(StoreId, Fun, auto)
.
See also: transaction/3.
transaction(StoreId, Fun, ReadWrite) -> Ret
StoreId = khepri:store_id()
Fun = khepri_tx:tx_fun()
ReadWrite = ro | rw | auto
Ret = Atomic | Aborted
Atomic = {atomic, khepri_tx:tx_fun_result()}
Aborted = khepri_tx:tx_abort()
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:
ReadWrite
is ro
, Fun
can do whatever it wants, except modify
the content of the store. In other words, uses of khepri_tx:put/2
or khepri_tx:delete/1
are forbidden and will abort the function.
Fun
is executed from a process on the leader Ra member.ReadWrite
is rw
, Fun
can use the khepri_tx
transaction
API as well as any calls to other modules as long as those functions or what
they do is permitted. See khepri_tx
for more details. If Fun
does
or calls something forbidden, the transaction will be aborted. Fun
is
executed in the context of the state machine process on each Ra
members.ReadWrite
is auto
, Fun
is analyzed to determine if it calls
khepri_tx:put/2
or khepri_tx:delete/1
, or uses any denied
operations for a read/write transaction. If it does, this is the same as
setting ReadWrite
to true. Otherwise, this is the equivalent of setting
ReadWrite
to false.Fun
can be any term. That result is returned in an
{atomic, Result}
tuple.
Generated by EDoc