Предел PostgreSQL: как кеш AI-тестов вырос до миллиарда записей и переехал на ClickHouse

от автора

Momentic использует AI, чтобы находить элементы веб-интерфейса по смыслу и выполнять пользовательские сценарии без хрупких ручных локаторов. Для ускорения регресса результаты работы модели кешируются, но с ростом продукта объём кеша увеличился с 80 тысяч до миллиарда записей. PostgreSQL перестал справляться. Разбираемся, как переход на ClickHouse помог масштабировать AI-тестирование до миллионов запросов в день.


Оглавление:

  1. Для чего это всё?

  2. Проблемы роста при использовании Postgres

  3. Решение перейти на ClickHouse

  4. Как мы оптимизировали архитектуру ClickHouse

  5. Замена UPDATE на INSERT при продлении TTL

  6. Миграция на ClickHouse

  7. Переключение на ClickHouse

  8. Результаты

Для чего это всё?

Для начала бизнес-контекст:

Иными словами, если мы хотим тестировать веб-приложение без собственного фреймворка, то передаём управление браузером Momentic CLI. Он читает тестовые сценарии из репозитория, при необходимости обращается к AI-агенту, создаёт и кеширует локаторы для каждого шага, а затем выполняет нужные действия и проверки в веб-приложении.

Проблемы роста при использовании Postgres

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

Изначально всё было просто

Мы хранили кеш в одной таблице Postgres. Однако недостатки такого подхода проявились довольно быстро. Из-за высокой нагрузки на чтение и запись мы столкнулись как с повышенным потреблением ресурсов, так и с конкуренцией за блокировки между запросами, которые одновременно читали и изменяли кеш. Когда количество записей выросло на несколько порядков, ситуация стала ещё хуже.

Старая система с Postgres.

Старая система с Postgres.

Решение перейти на ClickHouse

Мы решили перенести хранилище в ClickHouse, чтобы повысить производительность запросов поиска по кешу. По мере роста Momentic такие запросы стали выполняться 600 тысяч раз в день.

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

Чтобы понять причины этого решения, важно разобраться в устройстве данных кеша Momentic. Ключ кеша состоит из:

  • идентификатора теста;

  • идентификатора шага;

  • версии Momentic;

  • ветки Git;

  • времени коммита.

Для заданных идентификатора теста, версии Momentic, ветки Git и коммита существует практически фиксированное количество значений. Оно равно числу шагов теста и чаще всего составляет от 10 до 100.

Индексы Postgres основаны на B-деревьях, поэтому стоимость запроса неизбежно растёт вместе с объёмом данных. ClickHouse, напротив, использует разреженный первичный индекс. Если в каждом запросе нам известны четыре ключевых значения, мы можем очень эффективно сузить область поиска всего до нескольких гранул данных.

Как мы оптимизировали архитектуру ClickHouse

Выбор правильного первичного ключа

Правильно подобранный первичный ключ ClickHouse решил большую часть проблемы. В feature-ветках область поиска обычно удавалось быстро сузить до одной части данных, содержащей кеш нужного теста. После этого оставалось обработать в памяти от 10 до 30 тысяч строк.

Запросы на feature ветке

Запросы на feature ветке

С веткой main ситуация оказалась сложнее. Между двумя запусками конкретного теста могли появиться десятки или сотни коммитов, поэтому для текущего коммита кеша ещё не существовало. В таком случае Momentic искал последний предыдущий коммит, на котором этот тест действительно запускался, и пытался переиспользовать сохранённые локаторы.

Примерный код:

WHERE test_id = ?  AND commit_timestamp <= current_commit_timestampORDER BY commit_timestamp DESCLIMIT 1
Запросы на main ветке. До оптимизации

Запросы на main ветке. До оптимизации

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

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

Теперь поиск выполнялся в два этапа:

  1. В материализованном представлении находился последний коммит, для которого существовал кеш.

  2. Из основной таблицы по точному времени коммита извлекались сохранённые данные.

В результате чтение основной таблицы снова ограничивалось одной или двумя частями данных. При запуске теста Momentic применял найденные локаторы к текущему DOM. Если они больше не находили нужный элемент или элемент не соответствовал сохранённым признакам, система обращалась к AI и создавала новый кеш.

Итого

Итого

Замена UPDATE на INSERT при продлении TTL

При использовании Postgres для каждого запуска теста мы выполняли три запроса:

  1. SELECT — получить данные из кеша.

  2. UPDATE — продлить TTL использованных записей кеша; частота обновлений ограничивалась с помощью Redis.

  3. INSERT/UPDATE — сохранить обновлённые записи кеша.

Такой подход плохо сочетался с ClickHouse, поскольку потенциально два запроса из трёх были обновлениями, а ClickHouse выполняет их не особенно эффективно.

Вместо этого мы перешли исключительно на INSERT в сочетании с движком ClickHouse ReplacingMergeTree:

  1. Выполняем SELECT, чтобы получить записи кеша.

  2. Повторно вставляем использованные записи с помощью INSERT, тем самым продлевая их TTL.

  3. После запуска теста вставляем новые записи.

  4. ClickHouse асинхронно устраняет дубликаты.

Эффект оказался настолько значительным, что мы смогли полностью отказаться от слоя Redis. К тому моменту из-за возросшей кардинальности ключей кеша он и так приносил мало пользы.

Старая система на базе Postgres и Redis.

Старая система на базе Postgres и Redis.
Новая система на базе ClickHouse.

Новая система на базе ClickHouse.

Как мы мигрировали с Postgres на ClickHouse

Двойная запись

Чтобы во время миграции не потерять данные и проверить новый подход, сначала мы стали одновременно записывать кеш и в Postgres, и в ClickHouse.

Срок удаления устаревших записей кеша составлял 14 дней. Благодаря этому через две недели обе базы гарантированно содержали одинаковые значения.

Двойное чтение и проверка согласованности

Когда в обеих системах накопился одинаковый набор данных, мы начали проверять корректность и производительность запросов ClickHouse.

На этом этапе пользователи по-прежнему получали данные кеша из Postgres. Одновременно в фоновом режиме мы выполняли такой же запрос к ClickHouse, сравнивали результаты и отмечали все расхождения.

Это позволило нам:

  1. Убедиться, что обе базы возвращают согласованные результаты.

  2. Проверить производительность архитектуры ClickHouse и её способность работать в требуемом масштабе.

Именно на этом этапе мы смогли итеративно улучшить производительность, создав материализованное представление со временем коммитов.

Переключение на ClickHouse

Убедившись в надёжности конфигурации ClickHouse, мы начали постепенно переводить производственный трафик с Postgres на ClickHouse.

Двойную запись мы временно сохранили на случай, если потребуется откат. После первоначального периода проверки фоновая запись в Postgres была прекращена.

Новая система. Только ClickHouse.

Новая система. Только ClickHouse.

Результаты

Переход на ClickHouse позволил нам ежедневно выполнять более двух миллионов запросов к кешу и обрабатывать почти 20 миллиардов записей, сохраняя среднюю задержку разрешения около 250 мс.

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

Количество запросов к кешу ClickHouse в день.

Количество запросов к кешу ClickHouse в день.
Задержка разрешения кеша в зависимости от конфигурации клиента.

Задержка разрешения кеша в зависимости от конфигурации клиента.

🎯 Больше архитектурных разборов, активностей в виде архитектурный кат, System Design собеседований, встреч с экспертами индустрии на моём канале @system_design_world

ссылка на оригинал статьи https://habr.com/ru/articles/1066022/