Уровни совместимости
В предыдущем уроке мы увидели, какие операции каждый формат поддерживает. Но “поддерживает” — это не “безопасно”. Добавление поля в Avro безопасно, только если новое поле имеет default. Удаление поля безопасно, только если consumers его не используют. Compatibility levels формализуют эти правила.
В модуле 04 мы определили три базовых уровня — BACKWARD, FORWARD, FULL. Там же была ссылка: “Подробнее о TRANSITIVE вариантах — в модуле 10”. Вот мы и здесь. Разберём все 7 уровней плюс NONE, с конкретными правилами для каждого формата.
Модель: кто обновляется первым?
Уровень совместимости — это ответ на вопрос: в каком порядке можно обновлять producer и consumer? В распределённой системе с N producers и M consumers одновременный деплой невозможен. Кто-то обновляется первым — и в этот момент в системе сосуществуют старая и новая версии схемы.
BACKWARD
BACKWARD: consumer обновляется первым. Новый reader читает старые данные. Producer обновляется позже, когда все consumers готовы.FORWARD
FORWARD: producer обновляется первым. Новый writer пишет данные, которые старый reader может прочитать. Consumer обновляется позже.FULL
FULL: порядок не важен. И producer, и consumer могут обновляться в любом порядке — совместимость в обе стороны.7+1 уровней: полная картина
Schema Registry поддерживает 7 настраиваемых уровней плюс специальный NONE:
TRANSITIVE vs non-TRANSITIVE: ключевая разница
Разница между BACKWARD и BACKWARD_TRANSITIVE — не косметическая. Она определяет, насколько далеко в историю гарантируется совместимость.
Без TRANSITIVE — проверка только против непосредственно предыдущей версии. Проблема: если v1 → v2 совместимы, и v2 → v3 совместимы, это не гарантирует, что v1 → v3 совместимы. Совместимость не транзитивна по умолчанию!
С TRANSITIVE — проверка против всех предыдущих версий. Гарантирует, что цепочка совместимости не разорвётся.
BACKWARD (non-transitive) — default в Confluent Schema Registry. Для production Kafka с retention больше одного цикла деплоя этого недостаточно. Если ваш retention = 7 дней, а деплой раз в неделю — в топике данные двух последних версий. BACKWARD работает. Если retention = 30 дней и деплой ежедневно — нужен BACKWARD_TRANSITIVE. Правило: если количество активных версий схемы в retention > 2, используйте TRANSITIVE.
Допустимые изменения по уровням
Каждый уровень ограничивает набор допустимых изменений. Чем строже уровень — тем меньше можно менять:
Закономерность: BACKWARD разрешает удаление, но запрещает добавление без default. FORWARD — наоборот. FULL — только операции, безопасные в обе стороны.
Правила по форматам: Avro vs Protobuf vs JSON Schema
Schema Registry поддерживает три формата, и правила совместимости для каждого различаются:
Avro
Avro compatibility определяется ResolvingDecoder — его мы детально разобрали в модуле 04. Ключевые правила:
- Add field: обязателен default для BACKWARD (reader подставит его для старых данных)
- Remove field: безопасно для BACKWARD (reader пропустит неизвестное поле). Для FORWARD — нужен default в удаляемом поле (old reader подставит default)
- Type promotion:
int→long,long→float,float→double,string→bytes— одностороннее, только widening - Union evolution: добавление типа в union — FORWARD compatible (старый reader может получить новый тип, который не знает). Удаление — BACKWARD compatible
- Enum evolution: добавление символа — FORWARD compatible. Удаление — BACKWARD compatible
Protobuf
Protobuf compatibility базируется на wire format (varint, fixed, length-delimited) — не на .proto определениях:
- Add field: всегда безопасно — новый field number, старый ридер пропускает unknown fields
- Remove field: безопасно если field number помечен
reserved. Без reserved — будущий разработчик может переиспользовать номер с другим типом - Type change: совместимы пары с одинаковым wire type.
int32/int64/sint32/sint64/bool— все varint.fixed32/sfixed32/float— все 32-bit.fixed64/sfixed64/double— все 64-bit.string/bytes— оба length-delimited - Enum: добавление значения — forward compatible. Удаление — backward.
allow_alias = trueразрешает несколько имён для одного числового значения (аналог Avro aliases) - oneof: добавление поля в oneof — совместимо. Перемещение поля из обычного в oneof — breaking change
JSON Schema
JSON Schema compatibility — самая сложная из трёх, потому что JSON Schema описывает constraints, а не структуру:
- additionalProperties: по умолчанию
true— новые поля разрешены. Еслиfalse— добавление поля = breaking для FORWARD (старый reader сadditionalProperties: falseотклонит новые поля) - required: добавление в required array — breaking для BACKWARD (старые данные не содержат поле). Удаление из required — breaking для FORWARD
- Type restriction: сужение
"type": ["string", "number"]до"type": "string"— breaking для FORWARD. Расширение — breaking для BACKWARD - Pattern/format: добавление pattern — breaking для FORWARD (данные, ранее валидные, могут не пройти). Удаление — breaking для BACKWARD
JSON Schema compatibility — единственная из трёх, где additionalProperties кардинально меняет семантику. С additionalProperties: true (default) JSON Schema максимально гибка — добавление полей всегда безопасно. С false — каждое добавление = potential breaking change. Recommendation: не используйте additionalProperties: false в schemas, которые будут эволюционировать.
Выбор уровня: decision tree
Как выбрать правильный уровень для конкретного pipeline:
Контролируете обе стороны?
Начальный вопрос: контролируете ли вы и producers, и consumers? Если нет — нужен более строгий уровень.Retention > 1 deploy cycle?
Следующий вопрос: данные в retention дольше одного цикла деплоя?Можно ломать old consumers?
Вы можете сломать старых consumers?Рекомендации для типичных сценариев
Kafka-топик внутри одного сервиса — BACKWARD. Одна команда, координированный деплой, retention обычно короткий.
Kafka-топик с множеством consumers — BACKWARD_TRANSITIVE. Consumers обновляются не одновременно, в retention могут быть данные нескольких версий.
Public API / event contract — FULL_TRANSITIVE. Consumers вне вашего контроля, breaking change = инцидент.
Development / prototyping — NONE. Схема меняется часто и кардинально. Переключить на BACKWARD_TRANSITIVE перед production.
CDC pipeline (Debezium) — BACKWARD_TRANSITIVE для value schema (структура данных эволюционирует). FULL для key schema (ключ не должен ломаться ни в каком направлении).
Schema Registry позволяет устанавливать уровень per-subject, не только глобально. Используйте это: ключевые shared schemas на FULL_TRANSITIVE, internal service schemas на BACKWARD_TRANSITIVE, development topics на NONE. Глобальный уровень — fallback default.
Subject naming и уровни
Выбор subject naming strategy напрямую влияет на scope compatibility checks:
TopicNameStrategy (default) — subject = {topic}-value или {topic}-key. Все producers в один топик обязаны использовать совместимые схемы. Один топик = один контракт.
RecordNameStrategy — subject = fully qualified record name (com.example.User). Одна логическая сущность = один контракт, независимо от топика. Один сервис может писать User в разные топики — совместимость проверяется по сущности.
TopicRecordNameStrategy — subject = {topic}-{record}. Один рекорд в одном топике = один контракт. Самый гранулярный: User в orders и User в analytics — два независимых контракта.
Naming strategy определяет scope: при TopicNameStrategy два разных record types в одном топике должны быть совместимы между собой — что часто нежелательно. RecordNameStrategy снимает это ограничение.
Конфигурация в Schema Registry
Уровень устанавливается через REST API:
# Глобальный уровень (default для всех subjects)
PUT /config
{"compatibility": "BACKWARD_TRANSITIVE"}
# Per-subject уровень (переопределяет глобальный)
PUT /config/{subject}
{"compatibility": "FULL_TRANSITIVE"}
# Проверка текущего уровня
GET /config/{subject}
{"compatibilityLevel": "FULL_TRANSITIVE"}
# Проверка совместимости вручную (dry run)
POST /compatibility/subjects/{subject}/versions/latest
Body: {"schema": "...", "schemaType": "AVRO"}
Response: {"is_compatible": true}
Dry-run проверка — ключевая операция для CI/CD: проверить совместимость до регистрации, в build pipeline. Подробнее о CI/CD интеграции — в уроке 04.
Что дальше
Уровни совместимости — это правила. Enforcement этих правил обеспечивает Schema Registry — централизованный сервис, хранящий все версии схем и проверяющий совместимость при каждой регистрации. В следующем уроке мы разберём архитектуру Confluent Schema Registry: _schemas topic, single-primary leader election, monotonic IDs, REST API, и deployment patterns.