EXPEDIA GROUP ТЕХНОЛОГИИ — ДАННЫЕ

Соображения безопасности при использовании перечислений в схемах Avro

Использование перечислений обычно является хорошей практикой, но это не всегда так при использовании Apache Avro.

Apache Avro обычно используется как в пакетных системах, так и в системах данных реального времени для описания расширяемых и защищаемых схем данных. Avro позволяет создавать системы данных, в которых типы данных можно изменять, не затрагивая производителей или потребителей. Тем не менее, это не без проблем. Одна конкретная болевая точка касается типа данных enum, который явно предлагает большую полезность для наборов известных значений с низкой кардинальностью, но может вызвать проблемы, если его не применять с должным вниманием.

К счастью, в более поздних версиях Avro появились функции для решения этих проблем. Трудности, с которыми вы можете столкнуться при работе с перечислениями, будут значительно различаться в зависимости от используемых вами версий Avro, скорости изменения символов в ваших перечислениях и количества потребителей, использующих схемы, содержащие эти перечисления. Во-первых, позвольте мне заявить, что это решаемая проблема, если вы везде используете Avro 1.9.0 или более позднюю версию.

В этой статье описываются основные проблемы, связанные с перечислениями, и способы их решения как в более ранних, так и в более поздних версиях Avro.

TL;DR

Если вам просто требуется краткое руководство по использованию enum без каких-либо объяснений — прокрутите эту статью до конца.

Проблема

Avro очень любят из-за особенностей совместимости схем. Однако оказывается, что использование типа enum может подорвать полезность этой функции. В качестве аналогии давайте сначала рассмотрим гибкость поля записи Avro. Как пользователь, я могу определить поле, и пока я предоставляю значение по умолчанию, я могу быть уверен, что:

  • Старые записи, не содержащие поля, будут доступны для чтения схемой, объявляющей поле — читатель увидит значение по умолчанию (т. е. обратная совместимость).
  • Старые читатели, использующие события, содержащие значение для нового поля, просто проигнорируют его (так называемая совместимость вперед)

Теперь перечисление немного похоже на запись — оно расширяемо — мы можем добавлять к нему символы так же, как мы можем добавлять поля к записи. Так что же происходит, когда мы это делаем? Предположим, мы добавляем в перечисление новый символ, который является типом поля в записи.

  • Старые записи, не содержащие новый символ, будут доступны для чтения схемой, содержащей новый символ — набор всех возможных символов в старых записях является подмножеством набора символов, определенного в последней схеме.
  • Читатели старшего возраста, использующие события, содержащие новый символ, не смогут — они раньше не видели этот символ и не знают, как с ним обращаться!

Расширение перечислений нарушает совместимость вперед. На практике это означает, что ваши потребители не смогут использовать какие-либо события, содержащие новый символ, пока их определение схемы не будет обновлено.

В худшем случае

Представьте, что вы — организация с тысячами схем. Вы решаете из соображений согласованности и включения функциональности на уровне платформы — включать общую запись заголовка в каждую отдельную запись, создаваемую в организации. Вы разрабатываете общий тип заголовка, который можно импортировать в схемы всех приложений данных в вашей организации. Возможно, вы включаете идентификатор трассировки, отметку времени и другие поля. Они, вероятно, бесполезны для приложений бизнес-потребителей (которые могут больше заботиться о полях, специфичных для предметной области), но имеют большую ценность для платформенных систем. Теперь представьте, что в этот заголовок вы включаете поле source, которое принимает одно из трех значений. Разумно вы кодируете это как enum.

Все это работает очень хорошо, пока вам не нужно добавить новый source. Это вводит новый символ в перечисление и требует выпуска общего типа заголовка и всех схем, которые импортируют этот тип (непростая задача). Когда мы начинаем производить события, содержащие новый символ, мы начинаем видеть проблемы. Приложения, которые не обновили свои схемы, начинают отклонять записи, содержащие новый символ, даже если поле, содержащее этот символ, никогда не используется приложением. Приложения не распознают символ и не могут выборочно десериализовать отдельные поля внутри записи — они должны распаковывать ее целиком, включая общий заголовок, даже если приложение его не использует.

Очевидно, что нам нужно обновить приложения, чтобы они использовали последнюю схему, тогда они поймут новый символ и смогут десериализовать события. Итак, какой набор приложений нам нужно обновить? Он включает в себя любое приложение, которое использует записи, содержащие общий заголовок, и наша цель состояла в том, чтобы включить заголовок в каждую запись. Короче говоря, это каждый потребитель в организации — это множество приложений!

Как более поздние версии Avro решают проблему

Avro добавила идею символа default в 1.9.0. Это очень похоже на значение поля default. Он предоставляет читателю запасной символ, который он может использовать, если встретит символ, который не распознает. Это дополнение к значениям поля по умолчанию. С помощью этого примитива мы получаем все необходимое для достижения прямой совместимости с расширяемыми перечислениями. Использование Avro 1.9.0+ и добавление символов default устраняет проблемы, связанные с расширением перечислений и совместимостью вперед. Рассмотрим пример ниже:

{
  "type": "record",
  "name": "MyRecord",
  "fields": [
    {
      "name": "my_field",
      "type": {
        "type": "enum",
        "name": "MyEnum",
        "symbols": [
          "a",
          "b",
          "Unknown"
        ],
        /* 
         * Symbol default - for forwards compatibility - 
         * new in Avro 1.9.0
         */
         "default": "Unknown"  
      },
      /*
       * Field default - for handle backwards compatibility
       */ 
       "default": "Unknown"    
    },

Здесь мы указываем два значения по умолчанию:

  • Поле default — сообщает читателю, что нужно использовать значение по умолчанию, если поле отсутствует в записи. Также сообщает старшим читателям, что они могут пропустить это поле, если оно отсутствует в их схеме, но присутствует в событии.
  • Начиная с версии 1.9.0: символ default — информирует читателя о необходимости использовать символ по умолчанию, если он прочитал символ, который не узнает.

Обратите внимание, что символ по умолчанию может быть добавлен задним числом, а также допускается (но игнорируется) более ранними версиями Avro. Поэтому в тех случаях, когда нам действительно нужно использовать перечисления, мы должны принять практику добавления символов по умолчанию в наши схемы, чтобы они стали полезными, когда наши системы сходятся на Avro 1.9.0+.

Руководство

Если вы не можете использовать Avro 1.9.0+ во всех своих конвейерах, вот несколько рекомендаций о том, как и когда использовать перечисления в схемах Avro, а также некоторые альтернативы.

Когда следует избегать перечислений

  • Вы создаете широко используемый общий тип — вместо этого используйте string.
  • Вы не знаете полного набора потребителей, которым нужно будет прочитать ваш тип — вместо этого используйте string.

Когда вы можете использовать перечисления

  • У вас есть полный контроль над всеми вашими потребителями и вы можете при необходимости управлять обновлением их схем.
  • Вы уверены, что ваш тип будет только когда-либо касаться систем, использующих Avro 1.9.0+, и понимать функцию символов по умолчанию.
  • Вы не уверены, какие версии Avro используются, но уверены, что ваше перечисление имеет фиксированную кардинальность и никогда не будет добавлено новых символов.
  • Вы не уверены, какие версии Avro используются, но уверены, что вам никогда не потребуется прямая совместимость (например, передача данных только в пакет).

Если вы нашли эту статью полезной или используете Apache Avro в своих проектах, обратите внимание на две предстоящие статьи, в которых описывается, как справляться с неизбежными ситуациями, когда вы хотите несовместимым образом развивать свои схемы, и часто задаваемые вопросы Avro.

Узнайте больше о технологиях в Expedia Group