一覧に戻る

ブログ

ネットワーク中断をいかに優雅に処理するか

ネットワークは常に障害が起きます。ネットワーク中断時にTactic Remoteがいかに作業やコンテキストを失わないようにしているかを紹介します。

公開日 タグ: engineering / reliability / networking
ブログ

このカテゴリのプロダクト/開発アップデートを確認できます。

カテゴリを見る

短いネットワーク障害の後にスピナーが表示されたまま二度と復旧しないリモートツールを使ったことがあるなら、なぜ私たちが再接続の信頼性にこだわるか理解できるでしょう。モバイル開発ツールの世界では、ネットワーク中断はエッジケースではなく、日常的なケースです。スマートフォンはWi-Fiとセルラーを切り替えます。エレベーターは信号を遮断します。カフェのネットワークは本質的に不安定です。

Tactic Remoteの再接続エンジンは一つの原則を中心に設計されています:ネットワーク中断は可能な限りユーザーに透明であるべきで、透明にできない場合は優雅に伝えるべき。

状態マシン

再接続ロジックの中核は、6つの状態を持つ有限状態マシンです:

┌──────────┐     connect     ┌───────────┐
│ INITIAL  │───────────────►│ CONNECTED │◄─────────────┐
└──────────┘                 └─────┬─────┘              │
                                   │                    │
                              disconnect           reconnect
                                   │              success │
                              ┌────▼─────┐              │
                              │ UNSTABLE │──── retry ───►│
                              └────┬─────┘    (1-3s)    │
                                   │                    │
                              3 failures           ┌────┴─────┐
                                   │               │RECOVERING│
                              ┌────▼─────┐        └────┬─────┘
                              │RECONNECT │── retry ────►│
                              │  ING     │  (5-30s)     │
                              └────┬─────┘              │
                                   │                    │
                              timeout (5min)            │
                                   │                    │
                              ┌────▼─────┐              │
                              │DISCONNEC │─── manual ───┘
                              │  TED     │   reconnect
                              └──────────┘

CONNECTED:正常動作。データは自由に流れます。

UNSTABLE:接続試行が失敗しましたが、一時的なものと推測されます。1-3秒間隔でリトライします。UIにはさりげない警告インジケーターが表示されますが、ユーザーの操作を中断しません。

RECONNECTING:複数回の失敗が本当の問題を示唆しています。リトライ間隔は指数的に増加します(5秒、10秒、20秒、上限30秒)。UIに明確な「再接続中」バナーが表示されます。

RECOVERING:接続が再確立されましたが、状態の同期が必要です。クライアントはサーバーに状態デルタを要求し、適用後、整合性確認が完了するとCONNECTED状態に遷移します。

DISCONNECTED:リトライタイムアウト(デフォルト:5分)を超過しました。ユーザーが手動で再接続をトリガーする必要があります。これにより、完全にオフラインのデバイスでの無限のバッテリー消耗を防ぎます。

状態整合プロトコル

再接続は簡単です。整合——クライアントとサーバーを再同期させること——が難しい部分です。

iPhoneが中断後に再接続すると、最後の既知の状態ベクトル(最後に処理したターミナル更新のシーケンス番号、セッションID、タイムスタンプ)をサーバーに送信します。サーバーはこれを使って、クライアントが見逃したものを正確に計算します。

シナリオ1:短い中断(30秒未満)。サーバーの出力バッファにはまだすべての見逃した更新が含まれています。順番にリプレイし、クライアントが順次適用します。ユーザーの視点では、ターミナルが1秒未満で「追いつく」——見逃した出力が高速スクロールとして表示されます。

シナリオ2:中程度の中断(30秒〜5分)。出力バッファが部分的にローテーションしている可能性があります。サーバーは部分リプレイに続いて完全なターミナルスナップショットを送信します。クライアントはリプレイを適用し、スナップショットと照合して一貫性を検証します。

シナリオ3:長い中断(5分超)。サーバーは完全なスナップショットのみを送信します。クライアントは状態全体を置き換えます。これは最も効率の低いパスですが、どれだけ見逃しても正確性を保証します。

type ReconciliationResponse = {
  method: 'replay' | 'replay_and_snapshot' | 'snapshot'
  replay?: TerminalUpdate[]
  snapshot?: TerminalSnapshot
  sessions: SessionState[]
  pendingApprovals: ApprovalRequest[]
}

整合レスポンスにはpendingApprovalsも含まれることに注目してください。Claude Codeがスマートフォンのオフライン中に承認を要求した場合、その要求は再接続の一部として配信されます。これは極めて重要です——これがないと、Claude Codeはユーザーが一度も見ていない承認を待ち続けることになります。

私たちを壊しかけたエッジケース

承認中の切断

ユーザーが承認通知を受け取り、「承認」をタップし、承認がサーバーに届く前に接続を失います。アクションは実行されるでしょうか?

私たちの解決策:承認には確認応答プロトコルを使用します。サーバーはクライアントの承認メッセージを受信しかつクライアントに受信確認を返すまで、承認されたアクションを実行しません。承認中に接続が切れた場合、再接続時に承認が再送信されます。重複する承認は冪等です——「リクエスト#47を承認」を2回送っても1回送った場合と同じ効果です。

オフライン中にセッションが終了したケース

スマートフォンがオフラインの間にClaude Codeタスクが完了しました。再接続すると、セッションは終了状態です。クライアントは最終出力を表示すべきか?完了通知か?両方か?

整合レスポンスにsessionEndStateを含めることで対処しています。クライアントは最終的なターミナル状態をレンダリングし、何が起こったかの要約とともに「オフライン中にタスクが完了しました」バナーを表示します。ユーザーは古い進行中の出力を見つめるのではなく、すぐに結果を確認できます。

Macが再起動したケース

最も難しいシナリオ:スマートフォンがオフラインの間にMacが再起動した場合(システムアップデート、停電、クラッシュなど)。スマートフォンが再接続すると、サーバーには以前のセッションの記憶がありません。

私たちの解決策:Macコンパニオンアプリはセッションメタデータ(セッションID、プロジェクトパス、最後の既知の状態)をディスクに永続化します。再起動後、このメタデータを読み取り、可能な場合は生存しているtmuxセッションに再アタッチします。整合プロトコルはサーバーエポックカウンターによりサーバーの再起動を検出し、増分整合を試みるのではなく、完全な状態再構築を開始します。

パフォーマンス特性

再接続パフォーマンスを3つの指標で測定しています:

視覚復旧時間——ネットワーク復旧からユーザーが現在のターミナル状態を見るまでの時間。ローカルネットワークのP95は1.8秒、Cloudflare Tunnelは3.2秒です。

データ効率——整合中に転送されるデータ量と完全リフレッシュとの比較。60秒未満の中断では、整合プロトコルは完全リフレッシュと比べてデータ転送量を85%削減します。

正確性——状態の不整合に対するゼロトレランス。ランダム化されたタイミングで数千回の切断/再接続サイクルをシミュレートする継続的インテグレーションテストを実行し、最終的なクライアント状態がサーバー状態とバイト単位で一致することを検証しています。

開発者への教訓

リアルタイムモバイルアプリケーションを構築しているなら、私たちの経験から3つの原則をお伝えします:

  1. 初日から切断を前提に設計する。 常時接続を前提に設計されたシステムに後から再接続を追加するのは、間欠的な接続を第一級の関心事として構築するよりも桁違いに困難です。

  2. 冪等性は最良の味方。 クライアントからサーバーへのすべてのメッセージは安全にリプレイできるべきです。この単一の特性だけで、再接続バグのカテゴリー全体を排除できます。

  3. シミュレーターではなく実際のネットワークでテストする。 ネットワーク状態シミュレーターは実世界の障害の全多様性を再現できません——部分的なパケット配信、DNS解決の遅延、TLSハンドシェイクのタイムアウト。私たちは実際のセルラーネットワーク、エレベーターの中、混雑したカンファレンスのWi-Fiでテストしています。

信頼性の高い再接続は華やかな仕事ではありません。うまく機能している時、ユーザーはそれを称賛しません——単に中断に気づかないだけです。しかし失敗すると、信頼は即座に失われます。この投資は価値があると考えており、ユーザーがTactic Remoteを本番ワークフローに頼ることを選んでいることが、その証左だと思います。

Tactic Remote を試す

スマートフォンからコーディングエージェントを操作

Mac、Windows、Linux 上の Claude Code や Codex などに接続。iPhone や iPad で進捗を確認し、次の指示を送れます。