Pub/Subのスキーマ

# Pub/Subのスキーマとは
Google CloudのメッセージングサービスPub/Subは、オプションでトピックにスキーマを適用することができます。スキーマを適用した場合、スキーマに準拠しないメッセージの公開を制限します。また、スキーマは変更があるとリビジョンをとることができ、複数のリビジョンを同時にトピックに関連付ける事ができます。一方で、スキーマで定義されたフィールド以外のデータ入っていても公開できたりするので、実際の様子を見てみたいと思います。

# スキーマの作成
コンソールからスキーマは作成できます。スキーマの定義は、Apache AVROかプロトコルバッファーでできます。ここでは、`person`というスキーマを作成し、フィールドに`Name`と`Age`を持ちます。

![Screenshot 2024-05-21 at 17.31.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/f2e0a353-2f65-aa34-b50d-c3683424b05b.png)

そして、トピックにスキーマを適用できます。トピックの作成や編集時に、`スキーマを使用する`を選択し、対象のスキーマを選びます。

![Screenshot 2024-05-21 at 17.32.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/22f06491-521c-62a3-ac4a-e6288912a8d1.png)

以上です。簡単ですね。

# メッセージを公開してみる
まずは、スキーマ通りのメッセージを投げ込んでみます。コンソールでもできますが、コマンドで実行します。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Taro", "Age": 3}'
messageIds:
- '11327282810297513'
```

特にエラーなく、メッセージIDが返ってきました。次にサブスクリプションから取得してみます。

```bash
$ gcloud pubsub subscriptions pull projects/$(gcloud config get-value project)/subscriptions/schema-changing-test-sub --format="value(message.data)" --auto-ack | jq
{
  "Name": "Taro",
  "Age": 3
}
```

取れました。ま、これは当たり前ですね。

# スキーマに定義されているフィールドを含めて公開してみる
次に`Age`を含まないメッセージをトピックに投げ込んでみます。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Jiro"}'
ERROR: (gcloud.pubsub.topics.publish) INVALID_ARGUMENT: Invalid data in message: Message failed schema validation.
- '@type': type.googleapis.com/google.rpc.ErrorInfo
  domain: pubsub.googleapis.com
  metadata:
    message: Message failed schema validation
    revisionInfo: 'Could not validate message with any schema revision for schema:
      projects/111111111/schemas/person, last checked revision: revision_id=df0473fb
      failed with status: Invalid data in message: Field was not found in JSON object:
      Age.'
  reason: INVALID_JSON_AVRO_MESSAGE
```

Jiroは怒られました。`Age`がないよと。ちゃんとチェックしてくれていますね。

# スキーマにないフィールドを一部含めずにメッセージを公開してみる
では、スキーマにないフィールドが投げ込まれたときもちゃんと拒否されるでしょうか。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Sabro", "Age": 21, "Gender": "Male"}'
messageIds:
- '11327344946086872'
```

普通に通っちゃいました。そして下記の通りサブスクライバーからも見えます。

```bash
$ gcloud pubsub subscriptions pull projects/$(gcloud config get-value project)/subscriptions/schema-changing-test-sub --format="value(message.data)" --auto-ack | jq
{
  "Name": "Sabro",
  "Age": 21,
  "Gender": "Male"
}
```

つまり、余計なものは許可されます。

# スキーマを変更してみる（初期フィールドの削除）
フィールドの削除をコンソールから行います。スキーマを開いて`リビジョンの作成`とすると下記のように編集できます。

![Screenshot 2024-05-21 at 17.48.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/eafb9797-c19c-0c7c-90be-74acef8b285f.png)

ところが、`確定`しようとしたら怒られました。オプションじゃないフィールドは消せないと。

![Screenshot 2024-05-21 at 17.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/940e94bb-6e78-8ef9-f3da-3dd969c9747a.png)

# スキーマを変更してみる（フィールドの追加）
であれば、追加してみます。

![Screenshot 2024-05-21 at 17.51.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/3be270f9-a717-b7d5-40f8-8a8aea2159ac.png)

が、また怒られました。これもオプションじゃないフィールドだからとのこと。

![Screenshot 2024-05-21 at 17.52.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/bff459d0-162f-fd05-b400-923453687a41.png)

オプションフィールドとするには`default`を設定すれば良さそうです。

![Screenshot 2024-05-21 at 17.57.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/25cffd0f-53ae-2785-9f6b-1fe413d8510b.png)

そしてこれは無事成功しました。するとこんな感じでリビジョンができます。

![Screenshot 2024-05-21 at 17.58.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/b4df592c-c213-e870-3e15-d4ea612d4e91.png)

# スキーマに追加したフィールドを含めずにメッセージを公開してみる
まずは、新しいスキーマのみをトピックに適用します。次のように、トピックの編集でリビジョンの範囲を最新のリビジョンのみになるようにします。

![Screenshot 2024-05-21 at 18.02.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/65b147ca-e13b-4456-3ba6-e319d0ed04a0.png)

反映にはすこし時間がかかるようなので数分待ってから、まずは普通にスキーマにあるフィールドを全部含めたメッセージを公開します。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Shiro", "Age": 23, "Gender": "
Male"}'
messageIds:
- '11327883030332909'

$ gcloud pubsub subscriptions pull projects/$(gcloud config get-value project)/subscriptions/schema-changing-test-sub --format="value(message.data)" --auto-ack | jq
{
  "Name": "Shiro",
  "Age": 23,
  "Gender": "Male"
}
```

これはいけます。では、`Gender`を削ってみます。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Shiro", "Age": 23}'
ERROR: (gcloud.pubsub.topics.publish) INVALID_ARGUMENT: Invalid data in message: Message failed schema validation.
- '@type': type.googleapis.com/google.rpc.ErrorInfo
  domain: pubsub.googleapis.com
  metadata:
    message: Message failed schema validation
    revisionInfo: 'Could not validate message with any schema revision for schema:
      projects/111111111111/schemas/person, last checked revision: revision_id=e9712880
      failed with status: Invalid data in message: Field was not found in JSON object:
      Gender.'
  reason: INVALID_JSON_AVRO_MESSAGE
```

はじかれました。

# 初期作成時にAgeにdefaultつけてみる
では、はじめに作る際にdefaultつけるとどうでしょうか。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Goro"}'
ERROR: (gcloud.pubsub.topics.publish) INVALID_ARGUMENT: Invalid data in message: Message failed schema validation.
- '@type': type.googleapis.com/google.rpc.ErrorInfo
  domain: pubsub.googleapis.com
  metadata:
    message: Message failed schema validation
    revisionInfo: 'Could not validate message with any schema revision for schema:
      projects/111111111111/schemas/person, last checked revision: revision_id=23f1cb3a
      failed with status: Invalid data in message: Field was not found in JSON object:
      Age.'
  reason: INVALID_JSON_AVRO_MESSAGE
```

ま、ここは同じく弾かれますね。ただ、`Age`を消すことはできました。

![Screenshot 2024-05-21 at 18.23.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/fe908513-4ad5-21d7-4b1d-83006dbf2de9.png)

そして、`Name`だけで公開できました。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Goro"}'
messageIds:
- '11327997684512795'
```

# 複数のリビジョンを適用してみる
では、最後にこれまで単一のリビジョンをトピックに当ててきましたが、複数のリビジョンを当てるとどうなるか見てみます。スキーマの変更が発生して移行期間などこういう状態が考えられます。

まずトピックの編集でリビジョンの範囲の指定で異なるリビジョンを指定します。

![Screenshot 2024-05-21 at 18.44.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54176/5646cdf2-453f-c0c6-4fe1-1d7d9f6ef5ce.png)

これでどちらのスキーマのメッセージも通るはずです。つまり、先程まで`Gender`がないと怒られましたが、許してくれるようになるはずです。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Tokuro", "Age": 42}'
messageIds:
- '11327156735462791'
```

いけましたね。

記録のため、他に色々試しておきます。

```bash
$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Tokuro"}'                     
ERROR: (gcloud.pubsub.topics.publish) INVALID_ARGUMENT: Invalid data in message: Message failed schema validation.
- '@type': type.googleapis.com/google.rpc.ErrorInfo
  domain: pubsub.googleapis.com
  metadata:
    message: Message failed schema validation
    revisionInfo: 'Could not validate message with any schema revision for schema:
      projects/111111111111/schemas/person, last checked revision: revision_id=2443500c
      failed with status: Invalid data in message: Field was not found in JSON object:
      Age.'
  reason: INVALID_JSON_AVRO_MESSAGE


$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Tokuro", "Age": 42, "Gender": "Female"}'                                                                                                                                              
messageIds:
- '11328254979892295'


$ gcloud pubsub topics publish schema-changing-test --message='{"Name": "Tokuro", "Age": 42, "Gender": "Female", "BloodType": "A"}'
messageIds:
- '11328291513640241'
```

# まとめ
- スキーマで定義されたフィールドがメッセージにないと公開できない
- スキーマに無いフィールドが入っていても公開できるし、サブスクライバーもそれを受け取れる
- スキーマ作成時に`default`をつけたフィールドはあとから削除できる
- 逆に、defaultのない初期フィールドはあとから削除できない
- defaultをつけないとあとからフィールドを追加できない
- リビジョンの範囲を指定して、複数リビジョンを適用することで、複数のスキーマに準拠したメッセージを同時に許可できる