Crowi

リアルタイム共同編集の運用

共同編集を本番で動かすための構成・制約・障害時の挙動

リアルタイム共同編集は api プロセスに同居して動きます。別プロセスや別ポートを起動する必要はなく、api コンテナを動かせばそのまま有効です。利用者から見た使い方は リアルタイム共同編集 を参照してください。

動作の前提

  • WebSocket のエンドポイントは api と同じホストの /collab です。前段のリバースプロキシで Upgrade を透過させる必要があります (設定例は デプロイ構成)。
  • 接続は短命のトークンで認証されます。複数レプリカ構成では、そのトークンを発行したレプリカと検証するレプリカが別になるため、署名鍵は全レプリカで揃っている必要があります。
  • ページ本文の真実の源は MongoDB です。Redis はライブのルーティングにしか使いません。全レプリカを止めても、編集内容は MongoDB から復元されます。
  • ページ単位の同時編集者上限 (既定 20) は Redis のカウンタで数えるため、レプリカをまたいでも正しく数えられます。上限を超えた人は読み取り専用で参加します。

関連する環境変数 (WS_TOKEN_SECRET / CROWI_MULTI_INSTANCE / COLLAB_MAX_EDITORS_PER_PAGE / NEXT_PUBLIC_COLLAB_URL / REDIS_URL / REDIS_KEY_PREFIX) は 環境変数 にまとめてあります。

シングルインスタンス構成 (推奨)

最小構成は api + web + MongoDB です。

services:
  api:
    image: crowi/crowi:2.0.0-alpha.17
    environment:
      MONGO_URI: mongodb://mongodb:27017/crowi
      WS_TOKEN_SECRET: ${WS_TOKEN_SECRET}
      CROWI_ENCRYPTION_KEY: ${CROWI_ENCRYPTION_KEY}
      REDIS_URL: redis://redis:6379   # 単一レプリカでは任意
  web:
    image: crowi/crowi-web:2.0.0-alpha.17
  mongodb:
    image: mongo:8

REDIS_URL は単一レプリカなら省略できます。省略したときに機能ごとに何が変わるかは 環境変数 にまとめてあります。

複数レプリカ構成

複数レプリカ構成には、まれに編集内容を失う既知のリスクがあります。 古い状態を読み込んだレプリカが保存を書き戻すと、別のレプリカが書いた新しい状態を上書きしうる余地が残っています。Crowi はページの版を進めるときの照合と、空の内容で上書きしないためのガードでこれを緩和していますが、完全には防げません。同時編集の負荷分散が必要でない限り、単一レプリカ構成をおすすめします。

複数レプリカで動かす場合の追加要件は 3 つです。

  1. REDIS_URL を全レプリカで同じ Redis に向ける。 共同編集の更新がレプリカ間で伝わるようになります。
  2. CROWI_MULTI_INSTANCEWS_TOKEN_SECRET を全レプリカに設定する。 設定の内容は 環境変数 の各行のとおりです。
  3. スティッキーセッションは不要です。 クライアントはどのレプリカに接続してもかまいません。
services:
  api:
    image: crowi/crowi:2.0.0-alpha.17
    deploy:
      replicas: 2
    environment:
      MONGO_URI: mongodb://mongodb:27017/crowi
      REDIS_URL: redis://redis:6379           # 複数レプリカでは必須
      CROWI_MULTI_INSTANCE: "2"
      WS_TOKEN_SECRET: ${WS_TOKEN_SECRET}
      CROWI_ENCRYPTION_KEY: ${CROWI_ENCRYPTION_KEY}

障害時の挙動

  • 起動後に Redis が落ちた。 クライアントは再接続を続けます。同じレプリカにつながっている人どうしの編集は伝わり続けますが、レプリカをまたぐ伝搬は止まります。同時編集者上限は通す側に倒れます。Redis が戻れば伝搬も自動で再開します。
  • 起動時点で Redis に到達できなかった。 api は数秒間再試行したのち、Redis なしの縮退状態で起動します。この場合は Redis が復旧しても、レプリカ間の伝搬は自動では再開しません — api を再起動してください
  • 接続直後に切断され、エディタが「接続できませんでした」のままになる。 たいていは署名鍵がレプリカ間でずれていて、あるレプリカが発行したトークンを別のレプリカが検証できていません。環境変数WS_TOKEN_SECRET の行を確認し、全レプリカを同じ値で入れ替えてください。
  • プロキシが Upgrade を落としている。 /collab への接続が 400 / 426 で失敗します (デプロイ構成 のプロキシ設定を確認してください)。
  • 共同編集中のページが、別の経路 (API・AI ツール・CLI) から更新された。 編集中のクライアントには再読み込みの案内が出て、保存済みの内容から編集をやり直せます。未保存のローカル編集は復旧用のバッファから戻せます。ただしこの案内が届くのは、その更新を処理した api プロセスにつながっているクライアントだけです。複数レプリカ構成では、別のレプリカにつながっているエディタが古い内容のまま編集を続けることがあり、手動で再読み込みするまで気づきません。
  • 共同編集中のページが rename・削除・復元された。 その前に始まっていた編集セッションからの保存は、どのレプリカで操作されたかによらず必ず拒否されます (書き込みの安全性はレプリカをまたいで保たれます)。ただし再読み込みの案内は上と同じくレプリカ内に留まるため、別レプリカのエディタは保存が拒否されて初めて気づくことがあります。
  • api プロセスが落ちた。 全クライアントが再接続を試みます。ヘルスチェックで他レプリカへ振り分ける構成なら自動的につながり直し、編集内容は MongoDB から復元されます。

同時編集者数のチューニング

COLLAB_MAX_EDITORS_PER_PAGE で 1 ページの同時編集者上限を変えられます。既定の 20 は、カーソル表示の描画コストが大きくなる手前の経験的な目安です。大きく上げる場合は、エディタ側の描画負荷もあわせて確認してください。

編集は差分として追記され、定期的にページ本体へ畳み込まれます。追記された差分は 1 時間で自動的に消えるため、1 つのページを編集し続けてもデータベースが線形に増え続けることはありません。

関連ページ

On this page