[WIP] Riak_Coreを読む  Ring編

# Rick Coreを読む

現在Riak_Coreを調査中であり、ソースコードを読んでいる。

このエントリは、そのメモであり、まとめである。
もちろん、ただの、個人的なメモなので、間違えが当然ながら含まれているかもしれない。

# Ringを読む

## Ringのコンセプトやら概念

### Ringとは? 

Riak CoreのコンセプトにRingがある。Ringとは、データ構造であり、パーティションや、バーチャルノードの責務のデータである。Riak Coreのクラスターの全てのノードに、Ringがコピーされている。

## Ringのソースコードや、周辺のコード

Ringのソースコードは以下である。

https://github.com/basho/riak_core/blob/master/src/riak_core_ring.erl
https://github.com/basho/riak_core/blob/master/src/riak_core_ring_manager.erl
https://github.com/basho/riak_core/blob/develop/include/riak_core_ring.hrl

テストコード

https://github.com/basho/riak_core/blob/develop/test/riak_core_ring_eqc.erl

なんか、テストで使われているらしきデータ

https://github.com/basho/riak_core/blob/develop/test/my_ring

良くわからん。

## とりあえず、解説する。

### 定義されている、レコード、データ、その他

riak_core_ringモジュールで定義されている、データやらと解説するかもしれない。

#### chstate 
```erlang
-define(CHSTATE, #chstate_v2).
-record(chstate_v2, {
    nodename :: term(),          % the Node responsible for this chstate
    vclock   :: vclock:vclock() | undefined, % for this chstate object, entries are
                                 % {Node, Ctr}
    chring   :: chash:chash(),   % chash ring of {IndexAsInt, Node} mappings
    meta     :: dict() | undefined,  % dict of cluster-wide other data (primarily
                                 % bucket N-value, etc)

    clustername :: {term(), term()},
    next     :: [{integer(), term(), term(), [module()], awaiting | complete}],
    members  :: [{node(), {member_status(), vclock:vclock(), [{atom(), term()}]}}],
    claimant :: term(),
    seen     :: [{term(), vclock:vclock()}],
    rvsn     :: vclock:vclock()
}). 
```

とりあず、chstateのレコードが定義されている。chstateの"ch"の意味は分からない、とりあえず、状態であろう。(コメント頂きました。consistent hashingらしいですよ。)

nodenameはノードの名前、vclockはおそらくベクタークロック、chringは~~なんか良くわからなんけどキャッシュ~~consistent hashing の ring、metaは良くわからんけど、バケットやら、N-Valueが入っているらしい、clusternameはクラスターの名前?、next?わからん、たぶんこれから分かる。membersクラスターのメンバー、claimantは、スクを割り当てたり、処理を等しく分散する役割のノード、Seenとrvsnはまだ良くわからん。


```
%% Legacy chstate
-record(chstate, {
    nodename :: node(), % the Node responsible for this chstate
    vclock,   % for this chstate object, entries are {Node, Ctr}
    chring :: chash:chash(),   % chash ring of {IndexAsInt, Node} mappings
    meta      % dict of cluster-wide other data (primarily bucket N-value, etc)
}). 
```

レガシーな、chstateらしい。

```
-type member_status() :: joining | valid | invalid | leaving | exiting | down.
```

メンバーのステータス。というか、状態? join中、有効、無効、leave中、exit中、落ちた。状態遷移図書くかも。

```
%% type meta_entry(). Record for each entry in #chstate.meta
-record(meta_entry, {
    value,    % The value stored under this entry
    lastmod   % The last modified time of this entry, 
              %  from calendar:datetime_to_gregorian_seconds(
              %                             calendar:universal_time()), 
}).
```

「エントリ」のためのレコード。エントリってなんだ?


```
%% riak_core_ring() is the opaque data type used for partition ownership
-type riak_core_ring() :: ?CHSTATE{}.
-type chstate() :: riak_core_ring().

-type pending_change() :: {Owner :: node(),
                           NextOwner :: node(),
                           awaiting | complete}
                        | {undefined, undefined, undefined}.

-type resize_transfer() :: {{integer(),term()}, ordsets:ordset(node()), awaiting | complete}.
```

型周り。型は良いねー。

### public API

ここから、public APIを読んで行く長い。

うーん愚直に上から読んでも良いが、まぁ、その前に、exportされている関数を見よう。

```erlang
-export([all_members/1,
         all_owners/1,
         all_preflists/2,
         diff_nodes/2,
         equal_rings/2,
         fresh/0,
         fresh/1,
         fresh/2,
         get_meta/2,
         get_buckets/1,
         index_owner/2,
         my_indices/1,
         num_partitions/1,
         owner_node/1,
         preflist/2,
         random_node/1,
         random_other_index/1,
         random_other_index/2,
         random_other_node/1,
         reconcile/2,
         rename_node/3,
         responsible_index/2,
         transfer_node/3,
         update_meta/3,
         remove_meta/2]).

-export([cluster_name/1,
         legacy_ring/1,
         legacy_reconcile/2,
         upgrade/1,
         downgrade/2,
         set_tainted/1,
         check_tainted/2,
         nearly_equal/2,
         claimant/1,
         member_status/2,
         pretty_print/2,
         all_member_status/1,
         update_member_meta/5,
         clear_member_meta/3,
         get_member_meta/3,
         add_member/3,
         remove_member/3,
         leave_member/3,
         exit_member/3,
         down_member/3,
         set_member/4,
         set_member/5,
         members/2,
         set_claimant/2,
         increment_vclock/2,
         ring_version/1,
         increment_ring_version/2,
         set_pending_changes/2,
         active_members/1,
         claiming_members/1,
         ready_members/1,
         random_other_active_node/1,
         down_members/1,
         set_owner/2,
         indices/2,
         future_indices/2,
         future_ring/1,
         disowning_indices/2,
         cancel_transfers/1,
         pending_changes/1,
         next_owner/1,
         next_owner/2,
         next_owner/3,
         completed_next_owners/2,
         all_next_owners/1,
         change_owners/2,
         handoff_complete/3,
         ring_ready/0,
         ring_ready/1,
         ring_ready_info/1,
         ring_changed/2,
         set_cluster_name/2,
         reconcile_names/2,
         reconcile_members/2,
         is_primary/2,
         chash/1,
         set_chash/2,
         resize/2,
         set_pending_resize/2,
         set_pending_resize_abort/1,
         maybe_abort_resize/1,
         schedule_resize_transfer/3,
         awaiting_resize_transfer/3,
         resize_transfer_status/4,
         resize_transfer_complete/4,
         complete_resize_transfers/3,
         reschedule_resize_transfers/3,
         is_resizing/1,
         is_post_resize/1,
         is_resize_complete/1,
         resized_ring/1,
         set_resized_ring/2,
         future_index/3,
         future_index/4,
         future_index/5,
         is_future_index/4,
         future_owner/2,
         future_num_partitions/1,
         vnode_type/2,
         deletion_complete/3]).
```


相当多い。

読んで行こう。

## 名前と引数と型を読んで行く

とりあえず、型を読んで関数の意味を読んで行こう。型が無い場合は、実装を読むか、名前と引数で、推測する。

### legary_ring/1

```erlang
legacy_ring(#chstate{}) ->
    true;
legacy_ring(_) ->
    false.
```

lagacy_ringかどうか判定する。簡単。

### upgrade/1

```erlang
%% @doc Upgrade old ring structures to the latest format.
upgrade(Old=?CHSTATE{}) ->
```

まぁ、upgradeするよね。実装はあとで読む。

### downgrade/2

```erlang
%% @doc Downgrade the latest ring structure to a specified version.
downgrade(1,?CHSTATE{nodename=Node,
                     vclock=VC,
                     chring=Ring,
                     meta=Meta}) -> 
...
```

ugradeがあれば、downgradeもある。引数は2つで、第一引数がversionを取るのか。

### set_tainted

```erlang
set_tainted(Ring) ->
    update_meta(riak_core_ring_tainted, true, Ring).
```

taintedは「汚染する、腐敗させる」という意味。なんか嫌だ。

### check_tainted

```erlang
check_tainted(#chstate{}, _Msg) -> 
...
```

チェックするらしい。


### nearly_equal/2

```erlang
%% @doc Verify that the two rings are identical expect that metadata can
%%      differ and RingB's vclock is allowed to be equal or a direct
%%      descendant of RingA's vclock. This matches the changes that the
%%      fix-up logic may make to a ring.
-spec nearly_equal(chstate(), chstate()) -> boolean().
```

2つのchstate()を取り、boolean()を返す。まぁ、2つのringのメタデータが違い、vclockがequalかdescendantであるのを許可するぐらいの同一性てかね。

```erlang

nearly_equal(RingA, RingB) ->
    TestVC = vclock:descends(RingB?CHSTATE.vclock, RingA?CHSTATE.vclock),
    RingA2 = RingA?CHSTATE{vclock=undefined, meta=undefined},
    RingB2 = RingB?CHSTATE{vclock=undefined, meta=undefined},
    TestRing = (RingA2 =:= RingB2),
    TestVC and TestRing.
```

まぁ、vlock:descends/2で、vclockのチェック、そんで、vclockとmetaをundefinedにして、等しいかチェックしている。

### is_primary/2

```erlang
%% @doc Determine if a given Index/Node `IdxNode' combination is a
%%      primary.
-spec is_primary(chstate(), {chash:index_as_int(), node()}) -> boolean().
is_primary(Ring, IdxNode) ->
    Owners = all_owners(Ring),
    lists:member(IdxNode, Owners).
```

chstate()と、{chash:index_as_int(), node()}を取り,booleanを返す。{chash:index_as_int(), node()}がprimaryであるかを判定する。


### chash/1

```erlang
-spec chash(chstate()) -> CHash::chash:chash().
chash(?CHSTATE{chring=CHash}) ->
    CHash.
```

簡単。 

### set_chash/2

```erlang
set_chash(State, CHash) ->
    State?CHSTATE{chring=CHash}.
```

chashがあれば、set_chashもある。

### all_membars/1 と all_membars/2

```erlang
%% @doc Produce a list of all nodes that are members of the cluster
-spec all_members(State :: chstate()) -> [Node :: term()].
all_members(?CHSTATE{members=Members}) ->
    get_members(Members).

members(?CHSTATE{members=Members}, Types) ->
    get_members(Members, Types).
```

chstate()の全メンバーのリストを生成する。


### active_members/1

```erlang
%% @doc Produce a list of all active (not marked as down) cluster members
active_members(?CHSTATE{members=Members}) ->
    get_members(Members, [joining, valid, leaving, exiting]).
```

activeな(downしてない)クラスターのメンバーのリストを生成する。