本日は、London Tech TalkのBookclub、Database Internalsの振り返りを収録していきます。
今回は、Chapter 8、Distributed Systems Introduction and Overviewについて振り返っていきます。
今回の章からは、折り返しということで、Chapter 8から、分散システムにおけるデータベースの振る舞いについて、本章は話を広げていく形になっていますね。
今回の章の目玉は、何と言っても分散システムの難しさを多角的に理解することにあったと思います。
考えるポイントは、それぞれのマシンの状態、ステート、ステートマネジメントですね。
マシンごとに異なる状態を持つのが分散環境ですから、その異なるマシン間の状態をどうやって整合性を保つのか、という問題の難しさをイメージできるかどうかが鍵でした。
例えば、どれがデータベースのリーダーですか、という情報をそれぞれのマシンが別の認識をしてしまったら、データの整合性がとれず大きな問題につながってしまいますよね。
そういった分散環境でデータベースをデプロイし運用していくことによって、いかに難しいのか、そしてそれをどのようなテクノロジーやアルゴリズムで解決しているのか、というのが分かるのが第2部の醍醐味となっています。
第8章から後半にかけての第2部を読むことで、ロンドテックトークのゲストを中心としたディスコードがあるんですけれども、そこでそのブッククラブの参加者同士と読んだ後とか読む前とかに関連記事を共有したりとか議論したりするんですけれども、
SQLite3というのはシンプルでネットワーク通信のないただのファイルなので対象外性も高くて素晴らしいという長文コメントをしてしまったんですけれども、
それを見た参加者の方はその気持ちが第8章以降を読んでいくことで伝わったのかもしれません。伝わるんじゃないかなと思います。
そのディスコードのコメントについて簡単に紹介しますね。
そこでどんなことを書いたかというと、SQLite3について紹介されているスライドを参加者の方の一人が共有してくれて、僕も実はSQLite3をエキサイトしているせいだったので、僕なりの意見を共有させてもらいました。
SQLite3というのは結局はただのファイルなのでシンプルだし、シンプルであるというのが一番だと。ネットワーク通信のないただのファイルなのでレジリエンシーという観点でも高くて素晴らしいみたいなコメントをしたんですね。
SRAなので対象外性というのはなかなか個人的には重要な観点の一つとなっています。
実は僕の理解ですけど、SQLiteにエキサイトしている僕たちのような人たちの背景には少し深い理由があるんじゃないかなと思っています。
それはマイシークエルとかポスグレみたいなクライアントサーバーモデルのRDBMSにとって代わってSQLite3がこれからの覇権を握るぜっていうことではなくて、
SQLite3ってローカルで動かすデベロップメントのツールだと思ってたけど、意外と結構できるじゃんみたいな再発見が背景にあるのではないかなと思っています。
そのシンプルさとかハードウェアの進化と相まって意外とリードさばけるよねとか、意外とライトボトルテックにならないよねみたいなスケーラビリティも確保できる点に注目が集まっているのがここ数年の話なんじゃないかなと僕は思っています。
特にAIアシステットコーディングとか LLM を使ったプロダクションのアプリを作っていくときに個々人の開発スキルっていうのがChart GPT とかはLLM のモデルによってレバレッサレーターとプロトタイピングの速度が圧倒的に速くなってますよね。
その結果、最初からプロダクションにデプロイするツールの候補として、ローカルでポンと置いてすぐに使えるSQLite 3 でデプロイしても最初のプロトタイピングのそこそこスケールが必要なレベルまでは既存のプロダクトを使ってちゃんとスケールするSQLite 3 っていうのがそのテックスタックとして非常に注目されているんじゃないかなと思っています。
だからその AI を中心とするスタートアップとか、小規模なプロトタイプ開発で幅を大き化しているのがSQLite じゃないかなと思っています。
だからもちろんエンタープライズスケールとか、もっともっとレートステージのスタートアップとかでデータベースを RDBMS でデプロイするときに、MySQL とか Postgreのようなクライアントサーバー型のデータベースがなくなるとは思ってないんですけれども、
SQLite 3 が幅を利かせるポイントっていうのがその時代背景の変化によって増えてきたんじゃないかなと思っています。
その中で、最近SQLite が特にスポットを浴びているのは、Tulso という企業が非常に頑張っているからなんですね。
これご存知の方もいるかもしれないんですけど、もともとSQLite 3 は2000年代初頭から長年開発されてきたバックワードコンパチビリティ命みたいな、
そういう広報互換性命みたいな哲学を持つ、その DBMS における枯れた技術ナンバー1といっても過言ではない存在だったんですね。
そのSQLite っていうのはご存知の方も多いと思いますけど、 Firefox とか iOS のモバイルとかもいろんなエンベデッドの組み込みのところで使われている、
かなりデバイス数としては数億単位で世界中で動いているデータベースというか、ファイルシステムといっても過言ではないんですよね。
だからバックワードコンパチビリティを担保するというのがSQLite がこれだけ広がった理由でもあるし、それを広がり続けるために合法互換性を削らないという思想になっています。
これを言い換えると、互換性を考えすぎて新しい機能を全然入れられていないんじゃないかっていうある種のもどかしさも感じている開発者も結構いました。
そんな中、さっき言ったTulsaがDevSQLというSQLiteのフォークを開発し始めたんですね。
これはGitHubでオープンソースになっているんですけど、これは今までのSQLite 3にはなかったオープンソースでモダンな機能をガンガン取り入れていくぞみたいな。
その一方で互換性はそこまで気にしないよという、まさに新しい風を吹き込み始めたんですね。
これがとても理にかなっていて、例えばロンドンテックトークの以前のブッククラブが収録でも話したんですけれども、
その互換性のために例えばSQLite 3にはジャーナルモードっていうコンフィグレーションがあって、
これのデフォルトは、最近で言うとワルを使うことがライトハイトログモードにすることが多いんですけども、
デフォルトは決してワルにはならないという話をしたんですね。
これはデバイスとかエッジの文脈では重要なんですけど、トゥルスとかAIプロトタイピングのように、
ウェブアプリとかプロトタイピングにおけるデスクトップアプリで使うという分では、
最近のマシンのパワーとかトレンドに合わせて新しい機能をデフォルトで積極的に取り入れてほしいというニーズがあるんですよね。
例えばそのトレンドの中でも目玉機能となっているのが、ベクターサポートですね。
ベクターっていうのはベクタライズ化されたデータのことで、
これは例えばLLMベースのアプリケーションを作るときとかに、
例えばテキストデータとか音声データをベクタライズして保存したいみたいな要件がすごいあります。
ベクターデータベースなんてことを聞いたことある方もいると思うんですけども、
ストリングとかバイナリでもちろん表現できるんですけども、
ベクターをデータベースのファーストシチズンとして、
デフォルトネイティブでサポートされたデータ型として扱えることで、
より開発するときによりパフォーマンスの出る形で、
より自然な形でデータベースを使えるっていうところがポイントになってくるので、
最近データベースのスタートアップを見ていると、
どこの会社も投資家とかマーケットからのプレッシャーを受けて、
ベクタライズサポートっていうのはどこもしてます。
これはPOSGREとかも拡張機能でサポートしているわけなんですけど、
ほとんどの人が慣れているRDBMSがベクター化されたデータを保存できるというのは、
AIエンベディングアプリケーションを作る人にとってはとても楽しい機能ですよね。
もちろん大規模になっていれば専用のベクターデータベースを使ったりとか、
そういうケースも出てくるかもしれないんですけども、
最初はSQLite 3にベクターデータを入れちゃえばそこそこ動くんだよっていう、
そういうAIエンベディングアプリが簡単に作れるっていうのは、
これはプロトタイピングのフェーズにおいてかなり重要になってきますよね。
裏側でオープンAIなどを叩いて、
帰ってきたベクターデータ、PDFとか音声などのベクトル化したデータをSQLiteに入れるだけでいいですからね。
さらに最近のトレンドでいうとTorsoだけではなくて、
fly.ioっていうところにSQLite 3のエキスパートが人材がいるんですけども、
彼がSQLite 3を分散環境で動かすのに必要なlightstream.ioというリカバリーツールであったり、
これはfly.ioのプロダクトとして出ているlightfsというファイルシステムとして動くようなコンポーネントがあるんですけど、
もう開発してますと。
これによってDockerに.sqliteファイルをポンと置くだけで作ったプロトタイプが、
もしうまくいって当たって大きくしたいなってなった時も、
すぐにMySQLとかPosgreに移行しなきゃっていうストレスを感じることもなくて、
まずはTorsoとかfly.ioとかでスケールさせればいいかっていうeasyな選択肢が生まれて、
そこそこのスケールまではSQLite 3のままカバーできるようになったよっていうのも、
エコシステムという観点からは非常に大きいんですね。
ということでまとめると、
ハードウェアの進化によってパワフルになってきたSQLite 3が、
AIトレンドの中でベクトル化をサポートしてきたことによって、
AIスタートアップ界隈を中心にエキサイトしている人が増えてきたということなんじゃないかなと僕は思っています。
ということで冒頭からいきなり脱線してしまったんですけれども、
これがこのチャプター8で学ぶ分散システムの難しさとSQLite 3というシンプルなものがいかに現代ニーズにフィットしているかという僕の視点でした。
一人でソロ収録をすると、盛大に脱線しても止める人がいないということです。
それからメッセージ、ネットワークを通して送信されるログとかメッセージの準序保証、オーダーのギャラリティですね。
準序保証についても話が呼びました。
例えばシーケンス番号を管理して順番通りに処理して、次の番号よりも先に届いた場合はバッファリングしておくとか、
既に処理されていたら捨てるみたいなシンプルな説明だったと記憶しています。
イベントソーシングのようなシステムを作るときにもこの準序保証というのは重要な概念になってきますよね。
それからバックエンドエンジニアにとってはおなじみのバックオフとかジターという概念についても触れられましたね。
分散すればするほど遅くなるという現実に対してどのようにクライアント側でリトライ戦略を立てるのかという話でした。
これに付随してThundering Hard Problemの説明などもしました。
個人的に今回の章のハイライトとも言えるのが、
Two Generals ProblemとByzantine General Problemの違いについての議論でした。
本章で紹介されているのはTwo Generals Problemなんですけど、
本章ですね。
Byzantine General Problemというのもありますと、
まずTGPと呼ばれるTwo Generals Problemというのは先ほどもちょっと説明しましたけど、
信頼できないネットワーク通信チャンネルにおけるメッセージの損失とか遅延が主な課題であって、
二人の将軍の間では完璧な合意を達成することは理論的に不可能ですよということを証明した理論です。
一方、ジェネラルという観点でちょっと似ているのでよく聞く。
しかも分散界隈でよく聞くんですけど、
そのByzantine General Problem、BGPと呼ばれるのは、
このちょっと違って、
悪意のあるあるいは故障したアクターですね、
これビザンチン障害というんですけど、
これが存在する環境での合意形成が課題となります。
なぜこのビザンチンというのかというのは、
これを発明したランポート博士というめちゃくちゃ有名な方が書いた、
分散システムにも過大な貢献をしたすごい優秀な博士がいたんですけども、
彼がアナロジーを使って説明するのが上手で、
このペーパーにもそのビザンチンを攻める時の将軍たちのアナロジーを使って表現された感じですね。
この裏切り者と呼ばれる、複数の将軍の中にもし裏切り者がいたらどうするか、
もし裏切り者がいたらビザンチンの将軍たちの間で城を攻めるかどうかの合意を形成できるか、
について書かれた論文なんですね。
ということでビザンチン将軍の話をすると、
もう少しここら辺のコンセンスアルゴリズムの難しさを話したくなってくるんですけれども、
また脱線してきた気がするんですが、
この収録は本書の丸写しというよりは、
本にインスピレーションを受けつつ周辺コンセプトを紹介していくということなので、
また脱線してしまうんですけどお付き合いくださいということで、
このビザンチン将軍問題、そしてこれを解決するコンセンスアルゴリズムの難しさを理解するために、
また別のアナロジーを紹介しようと思います。
僕が自分のkenwagatsuma.comのブログで書いている、過去に書いたブログがあるんですけれども、
コンセンサスアルゴリズムを理解するときに、
人狼ゲームって皆さんご存知ですかね。
人狼ゲームというのはボードゲームとか、
複数人で遊ぶゲームの一種なんですけど、
その人狼ゲームのアナロジーを使って合意形成アルゴリズムを紹介したという、
めちゃくちゃ時間をかけて書いたわりには全然読まれないブログがありまして、
ちょっとこれを機にぜひ紹介したいと思うんですけど、
皆さん人狼ゲームはプレイしたことありますかね。
これはどういうゲームかというと、
村人と人狼チームというのに分かれて、
昼と夜のフェーズがあるんですね。
誰が人狼かというのが分からないんですよ。
村人側と人狼側のそれぞれのゴールというのがあって、
村人側は生き残ること、
人狼チームは村人を襲うこと。
心理戦のパーティーゲームの一つでして、
昼のフェーズではみんなで議論して人狼を特定して、
夜は人狼が村人を襲うというフェーズになっています。
この昼のフェーズで人狼同士に議論するとき、
誰が人狼か分からない。
これは要するにビザンチン問題でいうと裏切り者に当たるわけですね。
この人狼ゲームを分散システムにおける合意形成に例えると、
非常に面白い洞察が得られます。
まず、ゲームの参加者たちはそれぞれが独立したノードだと考えてみましょう。
そして村人たちは正常なノード、
人狼は悪意のあるあるいは故障したノード、
つまり裏切り者の役割を果たすとみなせます。
人狼チームは互いに誰が人狼かを知っていますが、
村人は誰が味方か分かりません。
この情報の非対称性が悪用されて、
人狼チームは村人たちの間に混乱を生み出すことができるんですね。
ヒルの議論フェーズは繰り返しになりますが、
まさに分散システムにおけるリーダー選出とか、
状態の共有プロセスに似ています。
村人たちは議論を通じて人狼を推測しようとするんですけれども、
人狼というのは自分が村人であるかのように嘘をつき、
無実の村人を吊るし上げようとしてしまうんですね。
つまり正常なノードが悪意のなるノードから偽の情報を受け取って、
それが全体の合意形成を阻害する可能性があるわけです。
そして夜の投票フェーズでは意思決定が行われます。
合意形成をみんなでトライしようとするわけですね。
多数決で誰かを追放するんですけれども、
もし人狼が過半数を占めてしまえば、
彼らは一方的に村人を排除できるようになりますよね。
これは分散システムで言えば過半数のノードが悪意を持つ、
あるいは故障してしまう51%攻撃のような状況に例えられます。
この状態に陥るとシステムは正常な合意を形成できなくなり、
全体としてその機能が失われてしまいます。
このアナロジーで人狼ゲームのルールを考えてみるとめちゃくちゃ面白くてですね。
人狼ゲームというのは理論的には最低3人からプレイできることが決められていて、
そして面白くなるたびには5人以上が推奨されているんですね。
5人というのはディストリビューティーとデータベースを管理している人からしたら
ちょっと聞き馴染みのあるマジックナンバーだと思うんですけど、
コンセンサスアルゴリズムにおける最低限必要なノード数とか、
推奨されるノード数に酷似していますよね。
例えば信頼性を確保するためには最低3ノード必要ですし、
対障害性を高めるために5ノードが推奨されるという、
いろんなディストリビューティーとデータベースのドキュメンテーションの
オペレーションページとかでも推奨されているこのマジックナンバーは
この人狼ゲームのアナロジーでもより直感的に理解できるのではないでしょうか。
このように人狼ゲームはですね、分散システムにおける裏切り者の存在が
合意形成にいかに大きな影響を与えるか、
そしてシステムがどのように多数決原理や情報交換を通じて健全性を保とうとするか