# MQTT Durable Sessions

EMQXにはDurable Sessions機能があり、MQTTセッションとメッセージをディスクに永続的に保存し、高可用性のレプリカを提供してデータの冗長性と整合性を確保します。セッションの耐久性により、効果的なフェイルオーバーとリカバリー機構を実装でき、サービスの継続性と可用性を保証し、システムの信頼性を向上させます。

本ページでは、EMQXにおけるセッション永続化の概念、原理、および利用方法を紹介します。

## 基本概念

EMQXのDurable Sessions機能を理解する前に、EMQXに関するいくつかの基本概念を押さえておくことが重要です。

### セッションと耐久ストレージ

**セッション**：セッションは、EMQX内でクライアント接続ごとに作成される軽量なプロセスです。セッションはMQTT標準で定められたブローカーの動作を実装しており、初回接続、トピックのサブスクライブ・サブスクライブ解除、メッセージの配信などを行います。

**耐久ストレージ（Durable Storage, DS）**：耐久ストレージはEMQX内部のデータベースです。セッションは自らの状態やトピックに送信されたMQTTメッセージを保存するために利用します。耐久ストレージのデータベースエンジンは[RocksDB](https://rocksdb.org/)を用いてデータをディスクに保存し、[Raftアルゴリズム](https://raft.github.io/)を使ってクラスター内でデータを一貫して複製します。耐久ストレージと**Durable Sessions**は混同しないように注意してください。

### セッション有効期限（Session Expiry Interval）

MQTT標準によれば、クライアントセッションはMQTTブローカー内でクライアント接続と状態を管理します。**有効期限（Expiry Interval）**は、クライアント接続が終了した後にブローカーがセッション状態を保持する期間を制御するセッションのプロパティです。本ドキュメントではこのプロパティが重要な役割を果たします。

有効期限が0に設定されたセッションは、クライアントがEMQXに接続している間のみ存在します。クライアントが切断されると、サブスクリプションや未配信メッセージを含むすべてのセッション情報は即座に破棄されます。一方、有効期限が0以外のセッションは、クライアント接続が終了した後もEMQXに保持されます。クライアントがセッション有効期限内に再接続すれば、セッションを再開でき、オフライン時に送信されたメッセージも配信されます。

- MQTT 5プロトコルを使用するクライアントは、`CONNECT`または`DISCONNECT`パケットの[Session Expiry Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901048)プロパティで明示的にセッション有効期限を指定できます。

- MQTT 3.*プロトコルを使用するクライアントの場合、EMQXは以下のルールでセッション有効期限を決定します。`Clean Session`フラグが`true`なら有効期限は0に設定され、それ以外の場合は`mqtt.session_expiry_interval`設定パラメータの値が使用されます。

## EMQXにおけるセッション実装

EMQXは用途に応じて最適化された2種類のクライアントセッション実装を提供しています。

- **レギュラーセッション**：実行中のEMQXノードのメモリ上に状態を保持するセッション。ノード再起動時に状態は失われます。
- **Durable Sessions**：状態と受信メッセージを耐久ストレージにバックアップするセッション。EMQXノード再起動後も再開可能です。

セッション実装の選択はセッション有効期限と`durable_sessions.enable`設定パラメータ（グローバルまたは[ゾーン単位](../configuration/configuration.md#zone-override)で設定可能）によって決まります。選択基準は以下の通りです。

| `durable_sessions.enable` | セッション有効期限 = 0 | セッション有効期限 > 0 |
| ------------------------- | ----------------------- | ----------------------- |
| `false`                   | レギュラー              | レギュラー              |
| `true`                    | レギュラー              | Durable Sessions        |

EMQXはメッセージ耐久性を管理する独自のアプローチを採用しており、レギュラーセッションとDurable Sessionsが共存しつつストレージコストを最小化しています。

### セッション実装の比較

クライアントセッションの管理戦略はサービスの安定性と信頼性を確保する上で重要です。本節では2つのセッション実装の特徴を比較し、それぞれの特性と適用シナリオを理解しやすくし、より適切なデプロイ判断を支援します。

#### レギュラーセッション

この実装はデフォルトであり、EMQX 5.7以前のすべてのリリースで使用されてきました。レギュラーセッションの状態は実行中のEMQXノードのRAMに完全に保持されます。

利点：

- 非常に高いスループットと低レイテンシ。
- クライアントへの即時メッセージ配信。

欠点：

- セッションデータはホストノードが停止または再起動すると失われる。
- 未配信メッセージはセッションのメモリキューに保存され、EMQXのメモリ使用量が増加する。
- メモリ枯渇を防ぐため、EMQXはメモリキューのサイズ制限を設けている。制限を超えると新規メッセージは破棄され、未配信メッセージが失われる可能性がある。

#### Durable Sessions

EMQX v5.7.0で導入されたDurable Sessionsは、セッション状態とDurable Sessionsにルーティングされるメッセージをディスクに保存します。この機能はデフォルトで無効であり、`durable_sessions.enable`を`true`に設定することで有効化できます。

Durable Sessionがトピックフィルターをサブスクライブすると、そのフィルターにマッチするトピックは「耐久性あり」とマークされます。これにより、これらのトピックからレギュラーセッションへのMQTT PUBLISHメッセージのルーティングに加え、ブローカーはメッセージを`messages`という耐久ストレージにも保存します。

メッセージ配信プロトコルはパブリッシャーではなくサブスクライバーのセッションの耐久性に依存する点に注意が必要です。

各Durable MQTTメッセージは、サブスクライブするDurable Sessionsの数や接続状態に関わらず、各レプリカに対して一度だけ保存されます。これにより効率的なメッセージファンアウトとディスク書き込みの最小化が実現します。

耐久ストレージは、EMQXクラスター内の複数ノードにセッションメタデータとMQTTメッセージを一貫して複製することで、高い耐久性と高可用性を提供します。設定可能な[レプリケーションファクター](./managing-replication.md#replication-factor)により、メッセージやセッションのレプリカ数を調整でき、耐久性とパフォーマンスのバランスを利用者の要件に合わせてカスタマイズ可能です。

利点：

- EMQXノードの再起動や停止後もセッションを再開可能。
- MQTTメッセージはメモリキューではなく共有かつ複製された耐久ストレージに保存されるため、オンライン・オフラインセッション双方のRAM使用量を削減。
- 未配信メッセージ数に上限がなく、メモリキューのオーバーフローによるメッセージ破棄が発生しない。

欠点：

- メッセージをディスクに保存するため、システム全体のスループットは低下する。
- Durable Sessionsはレギュラーセッションに比べてレイテンシが高い。メッセージの書き込みと読み込みがバッチ処理されるため、スループットは向上するが、クライアントがパブリッシュメッセージを受信するまでの遅延が増加する。

## Durable Sessionsのクイックスタート

本節では、EMQXおよびMQTTクライアントでDurable Sessions機能を素早く利用する方法と、Durable Sessionsの簡単なワークフローを紹介します。

::: tip 注意

Durable Sessionsを有効にしていなくても、ステップ2〜4を実行するとセッションは保持され、メッセージはクライアントキューに保存されます。違いは、ステップ5でノード再起動後にセッションが永続的に保存されるかどうか、復元可能かどうかです。

:::

1. EMQXでDurable Sessions機能を有効化します。

   デフォルトではDurable Sessionsは無効です。`etc/emqx.conf`ファイルを編集し、以下の設定を追加して機能を有効にします。

   ```bash
   durable_sessions {
     enable = true
   }
   ```

   設定変更後、EMQXを再起動して反映させます。

2. MQTTクライアントの接続パラメータを調整し、セッション耐久性を有効にします。

   例として[MQTTX CLI](https://mqttx.app/cli)（デフォルトでMQTT 5.0を使用）を用います。`--no-clean`オプションで`Clean Start = false`を設定し、クライアントIDを`emqx_c`に指定してEMQXに接続し、`t/1`トピックをサブスクライブします。

   ```bash
   mqttx sub -t t/1 -i emqx_c --no-clean -q 1
   ```

3. クライアントを切断し、セッションが保持されていることを確認します。

   ステップ2のクライアントを切断します。EMQXダッシュボードの**モニタリング** -> **クライアント**ページを開くと、クライアントの状態が**Disconnected**のまま表示され、セッションが保持されていることがわかります。

   ![MQTT persistent session](./assets/session-persistence-list.png)

4. クライアントにメッセージを送信し、メッセージがクライアントキューに保存されることを確認します。

   再度MQTTX CLIを使い、`bench`コマンドで1クライアントから`t/1`トピックに繰り返しメッセージをパブリッシュします。

   ```bash
   mqttx bench pub -t t/1 -c 1 -q 1
   ```

   MQTTプロトコルに従い、`emqx_c`クライアントがオフラインでも、サブスクライブしている`t/1`トピックのメッセージはクライアントキューに保存され、再接続時に配信されます。

5. EMQXノードを再起動し、セッションとメッセージが耐久ストレージから復元されることを確認します。

   EMQXノードを再起動します。クライアント接続操作を行わずにEMQXダッシュボードの**モニタリング** -> **クライアント**ページを開くと、クライアントが**Disconnected**状態で表示され、セッションが復元されていることがわかります。

   同じクライアントID `emqx_c` と `--no-clean` オプション（`Clean Start = false`）を指定してEMQXに再接続します。

   ```bash
   mqttx sub -t t/1 -i emqx_c --no-clean -q 1
   ```

   オフライン期間中に受信したメッセージが現在のクライアントに配信されます。

   ```bash
   ...
   [2024-5-22] [16:14:14] › …  Connecting...
   [2024-5-22] [16:14:14] › ✔  Connected
   [2024-5-22] [16:14:14] › …  Subscribing to t/1...
   [2024-5-22] [16:14:14] › ✔  Subscribed to t/1
   [2024-5-22] [16:14:14] › payload: Hello From MQTTX CLI
   ...
   ```

   ::: tip 注意

   - 永続セッションを復元するには、同じクライアントID `emqx_c` を使用し、`--no-clean`オプションで`Clean Start`を`false`に設定する必要があります。この2つの条件を満たす必要があります。
   - 以前のサブスクリプション情報はセッションに保存されているため、再接続時に`t/1`トピックを再度サブスクライブしなくてもメッセージは配信されます。

   :::

## 耐久ストレージのアーキテクチャ

Durable Sessionsはセッション状態とメッセージの永続化に耐久ストレージを利用します。このストレージ層の構造と動作については、[Design for Durable Storage](../../develop/design/durable-storage.md)の*Architecture: Backends and Storage Hierarchy*セクションを参照してください。

## 耐久ストレージがDurable Sessionsおよび共有サブスクリプションセッションを支える仕組み

耐久ストレージはEMQXのDurable Sessionsおよび共有サブスクリプションセッションの基盤です。

### Durable Sessions

Durable SessionsはDSデータベースエンジン上に実装されています。クライアントが**非ゼロのセッション有効期限**で接続すると、EMQXはセッション状態をDSに保存します。

- **メッセージ永続化：**

  Durable SessionがQoS > 0のトピックフィルターをサブスクライブすると、そのフィルターはEMQXのルーティングテーブルで「耐久性あり」とマークされます。これにより、そのトピックにパブリッシュされたメッセージは通常のクライアントへの配信に加え、DSにも保存されます。

- **進捗管理：**

  Durable Sessionsは*イテレーター*という軽量マーカーを用いてDSからメッセージを読み取ります。イテレーターは各耐久ストレージストリーム内の進捗を追跡し、切断やノード再起動後も確実にメッセージ再生を再開可能にします。

- **効率的なストレージ：**

  メッセージはDSの各レプリカに対して一度だけ保存されます。複数のDurable Sessionsが同じトピックをサブスクライブしていても、ストレージオーバーヘッドは最小化されます。

### 共有サブスクリプションセッション

EMQX v6.0以降、DSは共有サブスクリプションセッションの永続化もサポートしています。共有サブスクリプションはDSを利用してサブスクライバーグループ内で一貫したメッセージ分配を維持します。

- **イテレーター管理：**

  指定された共有サブスクリプションリーダーがグループのイテレーターセットを管理し、メンバーにイテレーターを割り当てて協調的な消費を実現します。

- **再生とリバランス：**

  共有トピックをサブスクライブするセッションはリーダーと通信し、メッセージ再生のためのイテレーターを借用します。更新されたイテレーターはリーダーに報告されます。クライアント切断やグループのリバランス時にはリーダーがイテレーターを取り上げ、他のメンバーに再配布して消費の継続性と負荷分散を保証します。

これらの仕組みにより、サブスクリプショングループ全体でのロードバランシング、メッセージ順序維持、フォールトトレランスが実現されます。

## クラスター全体の耐久ストレージ

EMQXクラスター内の各ノードには一意の*サイトID*が割り当てられます。これはErlangノード名（`emqx@...`）に依存しない安定した識別子で、ノードの初回起動時にランダム生成され永続化されます。この安定性により、ノード名変更や再設定があってもデータの整合性が保たれます。

管理者は`emqx_ctl ds info` CLIコマンドを使ってクラスター内の各サイトの耐久ストレージ状況を管理・監視できます。

## Durable Sessionsのハードウェア要件

セッション耐久性を有効にすると、EMQXはDurable SessionsのメタデータとDurable Sessionsに送信されたMQTTメッセージをディスクに保存します。そのため、十分なストレージ容量を持つサーバーにEMQXをデプロイする必要があります。EMQXのデータディレクトリはローカルファイルシステムを使用しなければなりません。なぜなら[組み込みの耐久ストレージバックエンド](../../develop/design/durable-storage.md#embedded-backends)はNFSやSMB/CIFSなどのネットワークファイルシステムをサポートしていないためです。最高のスループットを得るにはSSDストレージの利用を推奨します。

ディスク容量の目安は以下の通りです。

- **メッセージストレージ**：各レプリカで必要なメッセージ保存容量は、受信メッセージレートに`durable_sessions.message_retention_period`パラメータで指定された保持期間を乗じた値に比例します。このパラメータはメッセージ保持期間を決定し、総ストレージ容量に影響します。
- **セッションメタデータストレージ**：セッションメタデータの容量は、セッション数に各セッションがサブスクライブするストリーム数を乗じた値に比例します。
- **ストリーム数の計算**：ストリーム数はシャード数に比例し、トピック数にも（非線形に）依存します。EMQXは構造が類似したトピックを自動的に同一ストリームにまとめるため、トピック数の増加に対してストリーム数の増加は抑制され、セッションごとのメタデータ量を最小化します。

## 次のステップ

Durable Sessions機能の設定・管理方法や、EMQXクラスターでのDurable Sessionsの初期設定および変更方法については、以下のページを参照してください。

- [Durable Sessionsの設定と管理](./management.md)
- [データレプリケーションの管理](./managing-replication.md)

## さらに詳しく

MQTT Durable Sessionsの設計原理についてより深く理解したい場合は、[Design for Durable Storage](../../develop/design/durable-storage.md)をご覧ください。
