なぜRedis 6でサポートされるプロトコルはRESP3だけになるのか
原文は Salvatore Sanfilippo により に公開されました。 このブログを購読する
[追記! Stack OverflowのMarc Gravell氏から、後方互換性のためにコネクションごとにプロトコルを切り替え、RESP3を有効にするコマンドを送ればよいのではないかという提案があり、この件全体について考え直しているところだ。それならサーバーの挙動を切り替えるグローバルな設定は不要になる。そう考えると私としてははるかに受け入れやすく、このブログ記事の本質部分についても再検討している]
Redis 5のリリースから数週間、私はRESP3の実装に着手したところだ。数日作業してみて、ついにこれが実現しつつあるのを実感でき、とてもいい気分だ。RESP3は、Redis 6から使われることになる新しいクライアント・サーバー間のプロトコルだ。https://github.com/antirez/resp3にある仕様書を読めば、旧プロトコルであるRESP2のこの進化が、Redisエコシステムをどのように改善するのかが明確にわかるはずだ。ただ、最も重要な点を挙げるなら、RESP3はRESP2よりも「セマンティック」だということだ。例えば、マップやセット(順序なしの要素リスト)、返されるデータに付随する属性といった概念があり、付加的な情報でリプライを補強できるといった具合だ。最終的な目標は、新しいRedisクライアントの負担を減らすこと、つまり、RESP3のあらゆるリプライタイプをクライアントライブラリのプログラミング言語における適切な型へ変換するための固定されたルール一式を決めるだけで済むようにすることだ。
Redisの将来について、私は、内部的にはより賢く、コネクションやパイプライン、状態の処理に最善を尽くしつつ、ユーザーから見える側では一見ずっとシンプルなクライアントを思い描いている。理想的なRedisクライアントが次のようになるくらいにだ。
result = redis.call(“GET”,keyname);
もちろん、その上にさらに高度な抽象化を築くこともできるが、最も下のレイヤーはこのような見た目であるべきだし、返されるリプライは特定のコマンドにアドホックに対応したフィルタリングを必要とすべきではない。RESP3の返却型には、適切なデータ型を返すのに十分な情報が含まれているべきなのだ。たとえばHGETALLはRESP3の「map」を返し、LRANGEは「array」を返し、EXISTSはRESP3の「boolean」を返す。
これにより、新しいコマンドも、クライアントライブラリが*特に*そのコマンドに対応するよう設計されていなくても、期待通りに動作するようになる。RESP2では、代わりに起こっていたのは、そのコマンドが「method missing」のような仕組みで動くことは動くものの、後になってクライアントライブラリでそのコマンドが*実際に*実装された際に、返される型が変わってしまい、微妙な非互換性が生じるということだった。
しかし、新しいプロトコルは旧プロトコルからの段階的な改善ではあるものの、クライアントライブラリ側はもちろん、*アプリケーション層*においても破壊的な非互換性を引き起こすことになる。例えば、ZSCOREは今や文字列ではなくdoubleを返すようになるため、アプリケーションコードを更新するか、あるいはクライアントライブラリがRESP3のリプライを元のRESP2の型に戻す互換性オプションを実装する必要がある。
Luaスクリプトも、新しいプロトコル用に修正しなければ動かなくなる。Luaでもredis.call()コマンドが返す型がよりセマンティックになるからだ。同様に、LuaからもRESP3で実装されたすべての新しいデータ型を返せるようになる。
こうした事情から、私の決断を不安に思う人もいる。私はRedis 6を*RESP3のみ*をサポートする形で出荷するつもりだ。Redis 6のサーバーをRESP2に切り替える互換モードは存在しない。つまり、クライアントライブラリをアップグレードし、アプリケーションをアップグレードするか(あるいはクライアントライブラリの後方互換モードを使うか)しなければ、Redis 6に移行できないということだ。
私にはそうする十分な理由があり、その決断に至った理由と、ユーザーやクライアントライブラリの作者のために問題をどう緩和しようとしているのかを説明したい。まずは緩和策から始めよう。
- Redis 5は、Redis 6のリリース後も2年間は完全にサポートされる。重要なものはすべてRedis 5にバックポートされ、パッチレベルのリリースが継続的に提供される。
- Redis 6は1年から1年半後にリリースされる予定だ。しかしRedis 6は約1か月後にはRESP3に切り替わる。つまり、不安定なRedisのバージョンでありながら新しいプロトコルを使っているものを、人々は長い間使い、試し、向き合うことになる。多くの他のソフトウェアとは異なり、Redisのunstable版には多くの気軽なユーザーがいるからだ。GitHubではデフォルトのブランチであることも、伝統的にRedisのunstableが実際にはそれほど不安定ではないことも、その理由だ。これにより、事前に広く触れられる機会が確保される。
- まだ100%確定ではないが、LuaスクリプトエンジンにはRedis 5と同じ型を返すための互換モードが用意されるかもしれない。ただしこの互換性はデフォルトでは有効にならず、Redisコマンドを呼ぶ前に特別なredis.resp2_compat()関数を呼び出すことで、実行されるスクリプトごとにオプトインする形になる。したがって、どのRedis 6サーバーも、設定にかかわらず同じように振る舞うことになる。過去10年間、Redisが常にそうしてきたようにだ。
緩和策は以上だ。そして、なぜRedis 6で両方のバージョンをサポートしないのか、その理由は次の通りだ。
- ほぼ完全に無意味だからだ。もし人々がRedis 6をRESP2モードに切り替えれば、結局は過去に留まったままで、RESP2のサポートなしで出荷されるRedis 7を待ってすべてが壊れるのを待つだけになる。その間、Redis 6のインストールを扱うとき、その設定によって*何が返されるのか*がまったくわからなくなる。同じクライアントライブラリが、同じコマンドに対してHashを返したりArrayを返したりする可能性がある。
- 正当な理由もなく(「1」を参照)、作業が増え、複雑さが増すからだ。多くのコマンドで、どのフォーマットでリプライすべきかを見るために旧プロトコルかどうかのチェックが必要になる。
- 新しいRedis 6の機能とプロトコルの変更を紐づけることで、ユーザーに切り替えやクライアント・アプリケーションの移植を行う十分な理由を与えることになる。いずれすべてが終われば、新しいことに集中できる。そうしなければ、Redis 6に切り替えたユーザーの一部が新機能のために新しいサーバーに移行しつつも旧プロトコルのままになり、Redis 7でもまた同じ騒ぎが繰り返されることになる。
- クライアントライブラリの対応は大変な作業だと誰かが言うなら、あえて異を唱えたい。確かに変更すべき点はあるが、今サーバー側を実装してみて、そこまでひどいものではないと感じている。むしろひどいのは、ほとんどのクライアントの作業がまったく報われず、情熱と他者と共有したいという意欲だけで行われていることだ。RESP3の実装が短期間で数多く現れることは間違いないだろう。
- RESP3は、クライアントがそれがRESP2なのかRESP3なのかを自動的に検出し、切り替えられるように設計されている。そのため、新しいクライアントはRedis <= 5とRedis 6の両方で動作する。
以上だ。私の視点とその背景にある理由、そして同時にプロトコル切り替えの間に有効になる緩和策が、それほど「ハード」な破壊にはならないとユーザーに納得してもらう一助になれば幸いだ。
記事をランダムに読む
コメント
ログインしてコメントする