ReadAware

Lake Baikal:勝ち取るしかなかった同期

6週間前、蔵書がなぜあなたのデバイスに置かれるのかについての投稿で、ひっそりと約束をひとつ残しました。「同期は、そのうち」。バージョン0.5.0——Lake Baikal——が、その「そのうち」です。安定版のない期間は18日間に及び、このプロジェクトがこれほど長く沈黙したのは初めてのことでした。この空白には、きちんと説明が要るでしょう。

同期がいちばん難しい約束である理由

普通のアプリにとって、同期は機能のひとつです。ローカルファーストのアプリにとって、それは自分が信じていると公言してきたすべてを試す試験です。

私たちは、同期を残酷なまでに容赦のないものにしてしまう二つのことを、すでに約束してしまっていました。第一に、あなたのデータはエンドツーエンド暗号化されていること。リレーサーバが保管するのは、パスフレーズから導出した鍵で封をされた暗号文——それだけです。これは箇条書きの一行に聞こえますが、その代償に気づいた途端に話は別で、サーバはあなたのデータを決してマージできず、決して修復できず、デバッグを手伝おうと中身を覗くことすらできません。難問はすべて、構造上、あなたのデバイスの上で解かれるしかないのです。

第二に、ReadAwareのストレージはappend-onlyなイベントログで、アプリが読むテーブルは、そこから再構築されるプロジェクションだということ。つまり同期の単位は「あなたのbooksテーブル」ではなく、これまでに起きたすべてのイベントであり、オフラインの間にどのデバイスが何を書いたにせよ、最終的には同じ順序へ収束しなければなりません。二台目のデバイスにあなたの読書人生の全体をリプレイさせ、同じ蔵書に到達させるのは、ファイルをコピーするというより、自分の過去についての定理を証明するようなものです。

そこでLake Baikalは、遅い道を選びました。順序付けのためのhybrid logical clocks。巨大なバックログがプロジェクションを、ページごとに一度ずつではなく一度きりで再構築するようにした、バッチ化リプレイ。200 MBのスキャン本が再開可能な断片に分かれて旅をする、チャンク化アップロード。ブラウザのリンクからアプリを開けるサインイン。そして、デスクトップで付けたハイライトが、あなたがスマホを手に取るより先にそこへ届いている、ライブなpushチャネル。テーマ、タイポグラフィ、AI設定、APIキー——設定すらローミングしていくので、できたてのインストールも、一分もすればあなたのものらしく感じられます。

幽霊本

初めての本格的な二台運用で、このリリースで一番のお気に入りバグが生まれました。新しいデバイスには、表紙だけが存在してページのない本が現れたのです。幽霊です。

判明したのは、三つの独立した失敗が、それぞれが次の失敗を覆い隠すほど見事に積み重なっていた、ということでした。ブートストラップは、バイトのアップロードが完了していないファイルのマニフェスト行までコピーしていた。あるプラットフォームでは、webviewのセキュリティポリシーが、アプリからリレーへの接続を黙って塞いでいた。そしてリレーが、無料枠のサイズ上限超えを理由にファイルを拒否すると、その拒否は——黙って——誰の目にも触れない列に記録されていた。静かな失敗が三つ、不気味な症状が一つ、そして数日がかりの鑑識作業。

修正は、半分がコードで、半分が哲学でした。同期にはいま、本ごとのパネルがあり、何がクラウドに届いていて、何が届いておらず、その理由は何かが、正確に表示されるようになっています。黙って失敗することを許されたシステムは、黙って失敗する。唯一の治療法は、その状態を見えるようにすることです。

幽霊たちが求めた監査

あの「黙って記録された拒否」がずっと尾を引いていたので、同期が安定したところで、アプリ全体のエラーハンドリングを監査することにしました。結果は身の引き締まるものでした。生の内部エラーをそのままユーザーに見せていた箇所が十四か所、ユーザーが知るべきだった失敗を握りつぶしていた箇所が八か所、そしてインポートに失敗した後でデータを静かに削除しうるマイグレーションパスが一つ。

Lake Baikalには、その作り直しが全部入っています。いまやどの失敗にも安定したコードが付き、ローカライズされた、次の一手を示すメッセージとして表示され——スタックトレースは決してなく——「空」と「壊れている」を区別し、再試行が本当に助けになりうるときにだけ再試行を提案します。そして、万が一アプリがクラッシュしてしまったときは、次回起動時に診断レポートの送信を提案します(訊ねるのです——勝手に送信することは決してありません)。

配列ひとつぶんの壁

あるユーザーから、AndroidではTXTファイルがインポートできない、という報告が届きました。私たちは困惑しました。TXT対応は数か月前にリリース済みのはずです。そして、その両方が真実でした。フォーマットへの対応はアプリの中に生きていました。一方、壁のほうは、Androidのシステムのファイルピッカーに渡す、ハードコードされたMIMEタイプ一覧の中に生きていました——TXT対応が生まれるより前に書かれ、それっきり一度も更新されていない一覧です。すべてのAndroidデバイス上のすべての.txtファイルが、たった六行の設定によってグレーアウトされていたのです。

これが、このリリースで最も多くを教えてくれるバグです。機能とは、コードにできることではなく、ユーザーがたどり着けるもののこと。いまや私たちは、その「たどり着ける範囲」をテストしています。

湖にはほかに何があるか

本の記憶が劇的に速くなりました——何時間ものアイドル時間をかけて構築されていたキャラクターとコンセプトのグラフが、いまや本を開いて最初の数分のうちに、あなたと競争するくらいの勢いで組み上がります。そしてアシスタントは、それを毎回のプロンプトにホッチキス留めするかわりに、ツールのようにクエリするようになりました。いまやアップデートチャネルは二つです(追い越し車線がお好きなら、Betaを)。価格つきの本物のOpenRouterモデルピッカー。アプリ全体のテーマを変え、ロールバックつきで自分自身をアップデートできるプラグイン。そして、マウスホイールでのページめくり。

同期は無料枠つきでリリースされます。ライブラリが無料枠に収まらなくなったら、有料プランでストレージを増やせます——率直に言えば、それがリレーの運営資金です。

Lake Baikalは、世界の淡水の五分の一を湛え、一メートルの氷の下でも凍らず、これまでに受け取ったすべてのものを静かに保ち続けてきました。どんな一台のデバイスに何が起きようと、ライブラリが生き延びることを覚えたリリース——その名前として、これはふさわしいと思えました。湖は深い。あなたの本を、どうぞ。

← 記事一覧