0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qiita社ではデータの分析や閲覧にRedashを使っています。
適切な権限管理をしつつ、BigQueryやAthenaのデータを取り扱っています。
その中で、今回アップグレードした際に引っかかった部分があるので共有します。

何が起きたか

  • Redashを古いバージョン(v8系)から最新の26系へアップグレード
    • 厳密には8-> 10.1、10.1 -> 26.3と上げた
  • クエリエディタでデータソースをBigQueryにした時、左側のスキーマ一覧(テーブル名・カラム名が表示される)が空になった
    • アップグレード前は出ていた
    • テーブル名の推薦などもできない
  • クエリの実行は問題なくできる

ログで切り分け

Redashはスキーマ情報を、バックグラウンドジョブ(refresh_schemas)でデータソースに問い合わせて取得し、キャッシュしています。ここのログを確認しました。

  • スケジューラは refresh_schemas を30分間隔でちゃんと積んでいる
  • 実行側のジョブも動いているが、データソースごとの refresh_schema が失敗している
  • エラーの内容は以下
403 PERMISSION_DENIED
Access Denied: Table my-project:restricted_dataset.INFORMATION_SCHEMA.TABLE_OPTIONS:
User does not have permission to query table
my-project:restricted_dataset.INFORMATION_SCHEMA.TABLE_OPTIONS, or perhaps it does not exist.

スキーマ取得が、アクセス権のないデータセットの INFORMATION_SCHEMA を引こうとして弾かれていました。

原因: 全データセットを一括クエリして、1つの失敗で全滅する

Redash 26系の get_schemaredash/query_runner/big_query.py)は、ざっくり次の流れで動きます。

  1. datasets().list() でプロジェクト内の全データセットを列挙する
  2. それらを UNION ALL でまとめた1本のクエリを、各データセットの INFORMATION_SCHEMA.COLUMN_FIELD_PATHS(カラム)と INFORMATION_SCHEMA.TABLE_OPTIONS(説明)に投げる
  3. どこか1つでもエラーが返ると、例外を投げて get_schema 全体が中断する

問題は3番です。BigQueryをデータセット単位で権限分離している環境だと、サービスアカウントがアクセスできないデータセットがプロジェクト内に存在します。

それが UNION ALL に混ざると、そのデータセットだけで403になり、本来見たいデータセットのスキーマまで含めて、まるごと取得が失敗します。結果、スキーマ一覧が空になる、というわけです。

該当箇所はこんな形でした。

なぜ古いバージョンでは平気だったのか

この一括クエリ方式は、パフォーマンス最適化のために導入されたものでした(getredash/redash#5632)。

それ以前はテーブルごとにメタデータAPIを叩いていて、テーブルが大量にある環境では数分かかっていました。これを INFORMATION_SCHEMA への一括クエリにして数秒に短縮したのが #5632 です。

副作用として、スキーマ取得が all-or-nothing になりました。古い方式はアクセスできないデータセットを取りこぼしても全体は動いていたのが、新方式では1つの失敗が全体を巻き込みます。アップグレードで顕在化したのはこのためです。

一応回避策として getredash/redash#7289 が入ってますがこちらはロケーションが分かれている場合のアドホックな修正で、同一ロケーションで閲覧権限が分かれている場合難しいです。

対処の選択肢

整理すると、現実的な選択肢は2つでした。

  • 案A: Redashのイメージを派生させて get_schema をパッチする。データセット単位でクエリし、失敗したものはスキップする。最小権限の設計を維持できるが、パッチが本家に取り込まれて新バージョンが出るまでイメージのメンテが増える
  • 案B: サービスアカウントにメタデータ閲覧権限(roles/bigquery.metadataViewer)を付与する。即効だが、スキーマブラウザに全データセットのテーブル名・カラム名が並ぶようになる

ロケーションで絞る案は前述の通り使えませんでした。

採った応急処置: metadataViewer の付与

まずは早く直したかったので、応急処置として案Bを選びました。各サービスアカウントにプロジェクトレベルで metadataViewer を付与します。
(以下のコードはterraformでの付与例)

resource "google_project_iam_member" "redash_metadata_viewer" {
  project = var.project_id
  role    = "roles/bigquery.metadataViewer"
  member  = "serviceAccount:${google_service_account.redash.email}"
}

トレードオフは正しく理解しておく必要があります。

  • metadataViewer が許すのはメタデータの閲覧だけです。テーブル名・カラム名・型・説明は見えるようになります
  • 行データの読み取り(tables.getData)は含みません。権限のないデータセットを SELECT で叩いても、実行時に403のままです
  • つまりデータ本体のアクセス分離は維持されますが、テーブル構造(カラム名など)は全データソースの利用者に見えるようになります

カラム名そのものが業務情報になりうる環境では、ここが許容できるかが判断ポイントになります。今回は許容できると判断しました。

恒久対応

応急処置はメタデータの分離を緩めるので、本筋は get_schema 側をデータセット単位の失敗に強くすることです。

upstreamにこの「権限のないデータセットをスキップする」処理は無かったので、Issueを立てました(getredash/redash#7760)。PRも送る予定です。

直し方の方針はシンプルです。

  • まず今まで通り一括クエリを投げる(全データセットにアクセスできる通常ケースは1発で速いまま)
  • 一括が失敗したときだけ、データセット単位のクエリにフォールバックして、失敗したものだけスキップする

データセット単位でも、クエリ本数はデータセット数で頭打ちです。テーブル単位ではないので、#5632 が嫌った遅さには戻らないでしょう。

まとめ

  • Redash 26系のBigQueryスキーマ取得は、プロジェクト内の全データセットを一括でクエリする
  • 権限分離している環境では、アクセスできないデータセットが1つあるだけでスキーマ取得が全滅する
  • クエリ実行自体は無事。壊れるのは補完用のスキーマキャッシュだけ
  • 応急処置は metadataViewer の付与。ただしテーブル構造が見えるようになるトレードオフがある
  • 恒久対応はスキーマ取得をデータセット単位の失敗に強くすること

アップデートの際の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?