В конце января OpenAI порадовала статьёй про то, как у них устроен backend, вот её перевод на русский от @CoolJuice. Хороший материал для размышлений на тему system design, и, в частности, для поиска ответов на вопросы:

Стоит ли идти таким путём начинающему стартапу?

Спойлер

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

Стоило ли OpenAI так делать?

Спойлер

Всё сложно. Скорее нет, чем да.


О подходе

При анализе архитектуры системы следующие факторы являются ключевыми:

  • Нефункциональные требования (NFR)

  • Имеющиеся ресурсы (бюджет, команда, сроки)

Почему так? Одну и ту же функциональность можно реализовать по-разному, и выбор реализации будет зависеть как раз от этих факторов. Например, если нужна очень высокая производительность для записи событий, то можно смотреть в сторону Kafka, Redpanda, Pulsar. Если же требования к производительности умеренные, то можно события в том же PostgreSQL хранить. Возможен вариант, когда команда не имеет опыта с нужными технологиями, а сроки поджимают -- в этом случае недостаток производительности компенсируется "железом".

Посмотрим же на нефункциональные требования.


CAP. Но не теорема

Полный список non-functional requirement весьма внушителен, с чего начинать? С тройки Consistency, Availability, Partition tolerance Performance. Откуда это следует, почему consistency впереди? Потому что это требование оказывает сильное влияние на выполнение требований как по availability, так и по performance.

Рассмотрим на примере. Допустим, у нас в качестве СУБД работает Scylla/Cassandra и используется значение 3 для replication factor. Если для какой-то порции данных доступна только одна реплика, то система всё ещё может обрабатывать запросы с consistency level 1, а вот с consistency level 2 -- уже нет (наблюдаем влияние на availability). Что же касается performance, то запросы с consistency level 1 будут обрабатываться быстрее, чем запросы с consistency level 2.

Итак, consistency. Consistency определяет, насколько актуально состояние системы, наблюдаемое операцией чтения. В OpenAI типичная операция чтения актуального состояния системы не наблюдает, так как для операции чтения предназначены около 50 асинхронных реплик. Налицо eventual consistency (когда-то, в светлом будущем, читатель увидит то, что записал писатель), по крайней мере, для существенной части операций чтения.

Для некоторых операций записи допустимо не видеть изменений вообще, на это указывает "we... introduced lazy writes, where appropriate, to smooth traffic spikes". Lazy write при потере primary server означает потерю данных. Иными словами, потеря данных заложена в архитектуру, по крайней мере, для части операций.

Availability. С доступностью по типичному чтению всё хорошо -- с такими-то требованиями по consistency и количеством реплик чтения. С доступностью по записи и по чтению с высокой согласованностью ситуация сложнее.

OpenAI описывает ситуацию так:

To mitigate primary failures, we run the primary in High-Availability (HA) mode with a hot standby, a continuously synchronized replica that is always ready to take over serving traffic. If the primary goes down or needs to be taken offline for maintenance, we can quickly promote the standby to minimize downtime. The Azure PostgreSQL team has done significant work to ensure these failovers remain safe and reliable even under very high load.

Итак, если "in the fifth my server goes down", то в дело вступает запасной игрок. Как быстро это происходит? Recovery Time Objective для Azure PostgreSQL составляет 120 секунд -- речь про конфигурации Zone redundant high availability и Same zone high availability. Какое-то дополнительное влияние будет оказывать процесс cache warm-up, но степень этого влияния проанализировать не возьмусь, ибо OpenAI использует некий дополнительный caching layer.

Performance. Оригинальная статья даёт такой ориентир: "we’ve scaled PostgreSQL at OpenAI to support millions of queries per second". Этого недостаточно для оценки производительности, так как запросы могут быть разными по сложности, поэтому придётся довериться интуиции и пользовательскому опыту (я почти с самого начала "платный" пользователь ChatGPT). Видимо, речь про короткие транзакции с простыми операциями (INSERT, UPDATE, DELETE, SELECT по ключу/части), иными словами, перед нами типичная OLTP нагрузка.

Итак, что нам известно или угадано:

- Consistency target
  - Eventual consistency для большинства операций чтения
  - Возможна потеря данных для части операций записи
- Availability target: 99.999%
- Performance target:
  - QPS: millions
  - Load profile, educated guess:
    - В основном INSERT, UPDATE, DELETE, SELECT по ключу/части
    - 95%+ reads, <5% writes
  - p99 latency: low double-digit ms (~10-30ms)

Переходим к ресурсам.


Ресурсы

If you're not embarrassed by the first version of your product, you've launched too late

Reid Hoffman

Эта известная, среди участников стартапов, ма́ксима, содержит в себе весомое рациональное зерно. Если вы "умеете" в PostgreSQL и нужно срочно "пилить", при ограниченных ресурсах -- конечно "пилите"! Победителей не судят, некоторые косяки пользователи простят. Например, какое-то время я многократно наблюдал в ответах на свои вопросы некие хвосты ответов другим пользователям, и даже писал куда-то в поддержку OpenAI по этому поводу.

Тут, конечно, надо заметить, что "правило Хоффмана" по сути развивает более раннюю классическую мысль: "Premature optimization is the root of all evil" (Donald Knuth). То, что преждевременно для OpenAI, вовсе не преждевременно для продукта типа VictoriaMetrics.

А какие, собственно, ресурсы были у OpenAI? Согласно википедии:

In December 2015, OpenAI was founded as a not for profit organization by Sam Altman, Elon Musk, Ilya Sutskever, Greg Brockman, Trevor Blackwell, Vicki Cheung, Andrej Karpathy, Durk Kingma, John Schulman, Pamela Vagata, and Wojciech Zaremba, with Sam Altman and Elon Musk as the co-chairs. A total of $1 billion in capital was pledged by Sam Altman, Greg Brockman, Elon Musk, Reid Hoffman, Jessica Livingston, Peter Thiel, Amazon Web Services (AWS), and Infosys. However, the actual capital collected significantly lagged pledges. According to company disclosures, only $130 million had been received by 2019

Т.е. это не совсем типичный pre-seed раунд, когда инвестируют фаундеры, друзья, тёти или ангелы с бизнес-крылышками.

Давайте посмотрим, что получилось.


Текущая архитектура

Тут интересно не столько то, что получилось, сколько то, чем пожертвовали, по сравнению с "обычным" PostgreSQL-based решением. Цитаты из статьи:

  • Schema changes are restricted to existing tables

  • If a new feature requires additional tables, they must be in alternative sharded systems such as Azure CosmosDB rather than PostgreSQL

  • If joins are necessary, we learned to consider breaking down the query and move complex join logic to the application layer instead

  • Many of problematic queries are generated by Object-Relational Mapping frameworks (ORMs), so it's important to carefully review the SQL they produce and ensure it behaves as expected

  • Lazy writes (потенциальная потеря данных)

  • Cascading read replicas (требуется специальное сотрудничество с Azure PostgreSQL team)

  • Caching layer перед PostgreSQL

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


Оценка архитектуры

В моей новостной ленте в LinkedIn я довольно часто видел посты примерно такого содержания: "обычный PostgreSQL справляется с экстремальными нагрузками, а значит, не стоит спешить со сложными решениями". Автор перевода, @CoolJuice, формулирует эту мысль достаточно явно:

В качестве послесловия хочу отметить что опыт OpenAI наглядно демонстрирует, что даже в эпоху специализированных NoSQL решений, традиционные реляционные СУБД, такие как PostgreSQL могут ещё дать фору новомодным решениям и обеспечить приемлемый уровень доступности и что не всегда нужно спешить, и заниматься построением "космолетов".

С этим согласиться, конечно, нельзя. Ну то есть вот так -- взять и согласиться, без рассуждений. А рассуждения здесь следующие.

При заявленных нефункциональных требованиях и доступных ресурсах естественным выбором сегодня была бы распределённая NoSQL-СУБД вроде CosmosDB -- к чему OpenAI в итоге всё равно пришла. Такой выбор позволяет сосредоточиться на решении задач бизнеса, а не на постоянной оптимизации реляционной СУБД, большая часть возможностей которой в данном случае практически не используется.

Есть, впрочем, важный нюанс. То, что выглядит очевидным сейчас, было далеко не столь очевидно в 2015 году, когда OpenAI только была основана. Azure DocumentDB появилась лишь в 2014 году. Из того, с чем я работал, теоретически можно было бы выбрать Cassandra (2010), но, как обычно, задним умом все крепки.


Заключение

Решения, принятые в 2015 (?) году, были (возможно) рациональны в условиях того времени, доступных технологий и неопределённости будущего. Но это не делает их универсальной рекомендацией для систем, проектируемых сегодня. Использование PostgreSQL в данном случае приводит к длинному списку сознательных жертв.

Архитектура OpenAI -- это не пример "простого решения на PostgreSQL". Это пример того, во что превращается архитектура, когда PostgreSQL вынуждают работать в режимах, для которых он не предназначался.