Контентные модели и проблемы эволюции JSON-схем
Yandex Schema Registry поддерживает работу со схемами AvroOptional-friendly
. В Schema Registry реализована политика проверки совместимости JSON-схем, которая позволяет сохранять совместимость схем при добавлении и удалении опциональных параметров.
В этой статье описываются:
- Основы совместимости схем.
- Контентные модели JSON-схем.
- Проблемы эволюции схем в контентных моделях.
- Сохранение совместимости при эволюции опциональных параметров.
Если вы хотите использовать в Data Transfer формат схемы JSON Schema с проверкой на обратную совместимость и при этом обеспечить добавление и удаление опциональных параметров:
-
Задайте в пространстве имен политику проверки совместимости
Optional-friendly
. -
Задайте в настройках сериализации эндпоинта-приемника Yandex Managed Service for Apache Kafka® следующие настройки Debezium:
- Для ключа схемы:
key.converter.dt.json.generate.closed.content.schema
—true
. - Для значения схемы:
value.converter.dt.json.generate.closed.content.schema
—true
.
- Для ключа схемы:
Совместимость схем
Совместимость схем — это совокупность отношений между новой версией схемы и предыдущей версией или всеми предыдущими версиями этой схемы. Характер этих отношений задается параметрами Уровень проверки совместимости и Политика проверок совместимости для JSON.
В зависимости от типа совместимости эти отношения позволяют приложениям с более новой версией схемы читать данные, записанные в старой схеме, и наоборот. Эволюция схемы данных также зависит от используемого типа совместимости. Подробнее о совместимости схем
Schema Registry основан на стандарте Confluent Schema Registry, который выделяет следующие типы совместимости:
- Обратная совместимость (
BACKWARD_COMPATIBILITY
) — приложение с новой версией схемы корректно читает данные, записанные в предыдущей версии схемы. - Обратная транзитивная совместимость (
BACKWARD_TRANSITIVE_COMPATIBILITY
) — приложение с новой версией схемы корректно читает данные, записанные в любой из предыдущих версий схемы. - Прямая совместимость (
FORWARD_COMPATIBILITY
) — приложение с предыдущей версией схемы корректно читает данные, записанные в новой версии схемы. - Прямая транзитивная совместимость (
FORWARD_TRANSITIVE_COMPATIBILITY
) — приложение с любой из предыдущих версий схемы корректно читает данные, записанные в новой версии схемы. - Полная совместимость (
FULL_COMPATIBILITY
) — одновременно выполняются условия обратной и прямой совместимости. - Полная транзитивная совместимость (
FULL_TRANSITIVE_COMPATIBILITY
) — одновременно выполняются условия обратной транзитивной и прямой транзитивной совместимости. - Без проверки совместимости (
NONE
) — проверка совместимости не производится.
Подробнее о типах совместимости см. в документации Confluent Schema Registry
Контентные модели JSON-схем
Контентная модель — это совокупность правил, по которым может изменяться поле типа object
в схеме. У разных полей в одной схеме могут быть разные контентные модели.
В JSON Schema существуют следующие контентные модели:
В зависимости от типа, контентные модели допускают следующие действия:
- добавление нового обязательного параметра;
- добавление нового опционального параметра;
- удаление обязательного параметра;
- удаление опционального параметра;
- изменение типа параметра с опционального на обязательный;
- изменение типа параметра с обязательного на опциональный.
Открытая контентная модель
Открытая контентная модель допускает параметры, заданные в схеме, а также добавление других параметров любого типа. Эта модель используется по умолчанию.
Описание схемы в открытой контентной модели выглядит так:
{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"additionalProperties": true
}
Где additionalProperties
— возможность добавления параметров, true
или false
. По умолчанию параметр имеет значение true
, поэтому при описании схем в открытой контентной модели он часто опускается.
Закрытая контентная модель
Закрытая контентная модель допускает только параметры, заданные в схеме. Добавлять новые параметры нельзя.
Описание схемы в закрытой контентной модели выглядит так:
{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"additionalProperties": false
}
Частично открытая контентная модель
Частично открытая контентная модель позволяет добавлять новые параметры, если они соответствуют заданным ограничениям. Реализовать ограничения можно следующими способами:
- Разрешить добавление параметров только определенного типа, задав ограничение в параметре
additionalProperties
. - Разрешить добавление параметров определенных типов с заданным префиксом перед именем, перечислив префиксы и соответствующие типы параметров в объекте
patternProperties
.
Ограничение по типу добавляемого параметра
В такой схеме допустимые типы новых параметров описываются в параметре additionalProperties
. Добавление параметров другого типа не допускается.
Описание схемы выглядит так:
{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"additionalProperties": {"type": "string"}
}
Эта схема разрешает добавлять только новые параметры типа string
.
Ограничение по типу добавляемого параметра на основе префикса имени параметра
В такой схеме параметр additionalProperties
имеет значение false
, как в закрытой схеме. Допустимые новые параметры описываются в объекте patternProperties
, при этом имени каждого параметра присваивается префикс, указывающий на тип параметра. Имена параметров в схеме описываются регулярными выражениями. Добавление параметров, не описанных в patternProperties
, не допускается. Описание схемы выглядит так:
{
"type": "object",
"properties": {
"s_name": {"type": "string"},
"i_age": {"type": "integer"}
},
"patternProperties": {
"^s_": {"type": "string"},
"^i_": {"type": "integer"}
},
"additionalProperties": false
}
Эта схема разрешает добавлять только:
- Параметры типа
string
, имена которых начинаются сs_
. - Параметры типа
integer
, имена которых начинаются сi_
.
Проблемы эволюции JSON-схем в контентных моделях
Открытая и закрытая контентные модели по-своему ограничивают возможности эволюции схемы при сохранении совместимости с предыдущими схемами. Конкретные ограничения в зависимости от типа модели представлены в таблицах.
Открытая контентная модель
Изменение схемы | Прямая совместимость | Обратная совместимость | Полная совместимость |
---|---|---|---|
Добавление обязательного параметра | |||
Добавление опционального параметра | |||
Удаление обязательного параметра | |||
Удаление опционального параметра | |||
Изменение опционального параметра на обязательный | |||
Изменение обязательного параметра на опциональный |
Закрытая контентная модель
Изменение схемы | Прямая совместимость | Обратная совместимость | Полная совместимость |
---|---|---|---|
Добавление обязательного параметра | |||
Добавление опционального параметра | |||
Удаление обязательного параметра | |||
Удаление опционального параметра | |||
Изменение опционального параметра на обязательный | |||
Изменение обязательного параметра на опциональный |
Частично открытая контентная модель
Частично открытая контентная модель позволяет добавлять новые параметры с сохранением совместимости. Однако реализация через указание типа добавляемого параметра в additionalProperties
позволяет добавлять в модель только параметры одного типа. Это ограничивает практическую применимость такого решения.
Более универсальной реализацией является перечисление возможных типов параметров с присвоением имени параметра соответствующего префикса. Однако, в таком случае нужно описать в patternProperties
все возможные параметры, включая объекты object
и массивы array
.
Универсальная схема может выглядеть так:
{
...
"$ref": "#/definitions/obj",
"definitions": {
"obj": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^i_": { "type": "integer" },
"^n_": { "type": "number" },
"^s_": { "type": "string" },
"^b_": { "type": "boolean" },
"^o_": { "$ref": "#/definitions/obj"},
"^ai_": { "type": "array", "items": {"type": "integer"} },
"^an_": { "type": "array", "items": {"type": "number" } },
"^as_": { "type": "array", "items": {"type": "string" } },
"^ab_": { "type": "array", "items": {"type": "boolean" } },
"^ao_": { "type": "array", "items": {"$ref": "#/definitions/obj"} }
}
}
}
}
В примере выше не задано ни одного параметра, только описан универсальный набор возможных параметров в patternProperties
. Такая реализация имеет следующие недостатки:
- Схема становится громоздкой и трудноподдерживаемой.
- Возможную ошибку в
patternProperties
нельзя исправить без потери совместимости. - Невозможно использование новых типов параметров, если такие будут добавлены в спецификацию JSON, потому что они не описаны в
patternProperties
. - Для практического применения такой схемы требуется строгое соответствие имен всех параметров описанным шаблонам. Это возможно, только если вы контролируете производителя данных.
Сохранение совместимости при эволюции опциональных параметров
Из сравнительной характеристики поведения контентных моделей при эволюции схем следует, что открытая и закрытая модели не обеспечивают полной совместимости при добавлении или удалении опциональных параметров. На практике это приводит к возникновению ошибок PROPERTY_ADDED_TO_OPEN_CONTENT_MODEL
и PROPERTY_REMOVED_FROM_CLOSED_CONTENT_MODEL
при операциях с опциональными параметрами. Частично открытая контентная модель поддерживает совместимость в этих случаях, но имеет существенные недостатки, которые ограничивают ее использование.
Альтернативным решением этой проблемы является использование разных схем для производителей и потребителей данных. Это возможно, потому что:
- Производители никогда не потребляют данные, следовательно их схемы не должны поддерживать совместимость друг с другом.
- Потребители никогда не производят данные, следовательно их схемы также не должны поддерживать совместимость друг с другом.
- Главным требованием остается совместимость схем производителя и потребителя данных.
Для реализации этого решения производители и потребители должны использовать разные контентные модели:
- Производитель должен использовать закрытую контентную модель, потому что на стороне производителя всегда известен точный набор параметров.
- Потребитель должен использовать открытую контентную модель, потому что потребители получают набор параметров только из поступающих данных, а неизвестные параметры игнорируются.
При этом регистрируется только схема данных производителя. Схема для потребителя генерируется по необходимости на основе схемы производителя, но не регистрируется. Единственным отличием схем производителя и потребителя становится использование разных контентных моделей. Это сводит проверку совместимости схем к конвертации схемы потребителя из открытой контентной модели в закрытую и проверке, что такая схема зарегистрирована для производителя. Если такая схема зарегистрирована, проверка на совместимость пройдена. В то же время схема потребителя может содержать дополнительные параметры из других зарегистрированных схем производителя. Для поддержки полной транзитивной совместимости требуется, чтобы схема потребителя была совместима хотя бы с одной зарегистрированной схемой производителя. Подробнее о теоретическом обосновании
Schema Registry поддерживает проверку совместимости схем через конвертацию открытой схемы потребителя в закрытую. Для этого задайте в пространстве имен для JSON-схем политику проверки совместимости Optional-friendly
. Вы можете задать политику для нового пространства имен или изменить ее для существующего пространства. Чтобы политика Optional-friendly
работала корректно, схемы в пространстве имен должны создаваться по закрытой контентной модели.