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

Libcast v4 - это полнофункциональное веб-приложение на базе старой школы symfony1. На стороне интерфейса наиболее распространенным методом, предоставляемым фреймворком для идентификаторов, является создание ярлыков.

В нашем случае они вызваны вялым поведением Propel. Основное преимущество - это оптимизированные для SEO URL.

На стороне сервера база данных PostgreSQL обеспечивает уникальность идентификатора (с автоматическим приращением 32-битного целого числа).

Наша видеоплатформа «Libcast v5» будет представлять собой RESTful API с данными из источников событий, совместно используемыми несколькими внутренними службами. Какое представление идентификатора мы можем выставить?

Требования

Чтобы определить требования, мы должны различать использование идентификаторов.

Все случаи:

  • ДОЛЖЕН быть уникальным в экосистеме Libcast
  • ДОЛЖЕН быть неизменным в течение всего жизненного цикла (и за его пределами) объекта.
  • ДОЛЖЕН вписываться в распределенную среду

Для потребителей API (внешних разработчиков):

  • ДОЛЖЕН быть относительно компактным, но ограничивать риск столкновения для беспрепятственного взаимодействия с разработчиком.
  • ДОЛЖЕН помочь разработчикам быстро определить тип ресурса, чтобы уменьшить злоупотребление.
  • НЕ СЛЕДУЕТ сливать внутреннюю техническую информацию в целях безопасности.

Для производителей / потребителей событий (внутренняя экосистема услуг)

  • ДОЛЖЕН помочь разработчикам быстро идентифицировать ресурс для целей отладки.

Для реляционных баз данных (внутренние)

  • ДОЛЖЕН быть эффективным

Выбор правильного представления в нашем API

Как дела у других?

Давайте посмотрим на представления идентификаторов в некоторых API, с которыми мы встречались.

Идентификаторы Deliveroo - это целые числа (1337). Их легко проверять, сортировать и манипулировать.

Если мы предположим, что внутри используются одни и те же идентификаторы, это идеально подходит для разработчиков, обслуживающих сервис, но это техническая утечка, которая может указывать на угрозу безопасности (обнаружение ресурсов).

Идентификаторы Trello - это хеши (592f11060f95a3d3d46a987a).

Они компактны и имеют одинаковую длину.

Идентификаторы Bitbucket - это UUID, разделенные знаками отличия ({c788b2da-b7a2–404c-9e26-d3f077557007}).

Их представление немного длинновато, но допускает целочисленное 128-битное преобразование.

Идентификаторы Stripe состоят из префикса и компактного хэша (cus_BYolrDrn30AmqI).

Префикс помогает интегратору API определить тип ресурса, уменьшая вероятность путаницы.

Libcast v5?

Как мы видели в этих примерах, вариантов представления множество, и каждый из них имеет свои преимущества и недостатки.

Нам нравится, как Stripe создает свои идентификаторы, потому что мы думаем, что это помогает разработчикам при интеграции API.

Генерация сервера или клиента?

Важно решить, кто будет отвечать за создание идентификаторов.

Давайте изучим разные решения.

Централизованная архитектура

Эмпирический способ состоит в том, чтобы задействовать централизованную реляционную базу данных.

Он предоставляет уникальные идентификаторы с защитой транзакций с автоматически увеличивающимся целым числом.

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

Распределенная архитектура

При таком подходе мы не можем использовать стратегию автоматического увеличения, потому что вероятность столкновения очевидна.

Формат UUID довольно хорошо решает эти проблемы благодаря 128-битному целочисленному кодированию и множеству стратегий генерации, несмотря на крошечные шансы столкновения.

Его представление можно уменьшить для более компактного отображения.

Давай сделаем это?

Благодаря этому сильному обучению мы можем создать наше представительство:

  • Наша архитектура v5 разделена на несколько сервисов, взаимодействующих с обменом событиями.
  • Каждую службу можно реплицировать для обеспечения масштабируемости.

Как вы уже догадались, мы применяем стратегии распределенной архитектуры. Мы также сохраняем идею добавления префиксов к нашим идентификаторам.

Это будет сделано в несколько шагов:

  • сопоставить каждый тип объекта с префиксом
  • сгенерируйте значение UUID и уменьшите его (это можно сделать за один шаг)
  • собрать оба, и вуаля!

user_1KASJZ7DWmusQjtvucYnoh

Интеграторы API довольны. (это наш приоритет №1 на данный момент).

Кому еще нужно удовлетворение?

А как насчет хранения идентификаторов?

Система обмена сообщениями (Kafka)

Выбрав шаблон Event Sourcing, мы выбрали Kafka в качестве основного источника истины. Мы можем воспроизвести некоторые или все наши события, особенно когда нам нужно увеличить масштаб. События также являются хорошей основой для устранения проблем, поскольку они действуют как функциональный журнал.

В событиях очевидно, что мы повторно используем то же представление, что и в нашем API.

Разработчики сопровождения в Libcast тоже довольны.

Следующий?

Магазин мероприятий

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

На данный момент мы храним наши идентификаторы в том виде, в каком они появляются в нашем API.

Проекции (PostgreSQL)

Мы используем базы данных запросов для хранения прогнозов наших агрегированных событий. В целях экономии места и обслуживания мы храним некоторые реляционные данные.

Мы хотим, чтобы ответы на запросы чтения были эффективными. Вот почему мы введем ограниченное использование JOIN (мы могли бы договориться о максимальном количестве 2) в качестве строгого правила.

Быстрое и грязное решение:

Сначала мы можем повторно использовать идентификаторы API в качестве индексированных первичных и внешних ключей varchar. Выполнение некоторых JOIN неоптимально, но может работать в течение некоторого времени.

Оптимизация (как в старые добрые времена)

Мы не хотим делать преждевременную оптимизацию, но можем придумать какие-то решения.

  • Решение 1: деконструируйте идентификатор и оставьте только декодированную числовую часть, чтобы получить 128-битное целое число. Мы считаем, что этот этап преобразования дешев по сравнению с увеличением производительности, которое мы получаем от JOINing на целых числах.
  • Решение 2: идентификатор можно рассматривать как внешний функциональный идентификатор, и мы добавляем в наши таблицы некоторые внутренние первичные и внешние ключи. Мы даже можем использовать меньшие автоматически увеличивающиеся целые числа, если нам не нужно больше. Фильтрация по внешнему идентификатору не будет быстрее, но количество СОЕДИНЕНИЙ должно значительно возрасти.

Вывод

Выбрать способ представления и хранения идентификаторов - нетривиальная задача.

Это может повлиять на опыт разработчика, производительность или отслеживаемость.

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

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

Каковы были ваши решения относительно идентификаторов и что побудило вас их выбрать?