Riak VClock
关于向量时钟的概念。在这里就多讲了,大家能够參照一下Dynamo的论文了解一下,向量时钟在分布式主要用于解决一致性性问题。能够和CRDTs一起看。
以下的源码是參照riak中的,就是把它翻译为elixir格式而已。基本不变。
时钟主要出现的情况有网络分区和并行更新。
这样仅仅会丢掉一些向量时钟的信息,即数据更新过程的信息,可是不会丢掉实实在在的数据。仅仅有当一种情况会有问题,就是一个client保持了一个非常久之前的向量时钟,然后继承于这个向量时钟提交了一个数据,此时就会有冲突。由于服务器这边已经没有这个非常久之前的向量时钟信息了,已经被剪枝掉了可能,所以client提交的此次数据,在服务端无法找到一个祖先。此时就会创建一个sibling。
所以这个剪枝的策略是一个权衡tradeoff,一方面是无限增长的向量时钟的空间。还有一方面是偶尔的会有"false merge"。对,但肯定的是,不会悄无声息的丢数据。综上。为了防止向量时钟空间的无限增长,剪枝还是比用server标识向量时钟工作的更好。
主要有3个元祖{node, {opCount, TS}},分布为节点(协调器)。操作数和操作时间。
merge(合并):
合并的规则是,opCount>TS:当节同样时,谁的opCount大,谁赢;假设opCount一样时,谁的时间大谁赢。
Combine all VClock in the input list into their least possible common descendant
@spec merge(list, list) :: list
def merge([singevclock]), do: singevclock
## first is a list, eg [:a, {1, 1234}]
# rest is list of list, eg [[{:a, {1, 233}}, {:b, {3, 124}}]]
def merge([first|rest]) do
merge(rest, :lists.keysort(1, first))
def merge([], nclock), do: nclock
def merge([aclock|vclocks], nclock) do
merge(vclocks, merge(:lists.keysort(1, aclock), nclock, []))
def merge([], [], accclock), do: :lists.reverse(accclock)
def merge([], left, accclock), do: :lists.reverse(accclock, left)
def merge(left, [], accclock), do: :lists.reverse(accclock, left)
def merge(v = [{node1, {ctr1, ts1} = ct1} = nct1 | vclock],
n = [{node2, {ctr2, ts2} = ct2} = nct2 | nclock], accclock) do
merge(vclock, n, [nct1|accclock]);
merge(v, nclock, [nct2|accclock]);
({_ctr, _ts} = ct) = cond do
{ctr1, :erlang.max(ts1, ts2)}
merge(vclock, nclock, [{node1, ct}|accclock])
prune(裁剪):
裁剪的法则主要是空间和时间双方面.
!()[../pic/riak_4.png]
终于的裁剪函数prune_vclock1(v, now, bprops, headtime).
Possibly shrink the size of a vclock, depending on current age and size
@spec prune(v :: list, now :: integer, bucketprops :: any) :: list
def prune(v, now, bucketprops) do
## This sort need to be deterministic, to avoid spurious merge conflicts later,
# We achieve this by using the node ID as secondary key
sortv = :lists.sort(fn({n1, {_, t1}}, {n2, {_, t2}}) -> {t1, n1} < {t2, n2} end, v)
prune_vclock1(sortv, now, bucketprops)
def prune_vclock1(v, now, bprops) do
case get_property(:small_vclock, bprops) >= :erlang.length(v) do
{_, {_, headtime}} = hd(v)
case (now - headtime) < get_property(:young_vclock, bprops) do
false -> prune_vclock1(v, now, bprops, headtime)
def prune_vclock1(v, now, bprops, headtime) do
# has a precondition that v is longer than small and older than young
case (:erlang.length(v) > get_property(:big_vclock, bprops)) or ((now - headtime) > get_property(:old_vclock, bprops)) do
true -> prune_vclock1(tl(v), now, bprops);
def get_property(key, pairlist) do
case :lists.keyfind(key, 1, pairlist) do
# return true if va is a direct descendant of vb, else false -- remember, a vclock is its own descendant!
@spec descends(any, []) :: (true|false)
@spec descends(any, any) :: (false|true)
[{nodeb, {ctrb, _}} | resetb] = vb
case :lists.keyfind(nodeb, 1, va) do
(ctra >= ctrb) && descends(va, resetb)
Combine all VClock in the input list into their least possible common descendant
@spec merge(list, list) :: list
def merge([singevclock]), do: singevclock
## first is a list, eg [:a, {1, 1234}]
# rest is list of list, eg [[{:a, {1, 233}}, {:b, {3, 124}}]]
def merge([first|rest]) do
merge(rest, :lists.keysort(1, first))
def merge([], nclock), do: nclock
def merge([aclock|vclocks], nclock) do
merge(vclocks, merge(:lists.keysort(1, aclock), nclock, []))
def merge([], [], accclock), do: :lists.reverse(accclock)
def merge([], left, accclock), do: :lists.reverse(accclock, left)
def merge(left, [], accclock), do: :lists.reverse(accclock, left)
def merge(v = [{node1, {ctr1, ts1} = ct1} = nct1 | vclock],
n = [{node2, {ctr2, ts2} = ct2} = nct2 | nclock], accclock) do
merge(vclock, n, [nct1|accclock]);
merge(v, nclock, [nct2|accclock]);
({_ctr, _ts} = ct) = cond do
{ctr1, :erlang.max(ts1, ts2)}
merge(vclock, nclock, [{node1, ct}|accclock])
get the counter value in vclock set from node
@spec get_counter(node :: atom, vclock::list) :: (integer|:undefined)
def get_counter(node, vclock) do
case :lists.keytake(node, 1, vclock) do
Get the timestamp value in a VClock set from node
@spec get_timestamp(node :: atom, vclock :: list) :: (integer | :undefined)
def get_timestamp(node, vclock) do
case :lists.keytake(node, 1, vclock) do
@spec increment(atom, list) :: integer
def increment(node, vclock) do
increment(node, timestamp(), vclock)
@spec increment(atom, integer, list) :: list
def increment(node, incts, vclock) do
IO.puts "#{inspect node}, #{inspect incts}, #{inspect vclock}"
{{_ctr, _ts} = c1, newv} = case :lists.keytake(node, 1, vclock) do
{:value, {_n, {c, _t}}, modv} ->
# retrun the list of all nodes that have ever incremented VClock
@spec all_nodes(vclock :: list) :: (list)
vclock |> Enum.map(fn({x, {_, _}}) -> x end)
@days_from_gergorian_base_to_epoch (1978 * 365 + 478)
@seconds_from_gergorian_base_to_epoch (@days_from_gergorian_base_to_epoch * 24 * 60 * 60)
@spec timestamp() :: integer
{megaseconds, seconds, _} = :os.timestamp()
@days_from_gergorian_base_to_epoch + megaseconds * 1000000 + seconds
Compares two VClock for equality
@spec equal(va :: list, vb :: list) :: (true | false)
Enum.sort(va) === Enum.sort(vb)
Possibly shrink the size of a vclock, depending on current age and size
@spec prune(v :: list, now :: integer, bucketprops :: any) :: list
def prune(v, now, bucketprops) do
## This sort need to be deterministic, to avoid spurious merge conflicts later,
# We achieve this by using the node ID as secondary key
sortv = :lists.sort(fn({n1, {_, t1}}, {n2, {_, t2}}) -> {t1, n1} < {t2, n2} end, v)
prune_vclock1(sortv, now, bucketprops)
def prune_vclock1(v, now, bprops) do
case get_property(:small_vclock, bprops) >= :erlang.length(v) do
{_, {_, headtime}} = hd(v)
case (now - headtime) < get_property(:young_vclock, bprops) do
false -> prune_vclock1(v, now, bprops, headtime)
def prune_vclock1(v, now, bprops, headtime) do
# has a precondition that v is longer than small and older than young
case (:erlang.length(v) > get_property(:big_vclock, bprops)) or ((now - headtime) > get_property(:old_vclock, bprops)) do
true -> prune_vclock1(tl(v), now, bprops);
def get_property(key, pairlist) do
case :lists.keyfind(key, 1, pairlist) do
本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5229840.html,如需转载请自行联系原作者
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26692020/viewspace-3171419/
,如需转载,请注明出处,否则将追究法律责任。