kevy4.0

kevyのアップグレード

章は2つ、新しいものから。3.x → 4.0(APIの定義に関するメジャー。ワイヤとディスクはそのまま引き継がれ、Rustの顔は一度だけ変わって、以後は凍結されます)と、2.x → 3.x(能力のメジャー。すべてが引き継がれました)です。各章は、何が自動でアップグレードされ、何にコード変更が要り、そしてどう戻すのかを明示します。


3.x → 4.0

4.0は「産業運用に耐える」という宣言です。semverのメジャーは意図的に使いました——長年たまってきた公開APIの負債をすべて払うための、1回きりの破壊の窓です。そしてこれ以降、各面は凍結されます。以後の4.xリリースは追加のみです。ワイヤ越しにkevyと話しているなら、4.0はバイナリの差し替えです。kevyのcrateをリンクしているなら、短く機械的な移行を見込んでください——以下の変更にはどれも、1行のルールがあります。

TL;DR —— バージョンの一覧

コンポーネント3.x時代4.0やること
kevy(サーバー)3.18.x4.0.0バイナリを差し替え、同じデータディレクトリで再起動する
kevy-embedded3.18.x4.0.0バージョンを上げ、下のAPI表を適用する
kevy-client1.14.x4.0.0バージョンを上げる —— バージョンラインの統一(kevy-embeddedが3.0.0でやったのと同じ動き)+ API表
kevy-client-async1.1.x4.0.0同上
kevy-wasm / @goliajp/kevy(npm)4.0.04.0での新顔 —— docs/wasm.md
インフラcrate(kevy-storekevy-rt、…)3.18.x4.0.0ワークスペースのバージョンに追従する

自動で互換なもの

ワイヤプロトコル。 RESPは変わっておらず、verbのエイリアス(SLAVEOFHMSET、…)もすべて保たれています。Redisのクライアント、スクリプト、redis-cliのセッションは以前どおり動きます。valkey 9.1に対する応答パリティのスイートは、今もCIのゲートです。

スナップショットとAOF。 4.0のバイナリは、3.x(および2.x)のスナップショット形式をすべて読み、3.xのAOFを変更なしにリプレイします。形式は4.0で変わっていないため、3.18へのダウングレードも同じくバイナリの差し替えで済みます(3.x → 2.xの方向とは違います。そちらには形式上の縁があります——2.x → 3.xの章を参照)。

Config。 3.xのconfigキーはすべて受け入れられ、意味も同じです。2つのキーだけ、*より厳格に、あるいはより誠実に*なりました——下の「振る舞いの変更」を参照してください(notify_keyspace_eventsの未知フラグの拒否、min_replicas_max_lag_msの強制)。

削除されたノブが1つ。 スナップショット/AOFの*ファイル名*のカスタマイズはなくなりました(kevy-embeddedConfig::with_snapshot_filename / with_aof_filenameビルダー)。オンディスクのレイアウトは、シャードごとにdump-{i}.rdb / aof-{i}.aofで固定になりました。デフォルト名で書かれていたディレクトリは——レガシーな単一ファイルのディレクトリも含めて——そのままロードされます。*カスタム*名で書かれたディレクトリだけ、4.0で最初に開く前に、固定の名前へ一度mvする必要があります。

APIの破壊一覧

この節にあるものはすべて、コンパイル時の破壊であり、機械的な修正があります。明示した箇所を除いて、実行時のセマンティクスは変わりません。

1. flush()のshimを削除

非推奨だったエイリアスはなくなりました。生き残った名前は、自分がやること(ストアを消し飛ばすこと)を名乗ります。

Crate削除代わりに呼ぶもの
kevy-embeddedStore::flush()Store::flushall()
kevy-clientConnection::flush()Connection::flushall()
kevy-storeStore::flush()Store::flushall()

2. エラーの通貨を1つに:KevyError

kevy-embeddedkevy-clientの、失敗しうる公開の面はすべて、io::Result<T>ではなくKevyResult<T>Result<T, KevyError>)を返すようになりました。この型はkevy-storeに住み、両方のcrateが再エクスポートしています。

pub enum KevyError {
    Store(StoreError),      // structured engine errors, no longer
                            // flattened into io::Error strings
    Io(std::io::Error),     // real I/O, preserved via From
    Protocol(String),       // server error replies, wire text intact
    ReadOnly,               // replica write rejection
    InvalidInput(String),   // e.g. URL parse errors
    NotFound(String),
    Unsupported(String),    // e.g. remote-only calls on embedded
    TimedOut,               // e.g. Subscription::recv_timeout
    Closed,                 // stream/bus gone; also terminates
                            // subscriber iterators
}

移行はたいてい、戻り値型の注釈を書き換えるだけです——From<io::Error>From<StoreError>があるので、?はそのまま動き続けます。

// 3.x
fn warm(conn: &mut Connection) -> std::io::Result<()> {
    conn.set(b"greeting", b"hello")?;
    Ok(())
}

// 4.0
use kevy_client::{Connection, KevyResult};

fn warm(conn: &mut Connection) -> KevyResult<()> {
    conn.set(b"greeting", b"hello")?;
    Ok(())
}

エラーを*検査していた*コードは、厳密に得をします——文字列をパースするのではなく、バリアントにマッチさせてください。

3.xでのシグナル4.0
io::Error::other("kevy-store: …")のラッパーテキストKevyError::Store(e) —— 構造化されたStoreError
レプリカの書き込み拒否:io::Error::other("READONLY …")KevyError::ReadOnly(そのDisplayは今もREADONLYで始まります)
サーバーの-ERR …応答が、不透明なio::ErrorとしてKevyError::Protocol(text) —— ワイヤのテキストは保存されます
Subscription::recv_timeoutからのErrorKind::TimedOutKevyError::TimedOut
購読ストリームの消失シグナルとしてのErrorKind::UnexpectedEofKevyError::ClosedSubscriberEvents / SubscriberMessagesのイテレータはKevyResult<_>を返し、これで終端します
リモート専用機能に対するErrorKind::UnsupportedKevyError::Unsupported(msg)

From<KevyError> for io::Errorは意図的に存在しません。その逆向きの辺は、この変更が取り除いた損失つきのダウングレードを復活させてしまうからです。本当にio::Errorが必要な境界では、明示的に変換して、その損失を自分で引き受けてください。

kevy-resp-clientは意図的にio::Resultの顔を保っています——純粋なトランスポートの石であり、io::Errorこそがその誠実な通貨だからです。)

3. コンストラクタの命名:リソースはopen、ネットワークはconnect

種類ごとに1つのverbを、どこでも。ローカルの、ファイルに裏打ちされたものはopen。ピアを持つものはconnect。純粋にメモリ上の値はnew。改名は次のとおりです。

Crate3.x4.0
kevy-clientConnection::open(url)Connection::connect(url)
kevy-clientSubscriber::open(url, channels)Subscriber::connect_channels(url, channels)
kevy-client-asyncAsyncConnection::open(url)AsyncConnection::connect(url)
kevy-client-asyncAsyncSubscriber::open(url, channels)AsyncSubscriber::connect_channels(url, channels)
kevy-resp-clientRespClient::from_url(url)RespClient::connect_url(url)

すでに準拠していて変更がないもの:kevy_embedded::Store::openkevy_persist::Aof::openkevy_store::Store::newClusterClient::connectRwClient::connectSubscriber::connect(url)RespClient::connect(host, port)

4. kevy_rt::Runtimeは、位置引数ではなくビルダーで組む

位置引数のコンストラクタはなくなりました。Runtimeは自分自身のビルダーです。

// 3.x
let rt = Runtime::new([127, 0, 0, 1], 6004, 4, commands);

// 4.0
let rt = Runtime::builder(commands)
    .bind([127, 0, 0, 1], 6004)
    .shards(4);

builder(commands)のデフォルトは、bindが127.0.0.1:6004、シャード1つ、AOFはオン(EverySec)、データディレクトリは"."です。bind / shardsは、既存のwith_*チェーンと同じく#[must_use]のセッターです。これは4.0のインスタンス化作業の、目に見える顔でもあります。Runtimeはもうグローバル状態に触れないため、1つのプロセスで複数の独立したkevyインスタンスを走らせられます。

5. kevy-storeの書き込みは、借用したargvを取る

所有権を渡す形式は削除され、借用する形式(以前の_borrowedの双子)が正規の名前を引き継ぎました。

3.x(所有)4.0(借用、同じ名前)
del(&[Vec<u8>]) / exists(&[Vec<u8>])del(&[&[u8]]) / exists(&[&[u8]])
hset(&[(Vec<u8>, Vec<u8>)]) / hdel(&[Vec<u8>]) / hmget(&[Vec<u8>])hset(&[(&[u8], &[u8])]) / hdel(&[&[u8]]) / hmget(&[&[u8]])
sadd / srem / lpush / rpush / zrem (&[Vec<u8>])名前は同じで、(&[&[u8]])
zadd(&[(f64, Vec<u8>)])zadd(&[(f64, &[u8])])
zadd_flags_borrowed(…)zadd_flags(…)に改名
// 3.x
store.del(&[b"k1".to_vec(), b"k2".to_vec()]);

// 4.0 — pass slices; no allocation
store.del(&[b"k1".as_slice(), b"k2".as_slice()]);

これはAPI変更の衣を着た性能修正です。kevy-embeddedのファサードは借用したargvをそのまま素通しするようになり、組み込みのすべての書き込みパスにあった、呼び出しごとのto_vec()コピーが消えました。

6. Commandsトレイト + Route(独自コマンドセットを持つ組み込み側)

自分でimpl kevy_rt::Commandsを書いている場合にだけ関係します。

振る舞いの変更(コード変更は不要、運用からは見える)

フィーチャシステム(4.0での新顔)

kevy-embeddedはフィーチャで階層化され、小さなターゲットが使う分だけを支払うようになりました。デフォルトは今までどおり全部入りです。

フィーチャ何が加わるか引き込むもの
coreKV + TTL + pubsub + atomic/pipeline(なし)
persistスナップショット + AOFkevy-persist
index宣言型インデックス + ビューkevy-index
text全文(BM25)のセグメントindexkevy-text
vectorHNSWのANNセグメントindexkevy-vector
replicateレプリケーション + CDCフィードpersistkevy-replicate
listener読み取り専用のRESPリスナー(なし)

core階層はmuslターゲット向けにクロスコンパイルでき、強制された予算を持ちます(バイナリ700 KB以下、空ストアのRSS 2 MB以下)。加えて、5つの基盤crateはno_stdでもビルドされます。docs/iot.mdを参照してください。サイズのスペクトルの反対側では、その同じ組み込みコアが@goliajp/kevyとしてブラウザ上でも走ります——docs/wasm.mdを参照してください。

4.0 → 3.18のダウングレード

バイナリを戻すだけです。スナップショットとAOFの形式は共有されています。唯一の縁は、新しい通知フラグ(x/e/n)を使っているconfigファイルです。3.18でもパースは通りますが、そこでイベントが発火することはありません。


2.x → 3.x

kevy 3.xは2.xのスーパーセットです。2.xのワークロードはすべてそのまま動き、アップグレードはサーバーならバイナリの差し替え、組み込みユーザーなら依存のバージョン上げです。この章は、何が自動で引き継がれ、何が名前や数字を変え、そしてどの方向にだけ注意が要るのか(2.xへのダウングレード)を明示します。

TL;DR —— バージョンの一覧

コンポーネント2.x時代3.x(最終:3.18.x)やること
kevy(サーバー)2.0.x3.18.xバイナリを差し替え、同じデータディレクトリで再起動する
kevy-embedded1.x(1.4〜1.16)3.x依存を上げる —— ワークスペース全体が1つのバージョンに統一されたv3.0.0で、1.xのラインは終わりました
kevy-client1.12.x1.13〜1.14上げるだけ。APIは不変
kevy-client-async1.0.x1.1.x上げるだけ。APIは不変
kevy-cli未公開3.xcargo install kevy-cli —— 移行ツールチェーン一式を運ぶようになりました
インフラcrate(kevy-storekevy-rt、…)2.0.x3.xワークスペースのバージョンに追従する

kevy-embeddedの1.xから3.xへの跳躍は、バージョンラインの統一であって、APIの書き直しではありません。1.16の面は3.xに含まれています。Cargo.tomlkevy-embedded = "1"と書いてあるなら"3"に変えて再ビルドしてください——あるいは、上の章とともに一気に"4"へ進んでください。

自動で互換なもの

ワイヤプロトコル。 RESPは変わっていません。3.xも、CIでvalkey 9.1に対してバイト単位の応答チェックを受け続けています(98コマンド)。既存のRedisクライアント、スクリプト、redis-cliのセッションは以前どおり動きます。

スナップショット。 3.xのローダは、2.xのスナップショット形式をすべて読みます(KEVYSNAPのバージョン2〜5:相対TTLのv2ファイル、絶対TTLのv3、ストリームグループのv4、フィードカーソルのv5)。3.xのサーバーを2.xのデータディレクトリに向ければ、ロードされます。

AOF。 AOFはverbのログであり、3.xのverb集合は2.xのスーパーセットです——リプレイはそのまま動きます。appendfsyncのセマンティクスも変わりません。

Config。 2.xのconfigキーはすべて受け入れられます。新しいセクション([replication] single_source--accept-shards、…)は追加的であり、デフォルトは2.xの振る舞いを再現します。

アップグレードの手順

サーバーのデプロイ

  1. 稼働中の2.xサーバーでスナップショットを取り(SAVEまたは通常のバックアップ)、そのコピーを保管してください——理由は下の「ダウングレード」を参照。
  2. 2.xを止め、同じフラグとデータディレクトリで3.xのバイナリを起動します。
  3. 検証:DBSIZEが一致すること。暗号強度の保証が欲しければ、前後でkevy-cli digest -p <port> <prefix>を走らせてください——ダイジェストが等しければ、キー空間は同一です。

レプリカのペアをローリングで上げる場合:先にレプリカをアップグレードし、再同期させ、トラフィックをそちらへフェイルオーバーしてから、元プライマリをアップグレードします。(2.xに管理されたフェイルオーバーはないので、2.xからだとこれは通常の手動の入れ替えになります。3.15以降に乗れば、そのフェイルオーバーの手順自体が1つのverbになります——FAILOVER host portdocs/availability.mdを参照。)

組み込みアプリケーション

  1. Cargo.tomlkevy-embedded = "3"
  2. リビルドします。1.16のAPIはそのまま存在します。新しい能力の面(index/view/text/vector/feed/replication)は、追加のメソッドとConfigのオプションです。
  3. トレイトについて1点。独自のimpl kevy_rt::Commandsを書いていて、かつResolvedCmdをリテラルで構築している場合に限りますが、v2のアークの途中で2つのフィールドが増えました(block_hintwake_idx)。デフォルトのresolve()はそれらを埋めます。リテラルのコンストラクタには、2つのフィールドを足してください。
  4. 組み込み1.xのアプリが書いたオンディスクのデータは、そのままロードされます(サーバーと同じスナップショット形式です)。

クライアント

kevy-client 1.13+ / kevy-client-async 1.1はドロップインです。マイナーの上げは、内部のcrateを3.xのワークスペースに固定し直しただけです。汎用のRedisクライアントライブラリは、どちらにせよ影響を受けません。

3.xで加わるもの(アップグレードする理由)

hydration付きの宣言型インデックス(IDX.*)、名前付きビュー(VIEW.*)、書き込み時の集約(GROUP BY / 分散top-K)、辞書不要のCJK全文検索とBM25、HNSWによるベクトルKNN(さらにBM25+KNNのハイブリッド融合)、recovery-point契約を持つCDCフィード(FEED.*)、組み込みをプライマリにするレプリケーション、機械可読な契約(COMMAND DOCS、生成されるリファレンス、kevy-mcp MCPサーバー)、可用性のアーク(レプリケーションのラグの真実、FAILOVER、クォーラムのクラッシュ選挙、WAIT / REPL.TOKEN / REPL.WAITの整合性ラダー——docs/availability.md)、そして移行ツールチェーン(kevy-cli import/export/--verify/diff/inspect/digest)です。docs/designing-on-kevy.mddocs/cookbook.mdから始めてください。性能の領収書はbench/PERF-LEDGER.mdにあります。

これらのどれも、暗黙に有効化されることはありません。2.xのワークロードを載せた3.xサーバーは空のカタログを持ち、空のカタログに対するインデックスフックはperfgateのラチェットに載っています(2.x比でリグレッションなし)。

ダウングレード(唯一、注意が要る方向)

3.xのサーバーはスナップショット形式v4を書きます。CDCフィードのカーソルが存在すればv5です。2.xのバイナリはv4までしか読めません。

3.xで導入されたverb(IDX.*VIEW.*FEED.*、…)は、当然ながら2.xのバイナリではリプレイされません——それらを使っていたなら、正しいダウングレードはAOFのリプレイではなく、export/importの道です。

バージョン履歴、各1行