第 6 章 データ伝送
6.1 双方向データフロー
DTP 実装は以下の 2 つの独立したデータフロー方向をサポート しなければならない:
| 方向 | 名称 | 送信側 | 受信側 |
|---|---|---|---|
| Terminal → Fay | データ収集(Collection) | Slave | Master |
| Fay → Terminal | データ注入(Injection) | Master | Slave |
両方向は以下を満たさ なければならない:
- 同一の LogicalFrame 形式と処理フローを使用する。
- 独立したシーケンス番号空間を維持する。
- 独立した再開状態(未確認 Fragment キャッシュ、最高確認済みシーケンス番号など)を維持する。
- 互いに干渉しない:一方の方向の状態変化は他方の方向に影響を与えて はならない。
6.2 データ収集フロー(Terminal → Fay)
データ収集は以下のフローに従わ なければならない:
6.2.1 送信側(DTP_Slave)のフロー
- 端末アプリケーションが提出するデータを受信する。
- Fragment を構築する:
a. 新しい Fragment_ID(UUID v4)を生成する。
b.
agreementIdを現在のアクティブな Agreement の Agreement_ID に設定する。 c.originTimestampをデータが実際に発生した時刻の UTC ミリ秒タイムスタンプに設定する。現在時刻を使用 してはならない。 d. 構造化された ContextMetadata を付加する(第 4.11 節を参照)。 e.dagDependenciesを付加する(空配列で もよい)。 - DAG 依存関係を検証する:
a. DAG Manager を呼び出して循環を形成しないことを検証する(第 6.7 節を参照)。
b. 循環を検出した場合、その Fragment を拒否し
DAG_CYCLE_DETECTEDエラー(4001)を返 さなければならない。 - LogicalFrame を構築する:
a.
frameType = "data"を設定する。 b. Agreement_ID 圧縮規則を適用する(第 4.5 節を参照)。 c. 単調増加のsequenceNumberを割り当てる(データ収集方向)。 d. 暗号化メタデータを設定する。 - Payload を暗号化する:CAP で事前ネゴシエーションされた鍵を使用して Fragment の
dataフィールドを暗号化する。 - LogicalFrame をシリアライズする。
- Transport_Adapter を呼び出してバイナリデータを送信する。
- Fragment を未確認キャッシュに加える(第 8.2 節を参照)。
6.2.2 受信側(DTP_Master)のフロー
- Transport_Adapter から渡されたバイナリデータを受信する。
- LogicalFrame にデシリアライズする。失敗した場合、フレームを破棄し
FRAME_DESERIALIZATION_FAILEDエラー(1001)を返 さなければならない。 - プロトコルバージョンを検証する(第 10 章を参照)。
- Agreement_ID を解析する(第 4.5 節の圧縮規則を適用)。未知の Agreement に関連付けられている場合、フレームを破棄し
AGREEMENT_NOT_FOUNDエラー(3001)を返 さなければならない。 - Payload を復号する。失敗した場合、フレームを破棄し
DECRYPTION_FAILEDエラー(2001)を返 さなければならない。 - Fragment にデシリアライズする。
- DAG 依存関係を検証する:
a. すべての依存対象 Fragment が既に存在する場合、受け入れて
acceptedとしてマーク しなければならない。 b. 依存対象 Fragment がまだ到着していない場合、pendingとしてマークしてキャッシュ しなければならない(第 6.7 節を参照)。 c. 循環を検出した場合、拒否しDAG_CYCLE_DETECTEDエラー(4001)を返 さなければならない。 - 受信状態を更新する:その Fragment の sequenceNumber がインクリメントされていれば、その方向の最高受信済みシーケンス番号として設定する。
- 確認応答を送信する(第 8 章を参照)。
- Fragment を Personal Data Heap に永続化する。
6.3 データ注入フロー(Fay → Terminal)
データ注入は以下のフローに従わ なければならない:
6.3.1 送信側(DTP_Master)のフロー
- Personal Data Heap からクエリし、Agreement の
dataRangeでフィルタリングして最小化データセットを生成する。 - Fragment を構築する(6.2.1 のステップ 2-3 と同じ)。
- LogicalFrame を構築し、Payload を暗号化し、シリアライズし、送信する(6.2.1 のステップ 4-8 と同じ)。ただしデータ注入方向のシーケンス番号空間を使用する。
6.3.2 受信側(DTP_Slave)のフロー
- デシリアライズし、バージョンを検証し、Agreement_ID を解析し、復号し、Fragment にデシリアライズし、DAG を検証する(6.2.2 のステップ 1-7 と同じ)。
- 受信状態を更新し、確認応答を送信する(6.2.2 のステップ 8-9 と同じ)。
- Fragment を端末アプリケーションに引き渡す。
6.4 Agreement_ID の圧縮伝送
実装は第 4.5 節で定義された Agreement_ID 圧縮規則に厳密に従わ なければならない。
6.4.1 圧縮の例
以下は仕様に準拠した伝送シーケンスである:
Fragment 1: agreementId = "abc-123" (新しい Agreement、完全 ID)
Fragment 2: agreementId = null ("abc-123" を継承)
Fragment 3: agreementId = null ("abc-123" を継承)
Fragment 4: agreementId = "def-456" (新しい Agreement に切替、完全 ID)
Fragment 5: agreementId = null ("def-456" を継承)
6.4.2 圧縮の制約
実装は以下を満たさ なければならない:
- 圧縮を行わないことを選択 してもよい(つまり全 Fragment が完全な Agreement_ID を携帯する)。
- 圧縮を選択する場合、4.5 節の規則に厳密に従わ なければならない。
- 受信側は圧縮と非圧縮の両モードを同時にサポート しなければならない。
- コンテキスト Agreement_ID はセッションの一時停止からの復旧後、null にリセット しなければならない(つまり復旧後の最初の Fragment は完全 ID を携帯 しなければならない)。
6.5 シーケンス番号管理
6.5.1 単調増加
各 Fragment の sequenceNumber は以下を満たさ なければならない:
- 単一セッション内で単調増加する。
- 厳密に連続する べきである(つまり毎回 1 ずつ増加する)。
- 重複や後退をしては ならない。
6.5.2 双方向の独立性
データ収集方向とデータ注入方向は独立したシーケンス番号空間を維持 しなければならない:
データ収集方向 (collection): seq 1, 2, 3, 4, 5, ...
データ注入方向 (injection): seq 1, 2, 3, 4, 5, ...
実装は 2 つの方向間でシーケンス番号空間を共有 してはならない。
6.5.3 再起動とセッション
シーケンス番号は以下を満たさ なければならない:
- 新しいセッション開始時、シーケンス番号はリセット しなければならない(0 または 1 から開始 すべきである)。
- セッションが
Suspendedから復旧する際、シーケンス番号は一時停止前の値を保持 しなければならず、リセット してはならない。 - シーケンス番号が実装定義の最大値に近づいた場合、送信側はオーバーフローを避けるために能動的に新しいセッションを作成 しなければならない。
6.6 オリジナルタイムスタンプの保全
実装は伝送過程で Origin_Timestamp が変更されないことを保証 しなければならない:
- 送信側はデータが実際に発生した時刻を Origin_Timestamp として記録 しなければならない。
- シリアライズ、暗号化、伝送、復号、デシリアライズで Origin_Timestamp を変更 してはならない。
- 受信側は受信した Origin_Timestamp を変更 してはならない。
- 受信側が永続化する際、Origin_Timestamp を保持 しなければならない。
実装はフレームヘッダーの外に独立した「伝送タイムスタンプ」フィールドを維持 してもよい(実装定義)が、Origin_Timestamp と混同 してはならない。
6.7 DAG 依存関係の処理
6.7.1 Fragment を DAG に追加
受信側は Fragment を受信した際、DAG Manager を介して依存関係を処理 しなければならない:
- Fragment の
dagDependenciesを抽出する。 - 各依存関係について: a. 対象の Fragment_ID が DAG に既に存在するかを確認する。 b. そのエッジを追加すると循環を形成するかを確認する。
- 確認結果に応じて以下の 3 つの結果のいずれかを返す:
| 結果 | 意味 | 後続のアクション |
|---|---|---|
accepted | すべての依存関係が解決済み、循環なし | Fragment を DAG に追加 しなければならない |
pending | 一部の依存関係が未解決(対象 Fragment 未到着)、循環なし | その Fragment をキャッシュし、依存関係の解決を待つ 必要がある |
rejected | 循環を検出 | その Fragment を拒否し、DAG_CYCLE_DETECTED エラー(4001)を返 さなければならない |
6.7.2 遅延解決
Fragment が pending 状態の場合:
- 実装はその Fragment キャッシュ内で依存関係を継続的に待機 しなければならない。
- 依存対象 Fragment が到着した際、キャッシュ内の関連するすべての
pendingFragment を再評価 しなければならない。 - 最大キャッシュ時間を設定 してもよい(実装定義)。タイムアウト後、
DAG_DEPENDENCY_UNRESOLVEDエラー(4002)を返して破棄 すべきである。
6.7.3 循環検出アルゴリズム
実装は新しい Fragment を追加する前に循環を検出 しなければならない。検出アルゴリズムは DFS(深さ優先探索)または Tarjan アルゴリズムを使用 すべきである。
具体的なアルゴリズム:新しい Fragment の各依存対象から出発して DFS を実行する。依存対象から新しい Fragment 自身に到達できる場合、循環を形成する。
6.8 複数 Agreement の交錯伝送
複数のアクティブな Agreement が共存する場合、送信側は以下を満たさ なければならない:
- 各 Fragment のフレームヘッダーで Agreement_ID を介して正しい Agreement に関連付ける。
- Agreement を切り替える際(つまり次の Fragment が異なる Agreement に属する場合)、その Fragment に完全な Agreement_ID を携帯 しなければならない(つまり null 圧縮は使用できない)。
- Agreement の
priorityに従って送信順序をスケジューリング すべきである。
