OPAにおけるページネーションへの影響と解決策に関する検討

アプリケーション

OPA の基本モデルと課題背景

OPA(Open Policy Agent)は Rego 言語で記述されたポリシーを用いて、入力(input)や外部データ(data)に基づいて評価を行い、許可/拒否などの判定を行うエンジンである。

実装例については、AWS Prescriptive Guidance のマルチテナント API 認可制御ガイドが参考になる。OPA を活用した SaaS におけるマルチテナント認可の実装戦略が紹介されており、本稿の背景理解にも役立つ。

基本モデルのシーケンス

sequenceDiagram
    participant Client as クライアント
    participant API as アプリケーション
    participant OPA as OPA
    participant Policy as Regoポリシー
    participant Data as 外部データ

    Client->>API: リクエスト送信
    API->>OPA: input を含む評価要求
    OPA->>Data: ポリシー評価用データ取得
    Data-->>OPA: リソースデータ
    OPA->>Policy: Rego ポリシーで評価
    Policy-->>OPA: 結果(allow/deny)
    OPA-->>API: 判定結果
    API-->>Client: 応答

ページネーションでの課題

ページネーションにおいて OPA をナイーブに適用した場合、以下のような問題が発生する可能性がある。

  • 返却件数が不定となる: OFFSET/LIMIT で取得した件数に対して OPA 評価を行うと、ポリシーにより拒否されたリソースが除外されるため、結果として返却できるリソース数がページサイズ未満になる。
  • ページ境界の一貫性が崩れる: OPA 評価後に次ページ分の許可リソースを確保しようとすると、何度も追加取得・評価を繰り返す必要があり、結果として OFFSET の意味が曖昧になる(スキップしたリソースのうち許可対象が存在する可能性がある)。
  • パフォーマンスとレイテンシへの影響: 十分にフィルタされていない状態で多数のリソースを取得し、アプリ側で OPA による評価を繰り返す場合、処理回数・DBクエリ回数・OPA評価回数が増加し、システム全体の応答時間やリソース消費に大きく影響する。
  • クライアント体験の悪化: ページをまたいだ結果に重複や漏れが発生しやすくなるため、UI 上の一覧表示やページ送り操作の一貫性が失われ、ユーザーに混乱を与えるリスクがある。

このように、OPAを適用した際のページネーションには、単なるSQLベースの設計には存在しない実装上・体験上の課題が内在している。

ナイーブな実装においては、OPA は全件の評価結果を返すため、SQL のような OFFSET/LIMIT による制御が難しく、結果数が動的に変動する。このため、リスト取得時の許可リソース件数が不定であるという問題が発生する。

以下では、ポリシー適用なしの「通常の SQL」におけるページネーション(OFFSET/LIMIT やキーセット方式)の基本挙動を、具体例や図を交えて説明する。これと、OPA適用後の挙動との対比で理解が深まる。

SQL と OPA の比較

項目 SQL のフィルタリング OPA の評価処理
件数確定 WHERE 句で件数を事前制御 ポリシー評価後に件数が決定
ページネーション OFFSET/LIMIT が安定動作 再評価と取得の繰り返しが必要
処理負荷 DB が効率的に処理 アプリケーション/OPA 側で高負荷
sequenceDiagram
    participant Client
    participant API
    participant DB
    participant OPA

    Client->>API: GET /resources?page=N
    API->>DB: OFFSET/LIMIT でN件取得
    DB-->>API: N件返却
    loop リソースごと
        API->>OPA: evaluate(resource)
        OPA-->>API: allow/deny
    end
    alt 許可数 < N
        API->>DB: 追加取得 & 評価繰り返し
    end
    API-->>Client: 許可されたN件を返却

通常の SQL ページネーションの基本

sequenceDiagram
    participant Client as クライアント
    participant App as アプリケーション
    participant DB as データベース

    Client->>App: GET /items?page=P&size=N
    Note over App: page, size から OFFSET, LIMIT 計算
    App->>DB: SELECT * FROM items WHERE <条件> ORDER BY ... LIMIT N OFFSET (P-1)*N
    DB-->>App: N件(または残り分だけ)
    App-->>Client: 結果返却

通常SQLとOPAナイーブ評価のページネーション比較フロー

flowchart TB
    subgraph 通常SQL
        A1[Client: GET /items?page=P&size=N] --> B1[App: WHERE句組立 + ORDER BY + OFFSET/LIMIT 計算]
        B1 --> C1[DB: 絞り込み後に OFFSET/LIMIT 適用]
        C1 --> D1[App: 結果 N 件(または残り分)取得]
        D1 --> E1[Client: 結果返却]
    end

    subgraph OPAナイーブ評価
        A2[Client: GET /items?page=P&size=N] --> B2[App: OFFSET/LIMIT でまず取得]
        B2 --> C2[DB: N 件取得]
        C2 --> D2[App: 各リソースを OPA 評価]
        D2 --> F2{許可数 >= N?}
        F2 -- yes --> G2[App: N 件返却] --> H2[Client: 結果返却]
        F2 -- no --> I2[App: 不足分補うため次チャンク取得] --> B2
    end

解決方法の検討

1. 事前フィルタリング(ナイーブ型)

全件取得+逐次評価を避けるため、OPAのポリシー内容をアプリ側で理解し、事前に SQL へ条件を反映。

  • メリット: 一貫性ある件数制御が可能
  • デメリット: ポリシーと実装の乖離リスク

2. 部分評価(Partial Evaluation)

OPA の Compile API を利用し、Rego ポリシーの一部を SQL WHERE 句に変換可能なロジックに変換。

sequenceDiagram
    participant App
    participant OPA
    participant DB

    App->>OPA: Compile API(入力: user_id, unknowns: data.resources)
    OPA-->>App: 残余ロジック AST
    App-->>DB: SQL WHERE 句に変換してクエリ実行
    DB-->>App: フィルタ済データ取得
  • メリット: 一部ポリシーを SQL に落とせるため効率的
  • デメリット: SQL 化できない複雑ロジックは除外対象

3. フロントエンドへのページネーション委譲

バックエンドは全許可リソースを返し、フロント側でページネーションを行う方式。

  • メリット: 実装が簡素
  • デメリット: データ転送量・初期応答コストが大きい

4. ハイブリッドアプローチ(SQL+OPA)

DB で大まかに絞り込んだ上で、OPA をフィルタ処理に組み込む方式。

  • メリット: 精度とスケーラビリティの両立
  • デメリット: 実装の複雑化

まとめ

OPA をナイーブに適用したリスト取得においては、ページネーションとポリシー評価の非整合によって、返却件数の不定・処理負荷の増大・ページ境界の曖昧化・ユーザー体験の低下といった複合的な問題が発生する。

これらを解消するためには、以下のような方針が有効であると考えられる:

  • SQL 化できるポリシー条件を抽出し、WHERE 句に反映する:部分評価(Partial Evaluation)を活用し、可能な限り DB 側での絞り込みを実現する。
  • OPA によるナイーブ評価は最小限にとどめる:候補リストが小さい場合の補足的評価に限定し、ページネーション全体を OPA に依存させない。
  • 安定したページネーション方式を採用する:キーセット方式を活用し、OFFSET の増加によるパフォーマンス低下や不整合を防止する。
  • ポリシー設計とアプリ実装の連携を密に行う:Rego ポリシーの構造が SQL 化に適しているかを常に意識し、設計段階から整合性を重視する。

これらの対策を通じて、OPA を導入しつつも、スケーラビリティと一貫したページネーション体験を両立できるアーキテクチャを実現できる。

参考