KafkaでProtobufを採用する最大の理由は、軽量なワイヤ形式と強い型付け、そしてスキーマ進化のしやすさにあります。ただし、その前提は「フィールド番号」を正しく扱うことです。
本稿では、Confluent Schema Registry を利用した一般的な実装を前提に、CCDAKの出題範囲に沿って、フィールド番号と互換性の安全運用を具体例で解説します。
ConfluentのProtobufシリアライザは、メッセージペイロードにマジックバイトとスキーマIDを付加し、Schema Registry と連携してスキーマ進化を安全にします。コンシューマはスキーマIDからスキーマを取得し、バイト列を解釈します。
Protobufはワイヤ上でフィールド番号を用います。番号が意味を持つため、番号の再利用や付け替えは後方互換性を破壊します。スキーマの見た目(フィールド名)よりも、実体(番号と型)が重要だと理解してください。
Kafka × Protobuf × Schema Registry の関係
Protobufのフィールド番号は1〜536870911の範囲で、19000〜19999は内部予約のため使用不可です。番号はワイヤ互換性の核心であり、いったん公開したら変更しない・再利用しないが鉄則です。
番号にはエンコード上のコスト差があります。1〜15はタグが短く、頻出・必須に近いフィールドに割り当てると効率的です。将来の拡張を見越し、論理ブロックやチームごとに番号帯を計画的に確保しておくと運用が安定します。
.proto 例: 予約と追加のパターン
syntax = "proto3";
package com.example;
message OrderV1 {
// 頻出フィールドは小さい番号
int64 order_id = 1;
string customer_id = 2;
// 将来の状態コード
int32 status_code = 10;
}
// 進化後
message OrderV2 {
int64 order_id = 1;
string customer_id = 2;
// status_code を enum に置き換えたい → 新番号で追加
// 旧 10 は非推奨 → 予約して再利用禁止
reserved 10; // 旧: status_code
// 名前も予約できる
reserved "status_code";
// 新しい enum ベースの状態。旧クライアントは未認識として無視
OrderStatus status = 20;
// フィールド削除時は [deprecated = true] → 後で reserved へ
string note = 30 [deprecated = true];
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0; // 0 はデフォルト
ORDER_STATUS_PLACED = 1;
ORDER_STATUS_SHIPPED = 2;
ORDER_STATUS_CANCELLED = 3;
}
Schema Registry の互換性モードは主に Backward / Forward / Full の3種類。Protobufでは「新フィールド追加(新番号)」は多くのケースで後方互換的です。旧コンシューマは未認識フィールドを無視するため、既存の読み取りは成立します。
破壊的なのは、番号変更・再利用、型変更、oneof への移動、enum 番号変更などです。見た目の名称変更はワイヤ上は成立しても、コード生成や検証で破綻しやすく、実務上は破壊的とみなすのが安全です。
| 互換モード | 許容される変更例 | 失敗する代表例 |
|---|---|---|
| Backward | 新しい optional 相当のフィールドを新番号で追加; 既存フィールドの doc コメント更新 | 既存フィールドの番号変更・再利用; スカラー型の変更(int32→string など) |
| Forward | 使わなくなったフィールドを非推奨→実質的な削除(新コンシューマが扱える) | 旧メッセージ必須相当の情報を削る(新→旧で読めない) |
| Full | 命名やコメントの微修正; 完全に独立な新フィールドの追加 | 番号変更・再利用; oneof へ既存フィールドを移動; enum 番号の変更 |
削除や置換は段階的に進めます。いきなりフィールドを消すと旧アプリのコンパイルが通らず、あるいは互換性検証で落ちます。非推奨フラグ→利用停止→予約の順が安全です。
番号と名前の両方を reserved に入れることで、将来の誤再利用を防げます。スキーマ履歴を Schema Registry と一緒に運用し、PRレビューチェックに互換性検証を組み込むと事故が減ります。
.proto で削除時に行う定型
message CustomerV2 {
int64 id = 1;
string email = 2;
// 旧 phone は利用停止済み → 削除して予約
reserved 5; // 番号の再利用禁止
reserved "phone"; // 名前の再利用禁止
}
oneof にフィールドを追加すると、旧クライアントはその分岐を未認識として扱い、期待する意味論を満たせない可能性があります。Schema Registry の検証を通っても、アプリ互換性としては破壊的になりがちです。
map は内部的に繰り返しメッセージへ変換されます。キー・値の型変更は破壊的。enum は値の追加は安全ですが、値番号の変更や再割当ては破壊的です。名称変更はワイヤ上は許容でも、生成コードや読みやすさの観点から原則避けます。
Subject Naming Strategy は再利用戦略に直結します。TopicNameStrategy(デフォルト、<topic>-value)、RecordNameStrategy(メッセージ型単位で共有)、TopicRecordNameStrategy(トピック×型)を要件に応じて選択します。
Protobufシリアライザ/デシリアライザはそれぞれ io.confluent.kafka.serializers.protobuf.KafkaProtobufSerializer / KafkaProtobufDeserializer を使用します。生成クラスを返したい場合は specific.protobuf.value.type を設定します。
互換性レベルはグローバルやサブジェクト単位で設定可能です。開発環境は Backward、プロダクションは Full を基本とし、破壊的変更をブロックする運用が無難です。
Schema Registry とクライアント設定の例
# Producer/Consumer プロパティ(抜粋)
key.serializer=org.apache.kafka.common.serialization.StringSerializer
value.serializer=io.confluent.kafka.serializers.protobuf.KafkaProtobufSerializer
schema.registry.url=http://localhost:8081
# 型を返したい場合(例)
specific.protobuf.value.type=com.example.OrderV2
# Subject Naming Strategy を変更したい場合(例)
value.subject.name.strategy=io.confluent.kafka.serializers.subject.RecordNameStrategy
# 互換性モード設定(サブジェクト単位)
# Backward/Forward/Full などを指定
curl -X PUT -H "Content-Type: application/json" \
--data '{"compatibility": "FULL"}' \
http://localhost:8081/config/my-orders-value
CCDAK
問題 1
注文イベントの Protobuf スキーマで、これまで数値の status_code (番号10) を使っていました。今後は意味を明確にするため enum 型の status を導入したい。既存のコンシューマは段階的に移行する計画です。最も安全な対応はどれか?
正解: A
最も安全なのは新番号で enum フィールドを追加し、旧フィールドは非推奨→reserved として再利用を防ぐ手順。番号の再利用や型変更は破壊的で、互換性チェックを無効化する運用も推奨されない。
フィールド番号は飛ばしてもよいですか?
問題ありません。将来の拡張やチーム間の分割に備えて余白を設けるのは有効です。特に1〜15は希少なので優先配分しつつ、次のブロックを空けておく運用が実務的です。
同じ番号でフィールドの型だけを変えるのは可能ですか?
不可です。番号はそのフィールドの同一性そのもので、型変更は破壊的です。新番号で新フィールドを追加し、旧フィールドは段階的に廃止してください。
Schema Registry なしでも Protobuf は使えますか?
技術的には可能ですが、互換性の自動検証やスキーマ配布ができず運用負荷が跳ね上がります。CCDAK および本番運用では Schema Registry の利用を前提に設計するのが無難です。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Kafka Topic と Partition の基礎: 分散とスケーラビリティの要
CCDAK 対策と実務の両立を意識し、Topic/Partition/Replica/Consumer Group の役...
CCDAK 試験ガイド:出題範囲・配点・申込み・対策
Confluent Certified Developer for Apache Kafka (CCDAK) の出題範囲...
Confluent Certified Administrator (CCAAK) 対策: 出題範囲・配点の考え方・運用観点の要点
CCAAKに向けて、試験領域の押さえどころを運用目線で整理。プロダクションで通用する設定・監視・セキュリティの実践知を、...
Kafka の Replica と In-Sync Replicas を正しく設計する: 耐障害性と一貫性
レプリカとISRの仕組みを起点に、acks と min.insync.replicas、クリーン/アンクリーンリーダー選...
Kafka の Offset とコミット: ポジション管理と at-least-once の基礎
CCDAK 対策と実務の両立を意識して、Kafka コンシューマのオフセット管理とコミット戦略を整理。at-least-...