Riak VClock

来源:互联网 发布:php深入好难 编辑:程序博客网 时间:2024/04/28 13:18

Riak VClock

关于向量时钟的概念,在这里就多讲了,大家可以参照一下Dynamo的论文了解一下,向量时钟在分布式主要用于解决一致性性问题,可以和CRDTs一起看。

下面的源代码是参照riak中的,就是把它翻译为elixir格式而已,基本不变。

时钟主要出现的情况有网络分区和并行更新。

这样只会丢掉一些向量时钟的信息,即数据更新过程的信息,但是不会丢掉实实在在的数据。只有当一种情况会有问题,就是一个客户端保持了一个很久之前的向量时钟,然后继承于这个向量时钟提交了一个数据,此时就会有冲突,因为服务器这边已经没有这个很久之前的向量时钟信息了,已经被剪枝掉了可能,所以客户端提交的此次数据,在服务端无法找到一个祖先,此时就会创建一个sibling。所以这个剪枝的策略是一个权衡tradeoff,一方面是无限增长的向量时钟的空间,另一方面是偶尔的会有"false merge",对,但肯定的是,不会悄无声息的丢数据。综上,为了防止向量时钟空间的无限增长,剪枝还是比用server标识向量时钟工作的更好。
  • 结构:

主要有3个元祖{node, {opCount, TS}},分布为节点(协调器),操作数和操作时间。

  • 主要的方法:

merge(合并):

合并的规则是,opCount>TS:当节相同时,谁的opCount大,谁赢;如果opCount一样时,谁的时间大谁赢。

@doc """  Combine all VClock in the input list into their least possible common descendant  """  @spec merge(list, list) :: list  def merge([]), do: []  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))  end  def merge([], nclock), do: nclock  def merge([aclock|vclocks], nclock)  do    merge(vclocks, merge(:lists.keysort(1, aclock), nclock, []))  end  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    cond do      node1 < node2 ->        merge(vclock, n, [nct1|accclock]);      node1 > node2 ->        merge(v, nclock, [nct2|accclock]);      true ->        ({_ctr, _ts} = ct) = cond do                            ctr1 > ctr2 ->                              ct1;                            ctr1 < ctr2 ->                              ct2;                            true ->                              {ctr1, :erlang.max(ts1, ts2)}                          end        merge(vclock, nclock, [{node1, ct}|accclock])    end  end


prune(裁剪):

裁剪的法则主要是空间时间两方面.
!()[../pic/riak_4.png]

最终的裁剪函数prune_vclock1(v, now, bprops, headtime).

@doc """  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)  end  def prune_vclock1(v, now, bprops) do    case get_property(:small_vclock, bprops) >= :erlang.length(v) do      true -> v;      false ->        {_, {_, headtime}} = hd(v)        case (now - headtime) < get_property(:young_vclock, bprops) do          true -> v;          false -> prune_vclock1(v, now, bprops, headtime)        end    end  end  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);        false -> v    end  end  def get_property(key, pairlist) do    case :lists.keyfind(key, 1, pairlist) do      {_key, value} ->        value;      false ->        :undefined    end  end


  • source
defmodule VClock do  @moduledoc """    this is !!!!!!!!  """  @vsn 0.1  @spec fresh() :: []  def fresh do    []  end  # return true if va is a direct descendant of vb, else false -- remember, a vclock is its own descendant!  @spec descends(any, []) :: (true|false)  def descends(_, []) do    true  end  @type va :: list()  @spec descends(any, any) :: (false|true)  def descends(va, vb) do    [{nodeb, {ctrb, _}} | resetb] = vb    case :lists.keyfind(nodeb, 1, va) do      false ->        false;      {_, {ctra, _tsa}} ->        (ctra >= ctrb) && descends(va, resetb)    end  end  @doc """  Combine all VClock in the input list into their least possible common descendant  """  @spec merge(list, list) :: list  def merge([]), do: []  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))  end  def merge([], nclock), do: nclock  def merge([aclock|vclocks], nclock)  do    merge(vclocks, merge(:lists.keysort(1, aclock), nclock, []))  end  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    cond do      node1 < node2 ->        merge(vclock, n, [nct1|accclock]);      node1 > node2 ->        merge(v, nclock, [nct2|accclock]);      true ->        ({_ctr, _ts} = ct) = cond do                            ctr1 > ctr2 ->                              ct1;                            ctr1 < ctr2 ->                              ct2;                            true ->                              {ctr1, :erlang.max(ts1, ts2)}                          end        merge(vclock, nclock, [{node1, ct}|accclock])    end  end  @doc """  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      {_, {c, _}} -> c;      false -> :undefined    end  end  @doc """  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      {_, {_, ts}} -> ts;      false -> :undefined    end  end  @doc """    increment VClock at node  """  @spec increment(atom, list) :: integer  def increment(node, vclock) do    increment(node, timestamp(), vclock)  end  @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                                  false ->                                    {{1, incts}, vclock};                                  {:value, {_n, {c, _t}}, modv} ->                                    {{c + 1, incts}, modv}                               end    [{node, c1} | newv]  end  # retrun the list of all nodes that have ever incremented VClock  @spec all_nodes(vclock :: list) :: (list)  def all_nodes(vclock) do    vclock |> Enum.map(fn({x, {_, _}}) -> x end)  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  def timestamp do    {megaseconds, seconds, _} = :os.timestamp()    @days_from_gergorian_base_to_epoch + megaseconds * 1000000 + seconds  end  @doc """   Compares two VClock for equality  """  @spec equal(va :: list, vb :: list) :: (true | false)  def equal(va, vb) do    Enum.sort(va) === Enum.sort(vb)  end  @doc """  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)  end  def prune_vclock1(v, now, bprops) do    case get_property(:small_vclock, bprops) >= :erlang.length(v) do      true -> v;      false ->        {_, {_, headtime}} = hd(v)        case (now - headtime) < get_property(:young_vclock, bprops) do          true -> v;          false -> prune_vclock1(v, now, bprops, headtime)        end    end  end  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);        false -> v    end  end  def get_property(key, pairlist) do    case :lists.keyfind(key, 1, pairlist) do      {_key, value} ->        value;      false ->        :undefined    end  endend


0 0
原创粉丝点击