Мы перенесли 6,5 миллиардов строк данных Gauges из MongoDB в ClickHouse.
В начале этого года мы приобрели Gauges, простую аналитическую платформу. Мы только что закончили перенос 6,5 миллиардов строк исторических аналитических данных из базы данных MongoDB в ClickHouse — ту же базу данных, на которой работает Fathom. Я расскажу о том, как это происходило.
Новые клиенты и старые обещания
Когда мы приобрели Gauges, мы понятия не имели, сколько клиентов захотят перейти на Fathom. Мы знали, что предложим каждому путь развития вместе с Fathom и поможем с миграцией их данных. Многие клиенты связались с нами, выразив энтузиазм по поводу нового дома для их данных.
Затем мы дали два важных обещания. Во-первых, клиентам Gauges не нужно будет заменять код отслеживания на своих сайтах. Мы перенаправим существующие конечные точки приема данных Gauges в Fathom.
Во-вторых, мы провели переговоры с корпоративным клиентом, переходящим из Gauges. Мы пообещали, что им не придется переписывать какой-либо код API, который они использовали в рамках этой миграции.
Это означало, что нам нужно было перенести все исторические данные, сохранив при этом работу действующего продукта и его публичных интерфейсов в прежнем виде. По сути, две задачи: заменить приложение и перенести данные.
Кто не любит хороший рефакторинг?
Мы унаследовали приложение на Ruby on Rails, работающее на AWS. Оно использовало Elastic Beanstalk, EC2, SQS, ElastiCache, Application Load Balancer, VPC, CloudWatch, S3 и CloudFront. Инфраструктура обходилась в тысячи долларов каждый месяц.
Я хорошо знаком со всеми этими сервисами AWS и уверен, что мог бы модифицировать существующее приложение, но я не работал с Ruby с 15 лет. Я не хотел, чтобы наш долгосрочный план зависел от того, что я буду развертывать изменения на языке, который наша компания не знает глубоко.
Поэтому я переписал приложение на Laravel, разделив его на два подпроекта:
- Слой преобразования данных Gauges Ingest в формат приема Fathom
- Слой публичного API для корпоративного клиента
Я изучил API, конечные точки приема данных и базовую структуру данных. Новое приложение должно было обрабатывать точно такие же запросы и возвращать точно такую же структуру данных.
Потратив некоторое время на создание, тестирование и проверку работоспособности, мы подготовили приложение к запуску в продакшн. Однако с данными все было гораздо сложнее.
Поиск данных
Gauges был создан в 2011 году, за годы до появления ClickHouse, и хранил свою аналитику в MongoDB.
Я никогда в жизни не подключался к базе данных MongoDB. В молодости MongoDB была мемом, и я всю карьеру избегал её. Да, я знаю, что сейчас это очень успешная многомиллиардная компания-разработчик баз данных.
Я нашел экземпляр EC2, на котором работала MongoDB, добавил свой IP-адрес в брандмауэр и подключился со своей машины. База данных содержала 4705 коллекций и почти 7 миллиардов документов.
Данные были разделены на коллекции, названные по типу контента, году и месяцу. Исторические коллекции были неизменяемыми, что является моим любимым типом аналитических данных.
Но MongoDB — это документоориентированная база данных. Я не мог написать привычный SQL-запрос и начать переносить строки в ClickHouse. Мне нужен был способ безопасно читать их частями из нашего рабочего приложения Fathom.
Четыре часа от дедлайна до плана
Чтобы не переусердствовать с анализом проблемы, я написал клиентам Gauges, что начну миграцию их аккаунтов на следующей неделе.
Нет, у меня не было готового плана.
Я обнаружил, что наличие дедлайна заставляет меня двигаться быстрее. Менее чем через четыре часа после отправки письма у меня был ответ.
Версия MongoDB была устаревшей, поэтому наше современное PHP-приложение не могло к ней подключиться. Однако я мог создать сервер Laravel Forge с совместимым бинарным файлом MongoDB. Этот сервер мог общаться с базой данных, а наше рабочее приложение могло общаться с этим сервером.
Я превратил сервер Forge в небольшой частный API для миграции с доступом только для чтения. Fathom запрашивал бы у него данные; он читал бы их из MongoDB; а ответ возвращался бы в наш существующий код миграции.
Это позволило мне сохранить всю логику сопоставления аккаунтов, конфигурацию ClickHouse и состояние миграции внутри Fathom. У сервера Forge была одна задача: предоставить узкий интерфейс к старой базе данных.
Создание API для миграции
Я изучил кодовую базу Gauges и сопоставил её коллекции с конечными точками. Затем я создал прототип в нашей локальной панели администратора, который читал данные Gauges через API миграции.
Это сработало.
Обозреватель показывал сайты Gauges в аккаунте, доступные месяцы для каждого сайта и метрики в каждой коллекции. Мы могли просматривать защищенные данные MongoDB, не подключая наше основное приложение напрямую к MongoDB.
Теперь мы могли приступить к самой миграции.
Много шума из-за маппинга
Некоторые клиенты Gauges уже создали сайты в Fathom и установили наш код отслеживания. Другие все еще использовали оригинальный сниппет Gauges. Нам нужно было поддерживать и тех, и других.
Для 99,9% сайтов мы могли воссоздать их в Fathom и сопоставить с ID сайта Gauges. Для нескольких аккаунтов, которые уже создали свои сайты в Fathom, нам пришлось выполнять ручные переопределения.
Gauges хранил историю в виде разбивок по таким измерениям, как страницы, рефереры, браузеры и местоположения. Это были не те детальные данные о событиях, которые собирает Fathom, но они были похожи на агрегированные данные Google Analytics, которые мы уже импортируем.
Это означало, что мы могли сохранить полезную историю в импортированных аналитических таблицах Fathom. Клиенты видели бы свои исторические итоги и разбивки вместе с новыми данными Fathom.
Поиск узких мест
Настроив маппинг, я создал механизм миграции. Я начал осторожно, с низкой параллельностью, потому что не хотел перегружать базу данных, которая тихо работала годами.
Миграция шла медленно, поэтому я начал профилировать каждую часть. Я предполагал, что старый сервер MongoDB станет первым узким местом. Но это было не так.
Сервер Forge, на котором работал API миграции, имел только один vCPU и был загружен на 100%. Я создал его несколько месяцев назад и забыл, насколько он слабый. Я обновил его до сервера с оптимизированным процессором с 16 vCPU, и миграция полетела.
Тем временем старый экземпляр MongoDB едва замечал происходящее. Его загрузка процессора оставалась на уровне примерно 12–14% на протяжении большей части миграции.
Затем узким местом стал мой MacBook Air.
Я запускал ClickHouse локально для тестирования. Как только я увеличил количество воркеров миграции, локальный сервер ClickHouse начал потреблять всю память на моем MacBook.
У меня на столе стоял Mac mini на M4, но его настройка заняла бы время, которого у меня не было. ClickHouse Cloud был еще одним вариантом, хотя использование его для этого теста добавило бы сетевую задержку.
К этому моменту я провел и проверил достаточно миграций, чтобы доверять коду. Худший сценарий был управляемым: удалить импортированные сайты и данные, исправить проблему и запустить снова. Я перенес механизм миграции в продакшн.
Локальный успех, таймаут в продакшне
В продакшене мы столкнулись с другой проблемой. Начальная синхронизация сайта, которая копировала все сайты Gauges в Fathom, на моем компьютере завершалась быстро, но после развертывания превышала 10-минутный лимит ожидания AWS Lambda.
Сетевая задержка между производственными сервисами превратила тысячи мелких запросов в реальную задержку. Я занимаюсь разработкой программного обеспечения почти 20 лет, поэтому знаю, что локальная среда и продакшен ведут себя по-разному. Тем не менее, команды, которые я запускал локально, чтобы «сэкономить время», стоили мне целого дня, когда мы были готовы к запуску.
Как только это было исправлено, я синхронизировал первый крупный аккаунт и наблюдал, как сайты появляются в Fathom. История перемещалась. Теперь мне нужно было доказать, что API совместимости работает корректно.
Доказательство
Я создал инструмент для сравнения в реальном времени. Он отправлял один и тот же запрос к нашему API совместимости и к работающему API Gauges, используя один и тот же партнерский токен, а затем сравнивал два ответа бок о бок.
^ На скриншоте написано fathom.test для нашего API, но это остатки старого кода, это происходило в продакшене.
Итоговые значения за все время никогда не совпадали точно, и они никогда не должны были совпадать. «Живой» API продолжал собирать трафик после нашей фиксированной отсечки миграции, поэтому он оставался немного впереди нас. Как только я учел это, все остальные поля совпали.
Я тестировал детализированные данные отдельно, запуская импорт и проверяя их вручную. Мы знали, как Gauges преобразовывал и хранил данные, так что это было легко, и нам просто нужно было сопоставить данные на выборке сайтов, чтобы быть уверенными. Затем мы смогли бы суммировать все это в конце.
Слишком много кнопок
Я создавал и тестировал эту миграцию так много раз. В процессе тестирования я добавлял экраны отладки, менял команды, переписывал контрольный список развертывания и создавал несколько способов запуска каждого этапа. В конце концов, у меня начинала болеть голова, когда я смотрел на это. Обычно это признак того, что система стала слишком сложной.
Я удалил лишние элементы управления и создал единую страницу миграции. Каждый шаг должен был успешно завершиться, прежде чем появлялась следующая кнопка. Ничего не запускалось автоматически, и была предусмотрена кнопка экстренной остановки, если я видел что-то неладное.
Экстренная остановка на том скриншоте не прижилась. Каждое задание в очереди считывало эту строку перед выполнением любого действия, что превращало примерно 50 000 заданий в очередь из одного потока, где каждое задание ждало в среднем 504 мс только для того, чтобы получить разрешение на запуск. Мой предохранительный клапан стал «бутылочным горлышком», поэтому я его убрал.
Более простой интерфейс имел значение, потому что синхронизация переключения должна была быть запущена вскоре после того, как мы перенесли прием данных и API совместимости в Fathom. Я мог видеть точное состояние каждого аккаунта, не запоминая список команд.
Мой мозг наконец-то успокоился. Мы были готовы.
Великое переключение 2026 года
Перед днем миграции мы снизили DNS TTL для track.gaug.es и secure.gaug.es до 60 секунд.
Мы перенесли конечную точку API в CloudFront и обеспечили ее поддержку нашим API совместимости. Мы перенесли конечную точку отслеживания в Bunny, которая направляла запросы в среду приема данных Fathom.
Для приема данных потребовался другой подход. У клиентов был установлен старый скрипт Gauges на тысячах веб-сайтов, и мы не собирались просить их менять его.
Мы написали новый скрипт Gauges без использования файлов cookie, который поддерживал существующий API браузера. Мы также добавили промежуточное ПО в приложение приема данных Fathom, которое понимало запросы как от оригинального скрипта, так и от нашей замены.
Результат был тем, что волновало меня больше всего. Существующий код отслеживания продолжал собирать данные без необходимости посещения сайта пользователями, а интеграции по-прежнему вызывали API Gauges и получали ожидаемые ответы. Мы перестроили конечные точки, которые действительно использовались; остальные теперь указывают на страницу завершения поддержки.
Затем я сидел и наблюдал, как поступают новые события. Посещение сайта, отслеживаемого Gauges, требовало обновления чисел в ответе API совместимости и на панели управления Fathom. Как только оба показателя увеличились одновременно, я понял, что скрипт отслеживания, прием данных и API — все они подключены к одному месту.
Финальный импорт данных
Как только сайты оказались в Fathom, API совместимости стал стабильным, а новый трафик начал поступать в нашу систему приема данных, мы импортировали оставшиеся исторические данные.
Это включало страницы, браузеры, источники переходов, местоположения и другие параметры, хранящиеся в Gauges. Gauges хранил детализацию по дням, и мы могли бы импортировать все это, но свертывание данных до уровня месяца сократило количество запросов примерно в 30 раз. Когда вы просматриваете историю за годы, вы на самом деле смотрите на месячные итоги, поэтому мы пошли на такой компромисс.
Это заняло много времени. Было много сайтов, много коллекций и 6,5 миллиардов строк для обработки. Но панель управления миграцией позволила мне переходить по одному проверенному этапу за раз, пока каждый аккаунт не был завершен.
И это было все. Мы перенесли 15 лет истории аналитики из MongoDB в Fathom, заменили приложение Gauges, сохранили его API и интерфейсы отслеживания, а также вывели из эксплуатации унаследованную инфраструктуру, что позволило сэкономить пятизначную сумму на ежегодных расходах на хостинг.
Я официально закончил с миграциями баз данных… на данный момент.
Джек Эллис, основатель
Откройте для себя опыт Джека Эллиса, технического писателя, преподавателя и инженера-программиста с богатым опытом в создании программного обеспечения.
