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
テストコード
なんか、テストで使われているらしきデータ
良くわからん。
とりあえず、解説する。
定義されている、レコード、データ、その他
riak_core_ringモジュールで定義されている、データやらと解説するかもしれない。
chstate
-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されている関数を見よう。
-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
legacy_ring(#chstate{}) ->
true;
legacy_ring(_) ->
false.
lagacy_ringかどうか判定する。簡単。
upgrade/1
%% @doc Upgrade old ring structures to the latest format.
upgrade(Old=?CHSTATE{}) ->
まぁ、upgradeするよね。実装はあとで読む。
downgrade/2
%% @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
set_tainted(Ring) ->
update_meta(riak_core_ring_tainted, true, Ring).
taintedは「汚染する、腐敗させる」という意味。なんか嫌だ。
check_tainted
check_tainted(#chstate{}, _Msg) ->
...
チェックするらしい。
nearly_equal/2
%% @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であるのを許可するぐらいの同一性てかね。
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
%% @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
-spec chash(chstate()) -> CHash::chash:chash().
chash(?CHSTATE{chring=CHash}) ->
CHash.
簡単。
set_chash/2
set_chash(State, CHash) ->
State?CHSTATE{chring=CHash}.
chashがあれば、set_chashもある。
all_membars/1 と all_membars/2
%% @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
%% @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してない)クラスターのメンバーのリストを生成する。