はじめに
私はデータ基盤の運用保守に携わっており、主にバッチ処理まわりの保守や追加開発を担っています。
結論から言うと、別アカウントにあるテーブルと自アカウントのテーブルを結合した結果を、Dynamic Table として「宣言的に」持たせる構成を採りました。アカウントをまたぐデータの受け渡しには Private Listing を使い、結合結果は Secure View に絞って提供元の部署へ共有し返しています。結合ロジックを Dynamic Table の SQL 定義に寄せたことで、結合結果テーブルの差分更新やスケジューリングを自分で組まずに済みました。
Snowflake で「別アカウントにあるデータと自分のデータをかけ合わせたい」「その結合結果を鮮度を保って自動で最新化したい」という方に役立つ内容です。
この記事は実案件をもとにしていますが、社名・部署名・テーブル名などの固有情報は伏せ、構成の考え方に絞って書いています。
背景:なぜアカウントをまたいだ結合が必要だったのか
私が保守している部署の Snowflake と、別部署の Snowflake は別アカウントで運用されています。今回やりたかったのは、その別部署のテーブルと、自部署のテーブルを結合することでした。
なお、同じ会社ですが、部署ごとに Snowflake の組織(organization)が分かれており、アカウントは別組織でした。この「同一組織内か、別組織か」は共有方式の選び方に効いてくるので、後述します。
目的は、会員の属性A(他部署のみが持つ)と属性B(自部署のみが持つ)両方を持った会員が何人いるかを他部署に共有することです。このとき、主キー以外の情報(会員の他の属性)は他部署に共有したくありませんでした。そこで、データを片方へコピーして持つのではなく、共有+Dynamic Table で必要な結合結果だけを得る方式を選んでいます。
採用した構成(全体像)
大きく3つの部品で構成しています。ポイントは、データが行きと帰りの2方向で共有される点です。まず別部署のテーブルを Private Listing で受け取り、自部署の Dynamic Table で結合します。その結合結果から他部署に見せてよい列だけを Secure View に絞り込み、今度は自部署が提供側になって、同じ別部署(= Dynamic Table のソーステーブルをくれた部署)へ、もう一つの Private Listing で共有し返します。
| 部品 | 役割 |
|---|---|
| Private Listing | アカウント間でオブジェクトを共有する仕組み。①別部署→自部署(元テーブルの共有)と、②自部署→別部署(結合結果の Secure View の共有し返し)の2方向で使う |
| Dynamic Table | ①で共有されたテーブルと自部署のテーブルを結合する SQL を宣言し、結合結果を自動で最新化する |
| Secure View | Dynamic Table の結合結果から、他部署に見せてよい列(主キーなど)だけに絞り込み、SQL 定義を伏せた状態で②の共有に使うビュー |
(①は別部署が実施した作業のため本記事では割愛。自部署視点で書くなら「別部署が共有したオブジェクトを確認する」手順になる。)
① 別部署のテーブル
│ Private Listing(別部署 → 自部署)
▼
② 自部署から見える共有テーブル(参照のみ)
│ 自部署のテーブルと結合
▼
③ Dynamic Table(結合結果・主キーのみ/属性Bは含まない)
│ 他部署に見せてよい列だけに絞り込む
▼
④ Secure View(SQL定義を伏せて公開)
│ Private Listing(自部署 → 別部署)
▼
⑤ 別部署(①と同じ部署)が Secure View を参照
なぜこの2つを選んだのか
Private Listing を選んだ理由
別アカウントのデータを使う手段として、Snowflake には Direct Share(直接共有)や Listing といった仕組みがあります。今回は Private Listing を使いました。Private Listing は、相手を限定して、非公開でデータを共有できる仕組みです(マーケットプレイスのように一般公開はしません)。同一組織内でも別組織でも渡せますが、今回の相手は前述のとおり別組織のアカウントでした。
今回の提供側と利用側は、同じリージョンの別アカウントでした。後述のコラムのとおり、同一リージョンであれば Direct Share、Private Listing 2つの手法が選択肢にあります。
今回、Private Listing を選んだ理由は2つあります。
1つ目は、共有したオブジェクトをコンシューマー側がどう使っているか、プロバイダ側で把握できることです。Private Listing は Snowsight の Provider Studio(Marketplace » Provider Studio » Analytics)から、どのコンシューマーがリスティングをインストールしたか、どんなクエリを実行しているかを確認できます。この利用状況の可視化は Listing(Private Listing を含む)に備わる機能で、Direct Share の管理画面(Data sharing » External sharing)には同等の分析機能がありません。
2つ目は、Snowsight(Provider Studio)上でオブジェクトの作成から共有までを完結できることです。「作業手順を画面操作ベースで残したい」という要件もあったため、これも選定理由に挙げています。ただし Direct Share も Snowsight(External sharing)から作成できるため、この点は Private Listing だけの利点ではありません。
なお、1つ目の理由だけでも Private Listing を選ぶ十分な根拠になります。
コラム:Snowflake アカウント間のデータ共有方式
Snowflake の共有はどれも「データを物理コピーしない(提供側のテーブルを利用側が直接参照する)」のが土台で、その上で届け方が分かれます。
- Direct Share(直接共有):同じリージョン内の指定アカウントへ手早く共有する方式。説明やサンプルといったメタデータは付きません。同一リージョン限定で、リージョンをまたぐには手動レプリケーションが必要です。また、共有したデータをコンシューマー側がどう使っているか追跡する仕組みは標準では用意されていません。
- Private Listing:相手を限定して非公開で渡す Listing。メタデータが付き、**リージョン/クラウドをまたいでも自動配信(Cross-Cloud Auto-Fulfillment)**されます。加えて、Snowsight の Provider Studio から、どのコンシューマーがリスティングを利用しているかを確認できます。
- Marketplace Listing:Snowflake Marketplace で広く公開する Listing。
私は当初「別リージョンだと Private Listing は作れない」と思い込んでいたのですが、実際は逆で、リージョンをまたぐ共有こそ Private Listing が得意です。今回は同一リージョンだったので、共有そのものは Direct Share でも成立しました。ただし、共有後にコンシューマー側の利用状況を追跡したかったこと、また Snowsight 上で作成から共有までの手順を残したかったことから、今回は Private Listing を選んでいます。手早く済ませたい・利用状況の追跡が不要なら Direct Share、配布を管理したい・利用状況を可視化したい・将来リージョンをまたぐ可能性があるなら Private Listing、という選び分けになります。
共有先が同じ組織内のアカウントなら Organization Listing(CREATE ORGANIZATION LISTING)を使い、この場合は ORGADMIN でのプロバイダー規約への同意など、組織側の事前設定が要ります。一方、今回のように別組織へ渡す無料/有料のオフプラットフォーム Private Listing では、Snowflake の審査もプロバイダー/コンシューマー規約への同意も不要です。詳細は後述の参考リンクを参照してください。
Dynamic Table を選んだ理由
結合結果を保持する方法はいくつかあります(ビュー、定期的な CREATE TABLE AS、Stream + Task の手組みなど)。今回 Dynamic Table にしたのは、「結合結果のテーブルはこうあってほしい」という SQL を1つ宣言すれば、差分の取り込みと更新タイミングを Snowflake 側が面倒みてくれるからです。
Dynamic Table では、データの鮮度目標を TARGET_LAG で指定し、更新方式を REFRESH_MODE(INCREMENTAL / FULL / AUTO)で指定します。元テーブルの変更を検知して、ラグの範囲に収まるよう自動でリフレッシュされます。Stream で変更を捕まえて Task でスケジュールして…という手組みを、宣言1本に畳めるのが利点です。
なぜ「Secure View→Private Listing」ではなく「Dynamic Table→Secure View→Private Listing」なのか
結合結果を他部署に見せるだけなら、共有テーブルと自部署テーブルを結合する Secure View を直接作り、それを Private Listing で共有し返す構成でも成立します。実際にそうしなかったのは、他部署には見せたくない一部のカラムを、自部署側では参照したいという要件があったからです。
マスキングポリシーで列を隠す方法もありますが、他部署に不要な列までいったん見せられる状態にしておく必要はありません。そこで、結合結果を Dynamic Table として一度実体化し、自部署はその Dynamic Table を(マスキングなしで)そのまま参照できるようにしたうえで、そこから他部署に渡してよい列(主キーなど)だけを絞り込んだ Secure View を別途作り、それだけを外部に共有する、という2段構えにしています。
実装の流れ
1. 別アカウントのテーブルを Private Listing で共有する
2. 共有テーブルを参照する Dynamic Table を作る
CREATE OR REPLACE DYNAMIC TABLE my_joined_dt
TARGET_LAG = '1 hour' -- 必須パラメータ(鮮度目標)
WAREHOUSE = my_wh -- 必須パラメータ
REFRESH_MODE = AUTO -- オプションパラメータ
AS
SELECT ...
FROM shared_db.schema.remote_table AS r -- Private Listing 経由の共有テーブル
JOIN my_db.schema.local_table AS l
ON r.key = l.key;
3. Secure View を作り、Private Listing で他部署へ共有し返す
Dynamic Table の結合結果から、他部署に見せてよい列(主キーなど)だけを Secure View として定義します。
CREATE OR REPLACE SECURE VIEW my_shared_view AS
SELECT primary_key_column
FROM my_joined_dt;
このビューを Private Listing で、Dynamic Table のソーステーブルをくれた部署(=手順1の提供元)へ共有し返します。今回は「作業手順を画面操作ベースで残したい」という要件があったため、以下は Snowsight(Provider Studio)での手順です。
- Snowsight で Marketplace » Provider Studio » Create Listing » Specified Consumers を選ぶ
- 上記の Secure View を含むデータベース/スキーマを Data Product として追加する
- Who can access で共有先(提供元の部署)の組織名・アカウント名を指定して公開する
- 共有先のアカウントでは、届いた Listing を「Get」して自アカウントにデータベースとしてマウントすれば Secure View を参照できる
画面操作の詳細は、参考リンクの「Create and publish a listing」を参照してください。SQL と YAML でリスティングを定義する方法(IaC 寄りの運用)も用意されていますが、今回は上記の理由から Snowsight 上で完結させています。
この共有し返しを SQL で行う手順(共有オブジェクト=Share とリスティングオブジェクト=Listing の2層で書く)は、別記事にまとめました。相手が同一組織内か別組織かで使うコマンドが変わります。
- 今回のように別組織へ渡す場合:
CREATE SHARE→CREATE EXTERNAL LISTING(Snowflakeの組織外データ共有をSQLで作る) -
同一組織内へ渡す場合:
CREATE SHARE→CREATE ORGANIZATION LISTING(Snowflakeの組織内データ共有をSQLで作る)
共有に必要な権限・前提(オーナーロール、CREATE SHARE/CREATE LISTING 権限など)は上記のSQL記事にまとめています。
確認しておきたかったこと(共有データ × Dynamic Table)
REFRESH_MODE は、AUTO、FULL(全件更新)、INCREMENTAL(差分更新)から選択できます。AUTO は、コストとパフォーマンスの面で最も優れたモードを自動的に選択する仕組みです。差分更新がサポートされていない場合や、パフォーマンスが期待できない場合は、全件更新(FULL と同様)になります。
一見 AUTO が最適解に見えますが、どのようなケースだと AUTO では不都合になるのかは、あらかじめ考えておきたいところです。たとえば差分更新できないときに黙って全件更新へ切り替わるのではなくエラーにしたい場合は INCREMENTAL を選択する、といったように、プロジェクト固有の事情や設計を踏まえて吟味したほうがよいと思います。
そのほか気づいた点
マスキングの解除がすぐ反映されなかった
共有されたテーブルでは、当初一部のカラムにマスキングがかかっていました。その状態では結合結果が正しいか確認しづらく、また参照しても問題ないカラムだったので、提供側の部署にマスキングを解いてもらいました。
ところが、こちらで Dynamic Table を再作成・更新しても、マスキングは解除されたように見えませんでした。その後、提供側のテーブルのデータに更新が入ったタイミングで、ようやく解除が反映されました。
ここから推測すると、Dynamic Table のリフレッシュはベーステーブルのデータ変更を契機に走り、マスキングポリシーの付け外しのような「データ自体は変わらないメタデータの変更」は、すぐには取り込まれないことがあるようです(正式な仕様として確認したわけではなく、今回の観察です)。
GUI での構築は手順が陳腐化しやすい
本当は IaC の考え方に沿って、データ共有も Dynamic Table もコードで作りたいところでした。ただ「人に渡しやすい手順にしたい」という事情から、今回は Snowsight 上で構築しました。
すると、手順書を作った時点から UI が変わってしまい、構築作業で手間取りました。再現性の担保はもちろんですが、クラウドサービスは UI が変わりやすいぶん、コードでインフラ構築を残しておく価値が大きいと、あらためて感じました。
まとめ・学び
- アカウントをまたぐ結合でも、共有(Private Listing など)+ Dynamic Table を組み合わせると、結合ロジックを SQL 1本の宣言に寄せられ、差分取り込みや更新タイミングを Snowflake に任せられます。運用が軽くなるのが一番の利点でした。
- ただし、共有データをソースにしたときの差分更新の扱いには注意が要ります。今回は会員数が増減するため
FULLを選びましたが、共有データでINCREMENTALが成立するかは別途検証が必要です。 - Dynamic Table のリフレッシュはデータの変更を契機に走るため、マスキングのようなメタデータ変更はすぐに反映されないことがあります。
- 共有方式は、同一リージョンで手早く済ませたいなら Direct Share、リージョンをまたぐ・配布や利用状況を管理したいなら Private Listing、と目的で選び分けられます。
- 仕上げは GUI でなくコード(IaC)で残す。クラウドの UI は変わりやすく、再現性の面でもコード化が効きます。
参考リンク(公式ドキュメント)
- Dynamic tables(概要)
- Dynamic table refresh modes(INCREMENTAL / FULL / AUTO)
- Dynamic table limitations
- Set the target lag for a dynamic table
- CREATE DYNAMIC TABLE
- About listings(リスティングの概要)
- Data sharing and collaboration in Snowflake(共有の全体像)
- Create and publish a listing(Snowsight でのリスティング作成手順)
- Monitor listing use(Provider Studio での利用状況の確認)
- Data Sharing Usage schema(DATA_SHARING_USAGE)
- Create and configure shares(Direct Share を Snowsight から作成する)