ランデブーハッシュのノード選択
リストのノードからキーの担当を1つ選びます。cache-a、cache-b、cache-c の中で user:42 は cache-a に割り当てられ、ノードの構成かシードが変わるまで変わりません。各ノードのスコアも表示されるので、選ばれた理由を確認できます。ブラウザで無料で使えます。
- 無料
- ブラウザで動作
- 登録不要
- データは端末の外に出ません
無料で実行
計算例
- Key
- user:42
- ノード
- cache-a, cache-b, cache-c
- シード値
- 選択済み
- cache-a
- 節点数
- 3
ランデブーハッシュ(highest random weight hashingとも呼ばれます)は、仮想ノードのリングなしで、稼働中の集合から各キーをちょうど1つのノードに割り当てます。候補ノードごとにキーとそのノード識別子を組み合わせてハッシュし、スコアを並べて勝者を選びます。ノードの追加や離脱では、離脱ノードを好んでいたキー、または新規ノードを新たに好むキーだけが移動し、それ以外は据え置きのため、メンバーシップ変動下でもキャッシュヒット率とシャード所有は安定します。本セレクタはルーティングキー、ノードIDの一覧、任意のクラスタ用シード、任意の正の重みを受け取り、選択ノードと完全なスコア表を返して配置の監査や単体テストに使えます。
ランデブーハッシュがキーに対するノードを選ぶ仕組み
コンシステントハッシュはキーをリング上に置き、ノードの追加・削除でキーの一部だけが再配置される考え方を広めました。ランデブーハッシュは同じ負荷分散問題をより単純な契約で解きます。現在のメンバーシップ集合の各ノードについて、キーとそのノードの識別子だけに依存するスコアを計算し、最高スコアのノードを選びます。
古典名「highest random weight」は、ハッシュがペアごとの疑似乱数重みのように振る舞うことを表します。ノードAのスコアはノードBの有無に依存しないため、Bを外しても以前Bが勝者だったキーだけが影響を受け、他のキーは同じ生存ノードを選び続けます。ノードCを追加すると、旧所有者よりCとのスコアが高いキーだけが移ります。
この最小撹乱性が、大きな仮想ノードリングを持たずにバランスした所有を求めるキャッシュ、エッジPOP、シャーディングDBがランデブーを使う理由です。ノード一覧と並行な正の重みを渡すと加重HRWに切り替わり、ハッシュを(0,1]の単位区間uへ写し、スコアをuの(1/重み)乗として、重いノードがより頻繁に勝ちつつ完全に決定的なままにします。
32ビットでは稀ですが同点があり得るため、辞書順でより小さいノードIDを選んでクライアント間の不一致を防ぎます。
ランデブーノードセレクタが活きる場面
アプリケーション開発者はランデブーハッシュのノードセレクタを使い、ZooKeeperリングや大規模なコンシステントハッシュライブラリを立てずに、ユーザーセッション・オブジェクトシャード・レート制限カウンタを安定したバックエンドに固定します。CDNやマルチリージョンのエッジチームはコンテンツキーをPOPへ写像し、スケールアウト後もキャッシュ親和性を保ちます。
ゲームやIoTのフリートはデバイスIDをシミュレーションワーカーへ振り、離脱ノードを好んでいた一部だけを再配置します。Kafka風のキーを消費者間で分割するデータパイプラインは、候補ノード一覧をワーカーが呼ぶのと同じ純粋関数に offline で与え、メンバーシップ変更を事前検証できます。
活用例
キャッシュとシャードの親和性
オブジェクトやユーザーのキーをキャッシュ複製やDBシャードへ写像し、メンバーシップ変更では追加・削除されたノードを好んでいたキーだけが移るようにします。
マルチリージョンのエッジルーティング
各コンテンツキーについて健全な稼働リストからPOPやワーカーを選び、地域間の容量差には任意の重みを使います。
オフラインのリバランス予行
計画中のノード一覧をCI上の同じ決定的セレクタに渡し、本番メンバーシップを変える前に何キーが動くかを測ります。
よくある質問
ランデブーハッシュ(highest random weight)とは何ですか?
各(キー, ノード)ペアをハッシュで採点し最高スコアを選びます。削除されたノードを好んでいたキー、または新規ノードを好むキーだけが移動し、それ以外は据え置きです。
キーに対するノードはどう選びますか?
keyとnodes(1つ以上の一意なノードID)を送ってください。応答のselectedが勝者ノードです。
コンシステントハッシュとの違いは何ですか?
コンシステントハッシュはキーと仮想ノードをリングに置きます。ランデブーはリングも仮想ノードも不要で、稼働ノード一覧とキー+ノードのハッシュだけでメンバーシップ更新が単純になります。
ノードごとに容量を変えられますか?
方式は加重HRW(uの1/重み乗)に切り替わり、重いノードがより頻繁に勝ちつつ決定的なままです。
ノードを外すと、ほかのキーはどうなりますか?
ほかのキーは元のノードのままです。各ノードのスコアはキーとそのノードだけで決まるため、外したノードが勝っていたキーだけが移動します。ノードを追加した場合も、新しいノードのスコアが上回るキーだけが移ります。
関連ツール
開発者向け — APIアクセス
このページの機能はすべてAPIからも利用できます。自社システムに組み込みたいチーム向けのセクションです。それ以外の方は上のツールをそのままお使いください。
エンドポイント
Bearerトークンで認証し、POST1回でタスクをキューに登録します。結果はWebhookまたは署名付きリンクで受け取れます。
お使いのスタックから呼び出す
curl -X POST https://api.kit.forhosting.com/dev/rendezvous-hash \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"key":"user:42","nodes":["cache-a","cache-b","cache-c"]}'const res = await fetch("https://api.kit.forhosting.com/dev/rendezvous-hash", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"key": "user:42",
"nodes": [
"cache-a",
"cache-b",
"cache-c"
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/rendezvous-hash",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"key": "user:42",
"nodes": [
"cache-a",
"cache-b",
"cache-c"
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/rendezvous-hash", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"key":"user:42","nodes":["cache-a","cache-b","cache-c"]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"key":"user:42","nodes":["cache-a","cache-b","cache-c"]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/rendezvous-hash", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)リクエスト例
{
"key": "user:42",
"nodes": [
"cache-a",
"cache-b",
"cache-c"
]
}レスポンス例
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.rendezvous_hash",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}非同期APIです。task_idは即時に返ります。ポーリングは1秒あたり1リクエストまでです。
料金
単価はすべて公開しています。トークン換算や独自クレジットはありません。失敗したタスクは課金されません。
エラー
| HTTP | コード | 意味 |
|---|---|---|
401 | unauthorized | APIキーが無効か、指定されていません。Authorizationヘッダーを確認してください。 |
402 | insufficient_balance | 残高が不足しています。チャージ後に再度お試しください。 |
404 | unknown_type | 指定されたタスクタイプは存在しません。タイプ名を確認してください。 |
429 | rate_limited | リクエストが多すぎます。しばらく待ってから再度お試しください。 |
技術的な注記
純粋なparse実装はブラウザとAPIで決定的に一致し、モデルトークンを消費せず、ネットワークにもアクセスしません。
ランデブーハッシュがキーに対するノードを選ぶ仕組み
本エンドポイントは任意シード・キー・各ノードIDの32ビットFNV-1a混合による非加重HRWを実装し、生の符号なしハッシュをスコアとして整数比較を安定させます。
入力契約、応答形、検証ルール
不要なら省略するか空文字を渡します。ヘルスチェックと組み合わせ、nodesには健全なIDだけを渡すと、障害ホストはハッシュ内の特別ロジックなしでスコア競争から外れます。JSON本体にkey(trim後の空でない文字列、別名object_key)とnodes(1〜1000個の空でない一意なノードID文字列の配列、別名members)を送ってください。任意のseed(別名salt)はキーの前に付ける文字列ソルトで、独立クラスタが同じ業務キーをスコア表上で衝突させずに別マップできます。任意のweightsはnodesと同じ長さの正の有限数配列で、省略時は等重みの非加重ランデブーです。空キー、空ノード一覧、null要素、重複ID、非正の重み、長さ不一致はinvalid_inputで失敗し課金されません。成功時はkeyとseedをエコーし、selectedノード名、node_count、method(rendezvous_hrwまたはweighted_rendezvous_hrw)、scores(node・weight・score・元のhash整数を持つオブジェクト配列、score降順・ノード名昇順)を返します。この完全表は意図的で、負荷試験やコードレビューでキーが特定ホストに着地した理由を示す必要があるためです。例: key user:42 と nodes cache-a, cache-b, cache-c は、メンバーシップやseedが変わるまで変わらない単一の選択キャッシュ名を返します。
ランデブーノードセレクタが活きる場面
ランデブーハッシュやhighest random weightで検索上位を狙うコンテンツサイトは、有料APIとバイト一致する無料ブラウザツールの恩恵を受けます。実装は純粋parseのティアAのため、成功呼び出しは公開の基本価格$0.002で、モデルトークンも外向き通信もなく、検証失敗は残高を消費しません。
技術的な質問
ランデブーハッシュノードセレクタの料金は?
ブラウザツールは無料です。APIリクエストは成功1回あたり$0.002のKIT前払い残高を使い、無効入力は課金されません。