🛰️

BigQuery Subscription のスキーマ設定

2024/02/28に公開

1. はじめに

こんにちは、クラウドエース データソリューション部所属の泉澤です。
クラウドエースのITエンジニアリングを担うシステム統括部の中で、特にデータ基盤構築・分析基盤構築からデータ分析までを含む一貫したデータ課題の解決を専門とするのがデータソリューション部です。

データソリューション部では活動の一環として、毎週 Google Cloud の新規リリースを調査・発表し、データ領域のプロダクトのキャッチアップをしています。その中でも重要と考えるリリースを本ページ含め記事として公開しています。

今回紹介するリリースは、Pub/Sub の BigQuery Subscription に「Use Table Schema」 というスキーマ設定が追加されたという内容のものです。

このリリースは 2024/01/22 にありました。

https://cloud.google.com/pubsub/docs/release-notes#January_22_2024

本記事では、Use Table Schema を含め、BigQuery Subscription のスキーマ設定について紹介していきます。

まず初めに、本記事の前提知識となる Pub/Sub と BigQuery Subscription について説明します。

2. 前提知識の説明(Pub/Sub、BigQuery Subscription)

Cloud Pub/Sub とはメッセージングサービスです。あるサービスが発行するデータを別のサービスが受け取る時、この2つのサービスの間に Pub/Sub を挟むことによって、両者のサービスを疎結合にすることができます。この時、データを発行する側を「Publisher」、受け取る側を 「Subscriber」、扱われるデータを「メッセージ」と Pub/Sub では呼びます。

サービス間に Pub/Sub を挟まない場合、Publisher は Subscriber がメッセージの受け取りが可能になるまで待つ必要があるため、Publisher は送信したい時に送信できない、Subscriber はメッセージを受け取りたいときに受け取れないという状況が発生します。

しかし、ここに Pub/Sub を挟むことによって、Pub/Sub が一時的なメッセージの保管庫になってくれるため、お互いの都合に合わせたメッセージの送受信が可能になり、リアルタイムでのストリーミング処理や効率的な並列処理が可能になるのです。

その上、Pub/Subでは配信されたメッセージが正しく処理されたかの確認を行うため、信頼性を確保できます。

Pub/Sub の構成要素として重要なものに Topic と Subscription があります。
Topic はメッセージを保存する場所でメッセージのデータ形式を管理するためにスキーマを定義することができます。メッセージの形式に限りはありませんが、JSON 形式が一般的であり、そのスキーマの定義ができます。
Subscription はメッセージの配信先を定義するリソースです。下図に示すように、Subscriber からのメッセージ処理完了の通知を受け取ることで、メッセージの配信が終了します。

architecture.png

一つの Topic は複数の Subscription を紐付けることができ、Topic は紐付けられた全ての Subscription に対してメッセージを配信します。

この性質によって、Pub/Sub は様々なアーキテクチャ構成を取ることができ、ユースケースに沿ってパターンを選択することができます。

pattern.png
こちらのサイトより引用

Topic に紐付けられる Subscription にはいくつか種類があります。
その中の一つが今回紹介する BigQuery Subscription です。

BigQuery Subscription を使うと BigQuery テーブルに対して直接メッセージを書き込むことができますが、受け取ったメッセージを適切にテーブルに挿入するためには、今回紹介するスキーマ設定を考慮に入れる必要があります。

Pub/Sub と BigQuery Subscription の詳細は以下の公式ドキュメントをご覧ください。
https://cloud.google.com/pubsub/docs/overview?hl=ja
https://cloud.google.com/pubsub/docs/bigquery?hl=ja

3. BigQuery Subscriptionのスキーマ設定について

BigQuery Subscription には、3つのスキーマ設定があり、いずれか一つのみを選択する必要があります。

  • None
  • Use topic schema
  • Use table schema(New!!!)

これらを選択することによって、Topic からきたメッセージを Subscriber である BigQuery テーブルにどのように挿入するかが変わります。

まずは、「None」を選んだ場合とそれ以外の2つに分けて挙動を説明し、その後に「Use topic schema」、「Use table schema」の違いを紹介したいと思います。

3.1. 「None」とそれ以外の設定の違い

「None」の場合、メッセージの内容に関わらず、メッセージを丸ごと BigQuery テーブルの data カラムに要素として挿入します。そのため、あらかじめテーブルには data カラムを含むスキーマが設定されている必要があります。

table_no_schema.png

一方、「Use topic schema」もしくは「Use table schema」を選択した場合、メッセージのフィールド名とテーブルのフィールド名が一致した時、該当のフィールドの要素としてデータを挿入できます。

table_with_schema.png

また、「Use topic schema」か「Use table schema」を選択すると BigQuery 変更データキャプチャという機能を使用することができます。

https://cloud.google.com/bigquery/docs/change-data-capture?hl=ja
https://zenn.dev/cloud_ace/articles/0068533582574f

3.2. 「Use topic schema」と「Use table schema」の違い

両者の違いは、その名の通り、Subscription にアタッチされた Pub/Sub Topicのスキーマを使うか、Subscriber である BigQuery テーブルのスキーマを使うかです。

「Use topic schema」を選択した場合、Topic に対してスキーマを定義する必要があり、さらに Subscriber となるテーブルにも Topic のスキーマに適合するスキーマが定義されている必要があります(余分なフィールドが含まれていても問題ありません)。一方、「Use table schema」を選択した場合、テーブルにスキーマが定義されているだけで十分です。これによりスキーマ定義の手間は減りますが、どのようなメッセージでも Topic に配信ができてしまうため、運用を考えると次のような注意が必要です。

Topic にスキーマを定義すると、Topic へのメッセージ配信時にメッセージがスキーマにあっているか検証されます。この時、検証に失敗すると、Publisher に対してエラーが通知されます。しかし、Topic にスキーマが設定されておらず、メッセージが Subscriber に送信された際に問題が生じると、Publisher に対してエラーを通知する別の方法を用意する必要があります。そのようなシステムがなかった場合、Subscriber に対してメッセージが送り続けられたり、メッセージが消失するといった問題が生じる可能性があります。

bad_message_with_schema.png
Topic にスキーマを設定している場合、Topic への配信時に不適切なメッセージは拒否され、Publisher に対してエラーが通知される。

bad_message_no_schema.png
Topic にスキーマを設定していない場合、Subscriber への配信の段階で失敗することがある。もし Subscriber からレスポンスがなければ Subscribeription は Subscriber に対してメッセージを送り続け、最終的にメッセージが消失する可能性がある。

また、Subscriber が受領できるメッセージであったとしても、本来あるはずのフィールドの値がないなど、想定外な事象が発生する可能性は高まるかと思います。

そのため、Topic のスキーマなしで「Use table schema」を使用する際には少なくとも以下について考慮すると良いと思われます。

  • 「Drop unknown fields」を有効にする
    これを有効にしていないと、メッセージにテーブルスキーマに含まれていないフィールドが含まれていた際に、Subscriber への配信に失敗してしまいます。これを有効にしていた場合、一致したフィールドのみテーブルに書き込まれ、含まれていないフィールドは削除されます。

  • 「Dead lettering」を設定し、配信に失敗したメッセージを退避させる場所を用意する(推奨)
    Dead lettering では、配信の最大試行回数を超えたメッセージを別の Dead Letter Topic と呼ばれる Topic に再公開することができます。そのため、メッセージが気づかず消失してしまったということを回避できます。

dead_letter_topic.png
Dead letteringのアーキテクチャ図

そのほか、メッセージエラーの処理については以下をご覧ください。

https://cloud.google.com/pubsub/docs/handling-failures?hl=ja

4. 検証

本検証では、BigQuery Subscription における各スキーマ設定を簡単に試してみました。

4.1. 検証項目

各スキーマ設定 (None, Use table schema, Use topic schema) をした Subscription に対して、以下のようなメッセージを送信しました。

  • スキーマにあったメッセージ
  • スキーマにない余分なフィールドを加えたメッセージ
  • スキーマにあるフィールドを不足させたメッセージ

4.2. 検証方法

  1. BigQuery データセットとテーブルを作成する
  2. Pub/Sub Topic を作成する
  3. 各スキーマ設定で BigQuery Subscription を作成する
  4. 各 Topic にメッセージを配信し BigQuery テーブルに書き込まれたデータを確認する

4.3. 手順

1. BigQuery データセットとテーブルの作成

まずは Subscriber となる BigQuery のリソースを作成します。

export YOUR_PROJECT_ID=<実際のプロジェクトIDに置き換えてください>

# BigQuery データセットの作成
bq --location=asia-northeast1 mk \
    --dataset \
    $YOUR_PROJECT_ID:test_dataset

# 各スキーマ設定を行なった Subscription にアタッチする BigQuery テーブルの作成
bq mk \
--table \
$YOUR_PROJECT_ID:test_dataset.bqsubscription_none \
hoge:string,fuga:string,data:string 
bq mk \
--table \
$YOUR_PROJECT_ID:test_dataset.bqsubscription_table-schema \
hoge:string,fuga:string
bq mk \
--table \
$YOUR_PROJECT_ID:test_dataset.bqsubscription_topic-schema \
hoge:string,fuga:string

2. Pub/Sub Topicの作成

続いて Pub/Sub の Topic を作成します。

# スキーマをアタッチしない Topic の作成
gcloud pubsub topics create no_schema_topic

# スキーマとスキーマをアタッチする Topic の作成
gcloud pubsub schemas create test_schema --type=AVRO --definition='{
  "type": "record",
  "name": "Avro",
  "fields": [
    {"name": "hoge", "type": "string"},
    {"name": "fuga", "type": "string"}
  ]
}'
gcloud pubsub topics create with_schema_topic --schema=test_schema --message-encoding=JSON

3. BigQuery Subscriptionの作成

各スキーマ設定を行なった Subscription を作成します。「Use topic schema」を設定する Subscription にはスキーマを定義した Topic をアタッチします。

# スキーマ設定:「None」
gcloud pubsub subscriptions create "subscription_none" \
    --topic "no_schema_topic" \
    --bigquery-table=$YOUR_PROJECT_ID:test_dataset.bqsubscription_none

# スキーマ設定:「Use table schema」
gcloud pubsub subscriptions create "subscription_table-schema" \
    --topic "no_schema_topic" \
    --bigquery-table=$YOUR_PROJECT_ID:test_dataset.bqsubscription_table-schema --use-table-schema

# スキーマ設定:「Use topic schema」
gcloud pubsub subscriptions create "subscription_topic-schema" \
    --topic "with_schema_topic" \
    --bigquery-table=$YOUR_PROJECT_ID:test_dataset.bqsubscription_topic-schema --use-topic-schema

4. メッセージの配信と BigQuery テーブルに書き込まれたデータの確認

各 Topic に対してメッセージを配信します。

# no_schema_topic
gcloud pubsub topics publish no_schema_topic --message '{"hoge":"hoge", "fuga":"fuga"}'
# with_schema_topic
gcloud pubsub topics publish with_schema_topic --message '{"hoge":"hoge", "fuga":"fuga"}'

BigQuery に書き込まれたデータをコンソールから確認します。

result_none.png

result_table-schema.png

result_topic-schema.png

None ではメッセージが data カラムに挿入されてしまうのに対して、スキーマを使用した場合は一致したカラムに正しく挿入されていました。

続いてスキーマに対して余分なフィールドを追加したメッセージを配信してみます。

# no_schema_topic
gcloud pubsub topics publish no_schema_topic --message '{"hoge":"hoge", "fuga":"fuga", "piyo":"piyo"}'
# with_schema_topic
gcloud pubsub topics publish with_schema_topic --message '{"hoge":"hoge", "fuga":"fuga", "piyo":"piyo"}'

Topic への配信には成功したのでデータを確認します。

result_none_extra.png

result_table-schema_extra.png

result_topic-schema_extra.png

None では先ほどと同様にメッセージが丸ごと data カラムに挿入されていました。Use table schema を設定した Subscriber のテーブルには、追加データが挿入されていませんでした。余分なフィールド(piyo)のせいで Subscriber がメッセージを正常に受け取れなかったことが考えられます。ただし、この問題は「4.2. 「Use topic schema」と「Use table schema」の違い」で述べたように、「Drop unknown fields」というオプションを有効にすることで回避可能と思われます。Use topic schema の方では、余分なフィールドが無視され、先ほどと同様に正常にデータが挿入されていました。

最後にスキーマに対してフィールドを不足させたメッセージを配信してみます。

# no_schema_topic
gcloud pubsub topics publish no_schema_topic --message '{"hoge":"hoge"}'
# with_schema_topic
gcloud pubsub topics publish with_schema_topic --message '{"hoge":"hoge"}'

スキーマを持たない Topic (no_schema_topic) への配信には成功しましたが、スキーマを持つ Topic (with_schema_topic) への配信を試みた際には、フィールド(fuga)が不足しているというエラーが発生しました。

error.png

配信に成功した Topic の Subscriber であるテーブルを確認してみます。

result_none_missing.png

result_table-schema_missing.png

Use table schema の方では、不足分のカラムには null が入り、それ以外のフィールドには正常にメッセージが入っていました。

5. まとめ

最後に各オプションの違いを表にまとめてみました!

特徴 / オプション None Use topic schema Use table schema
メッセージの挿入先 メッセージを「data」カラムに挿入 メッセージのフィールドと一致する BigQuery テーブルのフィールドに対してデータが挿入される メッセージのフィールドと一致するBigQueryテーブルのフィールドに対してデータが挿入される
テーブルのスキーマに必須のフィールド data Topic のスキーマで必須としたフィールド 特にないが配信想定のメッセージに沿ったスキーマを定義しておく必要がある
Topic スキーマ設定の必要性 不要 必要 不要(ただしメッセージが JSON 形式である必要あり)
Topic へのメッセージ配信に失敗する可能性 高(例:Topic で必須のフィールドがない、メッセージにスキーマで設定されていないフィールドがある)
Subscriber へのメッセージ送信に失敗する可能性 低(「data」カラムさえあれば OK) 低(各スキーマ設計が正しければ失敗は発生しにくい) 中(例:BigQuery テーブルに存在しないフィールドがメッセージに存在する時→「Drop unknown fields」オプションで回避可能、メッセージにないフィールドが nullable でない時)
Dead lettering の設定の推奨度 高(Subscriber へのメッセージ送信に失敗する可能性が他2つと比較して高いため)

本記事では BigQuery Subscription のスキーマ設定について紹介しました。BigQuery Subscription を使用する際は、参考にしていただけますと幸いです🙌

Discussion