This module provides a POLog CRDT implementation with an Erlang map data type for use in internal CloudI services. Usage of the module handles the replication of Erlang map state between Erlang processes to provide an eventually consistent data store among internal CloudI service processes.
.Copyright © 2017-2022 Michael Truog
Version: 2.0.5 Oct 11 2022 20:13:41 ------------------------------------------------------------------------
Authors: Michael Truog (mjtruog at protonmail dot com).
This module provides a POLog CRDT implementation with an Erlang map data type for use in internal CloudI services. Usage of the module handles the replication of Erlang map state between Erlang processes to provide an eventually consistent data store among internal CloudI service processes.
The bootstrap functionality and the clean_vclocks functionality are not described in the POLog papers and are likely unique to this implementation. This additional functionality allows CloudI service processes that utilize cloudi_crdt to start, restart or fail (a crash, netsplit, etc.) without affecting other instances of cloudi_crdt that are configured with the same service name and manage the same data.
The cloudi_crdt functions that may be called within cloudi_service_init/4 are events_subscribe/3, events_subscribe/4, events_subscriptions/3, events_clear/2, events_clear/3, new/1 and new/2. A CloudI service that uses cloudi_crdt should have a destination refresh method that is immediate.
N.B., Any use of an update function or an assign function requires special attention to ensure the data change is repeatable for all CRDT instances. The update functions and the assign functions are not idempotent but can be used safely if the input data is not unique to a service instance process or service request. An assign function can cause concurrent assigns to occur for the same key (e.g., caused by concurrent service requests) and the value stored in each CRDT instance needs to be the same. To avoid the problem, data can be added to the CRDT instance (achieving consistency there) before it is used as data input for an update function or an assign function. It is important to also consider using the put function instead of update or assign to avoid potential consistency problems.
The papers related to this implementation of the POLog CRDT are:
Carlos Baquero, Paulo Sérgio Almeida, Ali Shoker. Pure Operation-Based Replicated Data Types. 2017. https://arxiv.org/abs/1710.04469
Georges Younes, Ali Shoker, Paulo Sérgio Almeida, and Carlos Baquero. Integration Challenges of Pure Operation-based CRDTs in Redis. In First Workshop on Programming Models and Languages for Distributed Computing (PMLDC '16), New York, NY, USA, Article 7, 2016. http://haslab.uminho.pt/cbm/files/pmldc-2016-redis-crdts.pdf
Carlos Baquero, Paulo Sérgio Almeida, and Ali Shoker. Making operation-based crdts operation-based. In Proceedings of the First Workshop on Principles and Practice of Eventual Consistency, page 7, 2014. http://haslab.uminho.pt/ashoker/files/opbaseddais14.pdf
Mattern, Friedemann. Virtual Time and Global States of Distributed Systems. Workshop on Parallel and Distributed Algorithms, pp. 215-226, 1988. http://homes.cs.washington.edu/~arvind/cs425/doc/mattern89virtual.pdf
Lamport, Leslie. Time, clocks, and the ordering of events in a distributed system. Communications of the ACM, vol. 21, iss. 7, pp. 558–565, 1978. http://research.microsoft.com/en-us/um/people/lamport/pubs/time-clocks.pdfbootstrap_state() = {VClockAvg::float(), NodeId::node_id(), VClock::vclock(), VClocks::vclocks(), POLogMode::polog_mode(), POLog::polog(), Data::data()}
event_id() = cloudi_service:trans_id() | any()
event_type() = assign | clear | decr | incr | put | update
events() = #{key() := [event_type()]}
initial_data_function() = fun((data()) -> any())
key() = any()
milliseconds() = 1..4294967295
node_id() = {node(), cloudi_service:source()}
operation_write() = {assign, Id::event_id(), Key::key(), Value::value()} | {incr, Id::event_id(), Key::key(), Value::value()} | {decr, Id::event_id(), Key::key(), Value::value()} | {update, Id::event_id(), Key::key(), ModuleVersion::list(), Module::module(), Function::atom()} | {update, Id::event_id(), Key::key(), ModuleVersion::list(), Module::module(), Function::atom(), Argument1::any()} | {update_assign, Id::event_id(), Key::key(), Value::value(), ModuleVersion::list(), Module::module(), Function::atom()} | {update_assign, Id::event_id(), Key::key(), Value::value(), ModuleVersion::list(), Module::module(), Function::atom(), Argument1::any()} | {update_clear, Id::event_id(), Key::key(), ModuleVersion::list(), Module::module(), Function::atom()} | {update_clear, Id::event_id(), Key::key(), ModuleVersion::list(), Module::module(), Function::atom(), Argument1::any()} | {put, Id::event_id(), Key::key(), Value::value()} | {clear, Id::event_id(), Key::key()} | {clear_all, Id::event_id()}
options() = [{service_name, string()} | {init_delay, undefined | cloudi_args_type:period()} | {node_count, non_neg_integer()} | {initial_data_function, initial_data_function() | undefined} | {clean_vclocks, seconds()} | {clean_vclocks_failure, undefined | float() | 1..100} | {retry, non_neg_integer()} | {retry_delay, cloudi_args_type:period()} | {timeout_default, cloudi_service:timeout_period()} | {priority_default, cloudi_service:priority()} | {priority_default_offset, -255..255 | undefined}]
polog() = [{vclock(), operation_write()}]
polog_mode() = bootstrap | normal
seconds() = 1..4294967
state() = #cloudi_crdt{service_name_full = cloudi_service:service_name(), init_delay = undefined | milliseconds(), node_count = non_neg_integer(), initial_data_function = undefined | initial_data_function(), clean_vclocks_interval = seconds(), clean_vclocks_failure = number(), queue = cloudi_queue:state(), word_size = pos_integer(), node_id = node_id(), node_ids = [node_id()], vclock = vclock(), vclocks = vclocks(), polog_mode = polog_mode(), bootstrap_node_id = undefined | node_id(), bootstrap_states = [bootstrap_state()], bootstrap_requests = non_neg_integer(), polog = polog(), flush = boolean(), data = data(), events = events(), events_any = [event_type()]}
value() = any()
vclock() = #{node_id() := non_neg_integer()}
vclocks() = #{node_id() := vclock()}
assign(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), State::state()) -> state()
assign_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), Id::event_id(), State::state()) -> state()
byte_size(Dispatcher::cloudi_service:dispatcher(), State::state()) -> non_neg_integer()
clear(Dispatcher::cloudi_service:dispatcher(), State::state()) -> state()
clear(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> state()
clear_id(Dispatcher::cloudi_service:dispatcher(), Id::event_id(), State::state()) -> state()
clear_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Id::event_id(), State::state()) -> state()
crdt_size(Cloudi_crdt::state()) -> pos_integer()
decr(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> state()
decr(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::number(), State::state()) -> state()
decr_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Id::event_id(), State::state()) -> state()
decr_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::number(), Id::event_id(), State::state()) -> state()
events_clear(Dispatcher::cloudi_service:dispatcher(), State::state()) -> state()
events_clear(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> state()
events_subscribe(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> state()
events_subscribe(Dispatcher::cloudi_service:dispatcher(), Key::key(), EventTypes::[event_type()], State::state()) -> state()
events_subscriptions(Dispatcher::cloudi_service:dispatcher(), EventTypes::[event_type()], State::state()) -> state()
find(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> {ok, Value::value()} | error
flush_next_write(Dispatcher::cloudi_service:dispatcher(), State::state()) -> state()
fold(Dispatcher::cloudi_service:dispatcher(), F::fun((Key::key(), Value::value(), AccIn::any()) -> AccOut::any()), AccInit::any(), State::state()) -> AccFinal::any()
get(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> Value::value()
handle_info(Request::any(), State::state(), Dispatcher::cloudi_service:dispatcher()) -> {ok, StateNew::state()} | {{error, Reason::cloudi_service:error_reason()}, StateNew::state()} | {ignored, State::state()}
cloudi_service_handle_info/3
callback function.
handle_request(RequestType::cloudi_service:request_type(), Name::cloudi_service:service_name(), Pattern::cloudi_service:service_name_pattern(), RequestInfo::cloudi_service:request_info(), Request::cloudi_service:request(), Timeout::cloudi_service:timeout_value_milliseconds(), Priority::cloudi_service:priority_value(), TransId::cloudi_service:trans_id(), Source::cloudi_service:source(), State::state(), Dispatcher::cloudi_service:dispatcher()) -> {ok, StateNew::state()} | {ignored, State::state()}
cloudi_service_handle_request/11
callback function.
incr(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> state()
incr(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::number(), State::state()) -> state()
incr_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Id::event_id(), State::state()) -> state()
incr_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::number(), Id::event_id(), State::state()) -> state()
is_key(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> boolean()
keys(Dispatcher::cloudi_service:dispatcher(), State::state()) -> [key()]
new(Dispatcher::cloudi_service:dispatcher()) -> state()
new(Dispatcher::cloudi_service:dispatcher(), Options::options()) -> state()
put(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), State::state()) -> state()
put_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), Id::event_id(), State::state()) -> state()
size(Dispatcher::cloudi_service:dispatcher(), State::state()) -> non_neg_integer()
update(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), State::state()) -> state()
update(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), Argument1::any(), State::state()) -> state()
update_assign(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), Module::module(), Function::atom(), State::state()) -> state()
update_assign(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), Module::module(), Function::atom(), Argument1::any(), State::state()) -> state()
update_assign_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), Module::module(), Function::atom(), Id::event_id(), State::state()) -> state()
update_assign_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Value::value(), Module::module(), Function::atom(), Argument1::any(), Id::event_id(), State::state()) -> state()
update_clear(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), State::state()) -> state()
update_clear(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), Argument1::any(), State::state()) -> state()
update_clear_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), Id::event_id(), State::state()) -> state()
update_clear_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), Argument1::any(), Id::event_id(), State::state()) -> state()
update_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), Id::event_id(), State::state()) -> state()
update_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Module::module(), Function::atom(), Argument1::any(), Id::event_id(), State::state()) -> state()
values(Dispatcher::cloudi_service:dispatcher(), State::state()) -> [value()]
zero(Dispatcher::cloudi_service:dispatcher(), Key::key(), State::state()) -> state()
zero_id(Dispatcher::cloudi_service:dispatcher(), Key::key(), Id::event_id(), State::state()) -> state()
Generated by EDoc