イベントテーブルのデータベースへの関連付けを試してみた #SnowflakeDB
はじめに
2025年2月のアップデートでイベント テーブルをデータベースに関連付ける機能が一般提供となりました。こちらの機能を試してみましたので、本記事で内容をまとめてみます。
アップデートの概要
Snowflake ではイベントテーブルとして、ストアドプロシージャや UDF などの Snowflake オブジェクトがテレメトリ データを出力すると、自動的にデータを収集できる特殊な種類のデータベース テーブルが提供されています。
ログの収集の場合、イベントテーブルを設定し、アカウントや対象のデータレベル等でログレベルの設定を行うことで、ログの収集が開始されます。
イベントテーブルについては以下でも紹介していますので、あわせてご参照ください。
これまではイベントテーブルをアカウントレベルで設定することができました。つまり、異なるデータベース内のプロシージャであっても、収集先のイベントテーブルは一つ(共有)でした。そのため、アクセス制御はレコードレベルで行う必要がありました。
今回のアップデートで、データベースレベルでイベントテーブルを関連付けることができるようになりました。アカウント内でイベントテーブルは複数作成できるため、これによりログの取得先となるイベントテーブルを分離できるようになります。
前提条件
以下の環境で検証しています。
- 本機能の使用には、Enterprise 以上のエディションが必要です
- イベントテーブル
- カスタムイベントテーブルを使用
- ※アカウントのデフォルトのイベントテーブル( SNOWFLAKE.TELEMETRY.EVENTS)は使用しない
検証手順
本機能については、以下に記載があります。
ここでは、異なるデータベースにイベントテーブルとストアドプロシージャを作成し、各イベントテーブルに対応するストアドプロシージャのログを取得してみます。
検証用のサンプルオブジェクトを作成
はじめに以下のコマンドで、同様の環境を2つ用意しました。
--sysadminで作成
USE ROLE sysadmin;
--変数を定義
----環境名
SET env_name = 'test1';
--SET env_name = 'test2';
----データベース名
SET db_name = concat($env_name,'_db');
----イベントテーブル名
SET event_table_name = concat($env_name,'_events');
--イベントテーブルとストアドプロシージャの作成先となるデータベースを作成
CREATE OR REPLACE DATABASE identifier($db_name);
--スキーマを作成
USE DATABASE identifier($db_name);
----イベントテーブルの作成
CREATE OR REPLACE SCHEMA event_tables;
----ストアドプロシージャの作成
CREATE OR REPLACE SCHEMA procedures;
--イベントテーブルを作成
USE DATABASE identifier($db_name);
USE SCHEMA event_tables;
CREATE OR REPLACE EVENT TABLE identifier($event_table_name);
--ストアドプロシージャの作成
USE DATABASE identifier($db_name);
USE SCHEMA procedures;
----サンプルテーブルを作成
CREATE OR REPLACE TABLE parent (ID INTEGER);
CREATE OR REPLACE TABLE child (ID INTEGER);
----Snowflakeスクリプトでストアドプロシージャを定義
CREATE OR REPLACE PROCEDURE insert_data()
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
$$
BEGIN
BEGIN TRANSACTION;
INSERT INTO procedures.parent VALUES (1);
SELECT 1/0; --エラーを発生させる
INSERT INTO procedures.child VALUES (1);
COMMIT;
RETURN 'complete';
EXCEPTION
WHEN OTHER THEN
ROLLBACK;
RAISE;
END;
$$
;
各環境ごとに実行後、以下のオブジェクトが作成されます。
データベースにイベントテーブルを関連付け
イベントテーブルを作成後、各データベースに紐づけます。この際、以下の権限が必要です。
- ACCOUNTADMIN
- イベントテーブルに対する所有者権限または INSERT 権限
SYSADMIN でイベントテーブルを作成したので、所有者権限は ACCOUNTADMIN にも継承されます。そのため、以下のコマンドで関連付けを行いました。
--データベースにイベントテーブルを関連付け
USE ROLE ACCOUNTADMIN;
----環境1
ALTER DATABASE test1_db SET EVENT_TABLE = test1_db.event_tables.test1_events;
----環境2
ALTER DATABASE test2_db SET EVENT_TABLE = test2_db.event_tables.test2_events;
--設定値を確認:環境1
>SHOW PARAMETERS LIKE 'event_table' IN DATABASE test1_db;
+-------------+------------------------------------+----------------------------+----------+-----------------------------------------+--------+
| key | value | default | level | description | type |
|-------------+------------------------------------+----------------------------+----------+-----------------------------------------+--------|
| EVENT_TABLE | test1_db.event_tables.test1_events | snowflake.telemetry.events | DATABASE | Event destination for the given target. | STRING |
+-------------+------------------------------------+----------------------------+----------+-----------------------------------------+--------+
データベースレベルでログレベルを設定
この状態では、まだログは取得されません。続けてログレベルの設定を行います。ログレベルはアカウントやデータベースなど各レベルで設定可能です。
ここでは、データベース単位でログレベルの設定を行います。データベースに対してログレベルの設定を行うには対象のデータベースに対する MODIFY 権限が必要です。
--データベースレベルのログレベル設定変更
----データベース所有者のSYSADMINを使用
USE ROLE SYSADMIN;
--環境1:ログレベルを「WARN」に設定
ALTER DATABASE test1_db SET LOG_LEVEL = WARN;
--環境2:ログレベルを「ERROR」に設定
ALTER DATABASE test2_db SET LOG_LEVEL = ERROR;
--確認:環境1
>SHOW PARAMETERS LIKE '%LOG_LEVEL%' IN DATABASE test1_db;
+-----------+-------+---------+----------+----------------------------------------+--------+
| key | value | default | level | description | type |
|-----------+-------+---------+----------+----------------------------------------+--------|
| LOG_LEVEL | WARN | OFF | DATABASE | LOG_LEVEL to use when filtering events | STRING |
+-----------+-------+---------+----------+----------------------------------------+--------+
イベントテーブルにログを取得する
イベントテーブルの設定と、データベースのログレベルを設定したので、この状態でプロシージャを実行します。
--各環境でストアドプロシージャを作成
USE ROLE SYSADMIN;
--環境1
USE SCHEMA test1_db.procedures;
CALL insert_data();
--環境2
USE SCHEMA test2_db.procedures;
CALL insert_data();
実行結果は下図のようにエラーとなります。
実行から1分程度したら、各イベントテーブルを確認します。下図の通りほぼ同じ時間にログが記録されています。[RESOURCE_ATTRIBUTES] を見るとどこで発生したログか確認できます。
- 環境1
{
"db.user": "<ユーザー名>",
"snow.database.id": 11,
"snow.database.name": "TEST1_DB",
"snow.executable.id": 513,
"snow.executable.name": "INSERT_DATA():VARCHAR",
"snow.executable.type": "PROCEDURE",
"snow.owner.id": 2,
"snow.owner.name": "SYSADMIN",
"snow.query.id": "01bacb2a-0002-274f-0000-000223724785",
"snow.schema.id": 33,
"snow.schema.name": "PROCEDURES",
"snow.session.id": 601922486771722,
"snow.session.role.primary.id": 2,
"snow.session.role.primary.name": "SYSADMIN",
"snow.user.id": 1,
"snow.warehouse.id": 4,
"snow.warehouse.name": "COMPUTE_WH",
"telemetry.sdk.language": "sql"
}
- 環境2
{
"db.user": "<ユーザー名>",
"snow.database.id": 12,
"snow.database.name": "TEST2_DB",
"snow.executable.id": 258,
"snow.executable.name": "INSERT_DATA():VARCHAR",
"snow.executable.type": "PROCEDURE",
"snow.owner.id": 2,
"snow.owner.name": "SYSADMIN",
"snow.query.id": "01bacb2a-0002-27fe-0002-23720001702e",
"snow.schema.id": 36,
"snow.schema.name": "PROCEDURES",
"snow.session.id": 601922486771722,
"snow.session.role.primary.id": 2,
"snow.session.role.primary.name": "SYSADMIN",
"snow.user.id": 1,
"snow.warehouse.id": 4,
"snow.warehouse.name": "COMPUTE_WH",
"telemetry.sdk.language": "sql"
}
内容からも各環境(データベース)で発生したログを対応するイベントテーブルに出力することができました。
デフォルトのイベントテーブルを使用する
さいごにこれまで通りの設定で試してみます。これまではアカウントレベルで一つのイベントテーブルを設定できました。デフォルトでは、以下のイベントテーブルが設定されています。
--デフォルトのイベントテーブルを確認
USE ROLE ACCOUNTADMIN;
SELECT * FROM SNOWFLAKE.TELEMETRY.EVENTS;
上記の手順では、各データベースに異なるイベントテーブルを設定していたので、これをデフォルトのイベントテーブルに変更します。
----環境1
ALTER DATABASE test1_db UNSET EVENT_TABLE;
----環境2
ALTER DATABASE test2_db UNSET EVENT_TABLE;
--確認:環境1
>SHOW PARAMETERS LIKE 'event_table' IN DATABASE test1_db;
+-------------+----------------------------+----------------------------+-------+-----------------------------------------+--------+
| key | value | default | level | description | type |
|-------------+----------------------------+----------------------------+-------+-----------------------------------------+--------|
| EVENT_TABLE | snowflake.telemetry.events | snowflake.telemetry.events | | Event destination for the given target. | STRING |
+-------------+----------------------------+----------------------------+-------+-----------------------------------------+--------+
データベースレベルでログレベルは設定済みなので、プロシージャを実行します。
USE SCHEMA test1_db.procedures;
CALL insert_data();
USE SCHEMA test2_db.procedures;
CALL insert_data();
イベントテーブルを確認すると、先ほど設定した各データベースのイベントテーブルにはログは取得されず、デフォルトのイベントテーブルにログが取得されます。
--デフォルトのイベントテーブルを確認
USE ROLE ACCOUNTADMIN;
SELECT * FROM SNOWFLAKE.TELEMETRY.EVENTS;
さいごに
イベント テーブルのデータベースへの関連付けを試してみました。
明確にログの取得先を分離できる点がこのアップデートの強みと思いました。これまではログの取得先が共有され、ユーザー権限によって特定のイベントテーブル レコードのみを返したい場合に、行アクセスポリシーを使用する必要がありましたが、この点が構成しやすくなったと思います。
こちらの内容が何かの参考になれば幸いです。