kevy
このページ

6.2 から 6.3 へのアップグレード

要点:あなたが書いたもので動かなくなるものはありません。 API は動いていません。crate の形も変わりません。データディレクトリは双方向に開き、6.2.x のレプリカは 6.3.0 のプライマリと会話します。番号を上げれば終わりです。

kevy-embedded = "6.3.0"

このページの残りは、6.3.0 で何ができるようになったか、それぞれがどんな場面のためのものか、そして「もともと間違っていたから直した」唯一の応答についてです。

早見表

こうしているならすること
サーバーやバインディングを使っているバイナリを入れ替える/パッケージ番号を上げる。それだけ
Rust から組み込んでいるkevy-embedded6.3.0
HGETALL で hash からサンプリングしているHRANDFIELD が使えます —— §3
接続時に notify-keyspace-events を設定するクライアントを使っている通るようになりました。設定ファイル側の回避策は削除できます —— §1
HELLO 3 を交渉し、かつ型で復号している5 つの応答が本来の型を持つようになりました —— §2
GEOPOS のエラーを処理している壊れた形の応答 1 件が、素のエラーになりました —— §4
ghcr.io/goliajp/kevy:latest を pull しているもう一度 pull を。このタグは 6.3.0 を指します

1. CONFIG SET notify-keyspace-events がエンジンまで届きます

場面。 キーの期限切れや keyspace イベントを購読するライブラリ——Spring Data Redis の RedisKeyExpirationEvent、socket.io の Redis アダプタ、いくつかのジョブキュー——を使っている。あるいは expired / evicted の通知が欲しくて、ワイヤ越しには設定できないので設定ファイルに書いていた。

何が間違っていたか。 keyspace 通知は以前から動いていましたが、設定ファイル経由だけでした。そこでのキー名はアンダースコアの notify_keyspace_events です。Redis はワイヤ上ではハイフンで綴り、その二つを橋渡しするものがありませんでした。つまりこのパラメータは、接続の上では読むことも書くこともできなかったのです。接続時にこれを設定するライブラリ——それが普通のやり方です——は、エンジンが実は持っていた機能について、最初の 1 秒で ERR unknown parameter に出会っていました。

することは。 回避策を入れていたのでなければ、何もありません。自分で設定したがるクライアントのために設定ファイルへ書いていたのなら、その回避策は削除して、クライアントに仕事をさせてください。

CONFIG SET notify-keyspace-events Ex     → +OK
CONFIG GET notify-keyspace-events        → "Ex"
CONFIG SET notify-keyspace-events ZZZ    → -ERR CONFIG SET failed for
                                           'notify-keyspace-events': unknown flag char 'Z'

フラグは Redis のものです:K keyspace、E keyevent、g generic、$ string、l list、s set、h hash、z zset、t stream、x expired、e evicted、n new-key、そして Ag$lshzxet の別名(A に関する Redis の取り決めどおり、n 以外のすべてのクラス)。フラグは保存して黙って無視するのではなく、書き込み時に検証されます。打ち間違いは、打ち間違えたその瞬間にエラーになります。イベントが必要になった瞬間の沈黙ではなく。


2. 5 つの RESP3 応答が本来の型を持つようになりました

場面。 クライアントが RESP3 を交渉し——HELLO 3——応答の型で復号している。protocol=3 の redis-py、v5 以降は既定でそうする node-redis、そしてそれらの上に建っているもの。クライアントが RESP2 を話しているなら(多くの構成でいまも既定です)、この節は関係ありません。

何が間違っていたか。 5 つのコマンドが、RESP3 を交渉済みの接続に RESP2 の形を送っていました:

コマンド送っていたものいま送るもの(Redis 8.10.1 と同じ)
ZPOPMINスコアが bulk stringスコアが double
ZADD … INCR新しいスコアが bulk string新しいスコアが double
GEOPOS座標が bulk string座標が double
SPOP key countarrayset
HRANDFIELD … WITHVALUES平坦なリストペアの入れ子

そのため型で復号するクライアントは、数値を期待した場所で文字列を、集合を期待した場所でリストを受け取っていました。多くのクライアントは暗黙に変換するので、これはエラーではなく「データの型がひそかに違う」という形で現れます。長く気づかれなかったのはそのためです。

することは。 自分で変換していたなら(RESP3 の ZPOPMIN のあとに float(score) など)、その変換はいまや冗長ですが無害です。RESP3 接続の上で RESP2 の形を記録したゴールデンファイル/スナップショットのテストがあれば、録り直してください。そのテストが固定していたのは欠陥です。

どう見つけたかは、何を信用してよいかの話でもあります。bench/resp3gate.sh は、固定した Redis に対して「HELLO 3 の下でどの動詞が形を変えるか」を尋ね、kevy が寸分違わず同じ場所で動くことを要求します。「RESP3 対応だと我々が思っているコマンド」の手書きリストではありません。リストは毎回、対戦相手から来ます。見つかった数が不自然に少なければ、ゲートは通過を拒みます。形を変える動詞は 11 個。kevy はいまその全部で一致しています。


3. HRANDFIELD を実装しました

場面。 hash からフィールドを抽出している——フィーチャーフラグ、A/B のバケット、シャードされたジョブキュー、「いくつか見せて」系のエンドポイント——けれど、HGETALL で hash 全体を引っぱってきて自分のコードで選んでいた。

HRANDFIELD key                      → フィールド 1 つ
HRANDFIELD key 5                    → 最大 5 つ、「重複なし」(hash が小さければ少なくなる)
HRANDFIELD key -5                   → ちょうど 5 つ、「重複あり」
HRANDFIELD key 5 WITHVALUES         → 各フィールドとその値

count の符号が API のすべてです。 正なら部分集合:フィールドは重複せず、hash のほうが小さければ頼んだ数より少なく返ります。負なら標本:ちょうど |count| 件で、同じフィールドが二度来ることがあります。これは Redis の取り決めで、kevy はそのとおりに従います。キーが無いときと count が 0 のときに空配列を返すところまで同じです。

何が節約できるか。 大きな hash への HGETALL は、3 つのフィールドを選ぶために hash 全体をワイヤに載せます。HRANDFIELD key 3 は 3 つだけ載せます。数千フィールドの hash をリクエストごとに 1 回サンプリングするなら、それは毎回のキロバイトと数十バイトの差であり、支配的なのは応答のほうです。選択そのものはフィールド列に対する部分 Fisher-Yates で、返す前半分だけをシャッフルします。

kevy の 4 つの hash 表現すべて(パック行を含む)で動くので、あるキーがいまどれを使っているかを知る必要はありません。


4. 型の合わないキーに対する GEOPOS

これが唯一変わった応答であり、既存のコードが気づきうる唯一の場所です。

以前は、文字列を保持するキーに GEOPOS を尋ねると、配列ヘッダが出たあとにエラーが続いていました——*1\r\n-WRONGTYPE …——ので、その応答を読むクライアントには「最初の要素がエラーである配列」に見えていました。型が解決される前に、ヘッダはもう出てしまっていたのです。

いまは -WRONGTYPE Operation against a key holding the wrong kind of value を返し、それ以外は何も返しません。Redis の答えと同じです。

気づくのは誰か。 応答がエラーそのものかどうかを見るのではなく、reply[0] にエラー標識があるかを調べていたコードです。そのコードは、これからはエラーがあるべき場所でエラーを見ます。多くのクライアントライブラリは旧来の形をすでに壊れた応答として扱っていたので、実際には壊すのではなく直すほうに働きます。

他の 3 つのケースは変わりません。実在するメンバーは座標を返し、存在しないメンバーと存在しないキーはどちらも null 配列を返します。


そのまま引き継がれるもの

  • ワイヤ(§2 と §4 を除く)。 他のすべての RESP2 / RESP3 応答は 6.2.2 とバイト単位で同一です。
  • データディレクトリ。 AOF、スナップショット、value log、各種 checkpoint はこれまでどおり、双方向に開けます。移行の手順はなく、片道の扉もありません。
  • レプリケーション。 ストリームは変わりません。HRANDFIELD は読み取りなのでストリームに入らず、このリリースはレプリケーション経路に何も触れていません。6.2.x のレプリカと 6.3.0 のプライマリはどちらの向きでも組め、1 ノードずつ上げられます。
  • すべての crate とバインディングの API。 6.2.2 から 6.3.0 へ、コードの変更なしに移ります。

6.3.0 が他に変えたこと——挙動ではなく、計測

以下はどの挙動にも影響しませんが、公開された数字にどれだけの価値があるかを変えます。

  • ベンチマークの対戦相手はすべて正確なバージョンに固定され、それぞれの上流の latest stable と照合されます。 Redis 8.10.1、valkey 9.1.2、Dragonfly 1.40.2、postgres 18.6、加えて適合性スイートが駆動する 4 つのクライアントライブラリ(go-redis 9.22.0、StackExchange.Redis 3.1.31、node-redis 6.2.1、redis-py 8.1.0)。このうち 2 つは 2024 年から凍っていて、2 つはバージョンを名乗ってすらいませんでした。いま計測装置は各エンジンに自分のバージョンを尋ね、食い違えば数字を出すことを拒みます。だから公開された表は、対戦相手をパッチ番号まで名指しできます。固定値を上げるのは編集ではなく、書かれた手順です——.claude/skills/competitor-anchors/SKILL.md
  • 固定した Redis が提供するコマンドは、いまやすべて説明がつきます。 その 599 のコマンドとサブコマンドのうち、kevy は 206 の動詞を実装しています。残りのうち 256 は理由を書いた上での除外、80 は名前のある RFC が引き受けています。未分類はゼロで、その数はラチェットに載っていて下がることしかできません。だから将来 Redis がコマンドを増やしたとき、それは沈黙としてではなく「下すべき決定」として着地します。必要な動詞が欠けているのではと思ったことがあるなら、答えは試して確かめるものではなく、bench/COMMAND-COVERAGE.json に書かれています。

6.3.0 の実測値——lx64、3 巡、セルごとの中央値、各エンジン自身のコマンドカウンタ、-c 50 -P 16

動詞kevyRedis 8.10.1対 Redis
GET7,489,119/s5,631,398/s1.33x
SET6,824,662/s2,567,607/s2.66x
INCR6,753,558/s3,294,927/s2.05x
SADD6,152,617/s3,753,131/s1.64x
HSET4,002,580/s2,966,288/s1.35x
ZADD3,242,967/s2,818,626/s1.15x
LPUSH3,142,699/s2,860,306/s1.10x

7 セルすべてで、kevy の最も悪い巡回がどの対戦相手の最も良い巡回にも勝ちます。最も狭いのは LPUSH と ZADD で、Redis に対して 1.08x。このリリースで配信経路は変わっていないので、これは 6.2.2 の数字を速くしたものではなく、測り直したものです。valkey と Dragonfly、巡回間のばらつきを含む完全な記録は bench/PERF-LEDGER.md にあります。


入手

# Cargo.toml
kevy-embedded = "6.3.0"
npm install @goliapkg/kevy@6.3.0          # wasm
npm install @goliapkg/kevy-node@6.3.0     # Node ネイティブ
npm install @goliapkg/kevy-bin@6.3.0      # サーバーバイナリ
pip install kevy==6.3.0
go get github.com/goliajp/kevy-go/v6@v6.3.0

ghcr.io/goliajp/kevy:latest を使っているコンテナ利用者は、次の pull で 6.3.0 になります。タグを固定しているなら ghcr.io/goliajp/kevy:6.3.0 です。