kevy4.0

移行——プレイブックとツールチェーン

2部構成です。プレイブック:稼働中のRDSワークロードを、検証クランプとロールバックポイントを備えたフェーズ単位でkevyへ移す方法。ツールチェーンkevy-cli):データをkevyへ、kevyから、そしてkevyサーバー間で動かし——移動が忠実だったことを証明するコマンド群。モデリングの疑問(「うちのJOINは何になるの?」)はrds-workloads.mdへ、実行可能なパターンはクックブックへ。

フェーズ制プレイブック

7つのフェーズがあります。要になる性質は、書き込みカットオーバーまでのすべてのフェーズで、真実の源は依然としてRDSであり、ロールバックは「アプリの向き先を戻す」ことである——決して「データを逆移行する」ではない、という点です。フェーズを飛ばさないでください。各フェーズの検証クランプこそが、次のフェーズを退屈なものにしてくれます。

フェーズ0——棚卸し

すべてのテーブルと、ワークロードが実際に実行するすべてのクエリ形を列挙します(真実はORMのクエリログやpg_stat_statementsであって、スキーマではありません)。各項目をrds-workloadsマトリクスに照らして分類します。大半の行は機械的に対応づきます。拒否された構文(ホットパスのN-way join、HAVING、深いOFFSETページネーション、アドホックなレポーティングSQL)に当たるものにフラグを立ててください。

フェーズ1——スキーマ対応づけ

棚卸しを宣言スクリプトへ変換します。プレフィックス、フィールドレイアウト、型、そしてすべてのIDX.CREATEVIEW.CREATE行です。マトリクスの型規則——DECIMALは最小単位の整数へ、DATETIMEはエポックi64へ、NULLはフィールド不在へ——を今適用してください。これらはバックフィル後に安くはやり直せない、書き込みフォーマットの決定だからです。

フェーズ2——変更キャプチャ(二重書き込みウィンドウが開く)

バックフィルのスナップショットより前にRDSへの書き込みのキャプチャを始めます。何も隙間に落ちないようにするためです。ルートは2つあります。

どちらのルートでも、kevyへの書き込みは冪等な再構築形(フル行のHSET、コレクションはDEL+再追加)でなければなりません。そうしておけばバックフィルとの重なりは無害です。

フェーズ3——バックフィル

既知のキャプチャ位置でRDSのスナップショットを取り、RESPコマンドファイルへエクスポートして、バルクロードします。

# generate rebuild frames from your RDS export (one HSET per row):
#   HSET user:42 name ada email ada@example.com …
kevy-cli import -p 6004 --strict rows.resp        # ≥200k cmd/s

インポートが先、インデックス宣言は後(インデックス後回しの規則)。バックフィルは既存の行から各インデックスをバルク速度で構築します——インポートする行ごとに書き込みフックのコストを払うより桁違いに安上がりです。その後フェーズ1の宣言スクリプトを実行し、IDX.LISTstate=readyを報告するまで待ちます(詳細はインデックス済みキー空間へのロード)。

フェーズ4——読み取りカットオーバー(エンドポイント単位のカナリア)

読み取りはビッグバンではなくエンドポイント単位で切り替えます。各エンドポイントに必要な一貫性の段を選びます(availability.md)。閲覧/一覧系のエンドポイントは段0(非同期レプリカ読み取り)に乗せ、「今書いたものを見せて」のフローはREPL.TOKENREPL.WAIT(段1)を使うかプライマリを読みます。単一ノードのkevyなら考えるべき梯子はありません——すべての読み取りがプライマリです。

フェーズ5——書き込みカットオーバー+ロールバックウィンドウ

書き手をkevyへ切り替え、同じデプロイで逆方向ミラーを開始します。kevyのフィードをSQLとしてRDSへ流し戻すCDCコンシューマです(FEED.READUPDATE/INSERT、クックブックのレシピ13)。RDSはこれで、kevyを数秒遅れで追いかけるウォームスタンバイになります。ロールバック計画は「アプリの向き先を戻す」であって、「逆移行」ではありません。

切り替えの前に、シーケンスキーをRDSの最高水位より上にシードしてください(新しいキーにINCRBY seq:order <rds_max_id>)——古典的なauto-increment衝突が、フェーズ5のいちばん鋭い刃です。

フェーズ6——退役

確信が固まったら(数週間のクリーンなdiffと、一度は実施したフェイルオーバー訓練)、逆方向ミラーを止め、アーカイブ用に最後のRDSダンプを取り、運用の物語を完全にkevyへ移します。スナップショット+AOF(+フィードカーソルのリカバリポイント)はpersistence.md、レプリカ/フェイルオーバー態勢はavailability.mdの通りです。

よくある落とし穴


ツールチェーン(kevy-cli

kevy-cli export  -p 6379 --prefix user: dump.resp
kevy-cli import  -p 6004 --strict dump.resp        # ≥200k cmd/s
kevy-cli import  -p 6004 --resume dump.resp        # after interruption
kevy-cli digest  -p 6004 user:
kevy-cli diff    hostA:6379 hostB:6004 user: order:
kevy-cli copy-prefix   -p 6004 --rate 5000 user: staging:user:
kevy-cli delete-prefix -p 6004 --rate 5000 --dry-run tmp:
kevy-cli inspect -p 6004 user:

ワイヤフォーマット

exportは再構築フレームのプレーンなRESPコマンドストリームを書き出します——DELSET/HSET/RPUSH/SADD/ZADD、TTLには絶対時刻のPEXPIREATです。このためファイルはredis-cli --pipeと双方向に互換です。kevyのエクスポートはRedisに食わせられますし、どんなRESPコマンドファイルもkevy-cli importに食わせられます——RDSのダンプから自作したもの(プレイブックのフェーズ3)を含めて。

キーごとに先頭へ置かれるDELが、リプレイをゼロからの再構築にします——あらゆる型に対して本物の冪等です(RPUSHのような追記verbは、そうしないと再インポートでリスト内容が倍になります)。

一貫性と再開可能性

検証

PREFIX.DIGEST <prefix>(サーバーおよび組み込みのprefix_digest)は[count, hex64]を返します。正準化した行バイトに対する順序非依存のチェックサムです(ハッシュフィールドとセットメンバーはソート、zsetはスコアビット→メンバー順、リストは並び順のまま——リストの順序は同一性そのものです)。シャード数にも挿入順にも影響されないので、トポロジをまたいだ比較ができます。kevy-cli diff A:port B:port prefix…は不一致が1つでもあれば非ゼロで終了します。

TTLはダイジェストに参加しません(減衰するため)。値は参加します。

バルク操作

copy-prefixは読み取り+再構築フレームで各行を新しいプレフィックスの下へキー替えします(サーバーには意図的にCOPY verbがありません。TTLは絶対時刻の期限として運ばれます)。delete-prefixはSCAN+UNLINKです。どちらも--rate N(トークンバケット、最初のopから厳密なペーシング)を取り、deleteは--dry-runをサポートします。

インデックス済みキー空間へのロード

インデックスのready待ち

IDX.CREATEは即座に戻り、バックグラウンドでバックフィルします。完了までクエリは-INDEXBUILDINGを返します。標準の待ち方はIDX.LISTのポーリングです——stateカラムがbuildingからreadyへ切り替わります。

until kevy-cli -p 6004 IDX.LIST | grep -A1 my_index | grep -q ready; do sleep 1; done

テキストインデックスのバックフィル速度はドキュメントサイズに比例します。小さな行なら100万行あたり約7秒ですが、数KBの本文はおよそ100万件あたり85秒でインデックスされます(実測:メールサイズの本文20万件を17秒)。

移行全体を1コマンドで検証する

kevy-cli diffは、2つの稼働中サーバー間で任意個のプレフィックスを1回の呼び出しで比較します——プレフィックスごとのダイジェスト対よりこちらを使ってください。

kevy-cli diff 127.0.0.1:6004 127.0.0.1:6005 msg: mbox: usr: tag: session:

大きなエクスポート

ダンプは非圧縮のRESPテキストです(高速で、grep可能)。10GB超のキー空間ではgzipをパイプしてください——フォーマットはストリームフレンドリーです。

kevy-cli export -p 6004 /dev/stdout | gzip > dump.kevy.gz
gunzip -c dump.kevy.gz | kevy-cli import -p 6005 --strict /dev/stdin

--resume.progress サイドカーのために実ファイルが必要です——再開可能性が欲しければ、先にディスクへ展開してください。)

インデックスの作成はバルクロードのに。インデックスエンジンのバックフィルは既存データからバルク速度で構築し(実測で100万行あたり約7秒)、書き込みフックの維持コストを100万回払うのに勝ります。スイッチではなく、操作の順序の問題です。

kevy-cli import -p 6004 dump.resp
kevy-cli -p 6004 IDX.CREATE users ON PREFIX user: FIELD age TYPE i64 KIND range

ゲート:bench/onrampgate.sh(100万行ラウンドトリップ、インポート200k cmd/s以上、kill -9からの再開収束、レート精度±20%)。