Упростите и увеличьте масштаб. Тестируйте агрессивно. Измерьте все.

Команда и проект

Команда инженеров по обеспечению надежности сайта (SRE) в Sailthru сосредоточена на трех основных задачах:

1. Повышение стабильности системы
2. Уменьшение количества инцидентов и предупреждений
3. Улучшение мониторинга

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

ПЕРЕРАБОТКА ПОЧТОВОЙ СИСТЕМЫ

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

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

Мы успешно завершили этот годичный проект в срок (до массовых праздников), руководствуясь этими тремя руководящими принципами:

1. Упрощайте и масштабируйте
2. Настойчиво тестируйте
3. Измеряйте все

Восстановление почтовой системы

УПРОЩЕНИЕ И МАСШТАБИРОВАНИЕ

Существует два основных процесса персонализации содержимого электронной почты:

  1. Определите аудиторию - динамический список людей, которые получат письмо. Список формируется серией сложных запросов, выполняемых по всей клиентской базе каждого клиента. Например, маркетолог, желающий разослать новый купон на 20% скидку на покупку на 100 долларов в магазине в Лос-Анджелесе, может захотеть найти всех клиентов, которые: 1) открывались за последние 30 дней, 2) открывали большую часть своих покупок в Лос-Анджелес, 3) ничего не покупали в последнее время и 4) статистически вероятно потратят 100 долларов и более.
  2. Составьте и отправьте персонализированное сообщение - нужный контент нужному человеку в нужное время. Сообщения составляются на полном по Тьюрингу языке шаблонов, поэтому возможности для детального контроля практически безграничны: например, используя несколько операторов {if}… {else}, кампания, отправленная 1 миллиону человек, может отправить 1 миллион совершенно разных сообщений. , каждое сообщение персонализировано с содержанием, наиболее соответствующим интересам конкретного клиента и истории кликов.

Унаследованные системы в Sailthru, как правило, вращаются вокруг одного курсора MongoDB, где один процесс PHP перебирает результаты запроса и выполняет бизнес-логику на каждой итерации. Эта последовательная обработка имеет низкий потолок масштабирования, а также несет с собой некоторые серьезные проблемы с отказоустойчивостью: переключение базы данных при отказе может привести к перезапуску длительных процессов с самого начала.

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

Паттерн, который, как мы обнаружили, был очень успешным в отношении этого типа унаследованной архитектуры, имеет два направления: упрощение и масштабирование. Для упрощения сам запрос MongoDB сводится к чему-то, что будет быстро выполняться по известному индексу БД и будет извлекать только _ids из базы данных. Для горизонтального масштабирования эти идентификаторы _ids помещаются в распределенную очередь (мы используем ActiveMQ) и потребляются работниками без сохранения состояния. Затем рабочие выполняют findOne, чтобы получить весь документ базы данных. Имея под рукой полный документ, рабочие могут выполнять любую бизнес-логику для сопоставления и отправки сообщения.

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

ИСПЫТАТЬ АГРЕССИВНО

Как и многие стартапы, Sailthru начинала с языка сценариев с динамической типизацией (PHP) и теперь переводит новые сервисы на более масштабируемый язык (Java). Java предоставляет множество парадигм параллелизма, которые помогают гибко решать всевозможные проблемы, а ее способность жестко контролировать объединенные соединения с подчиненными ресурсами является огромной победой для нашей команды, занимающейся базами данных.

Перенос кода с динамически типизированных однопоточных языков на статически типизированные многопоточные языки сопряжен с невероятными проблемами. Этот == (оператор сравнения двойного равенства), который раньше просто работал в PHP, расширяется до дюжины строк кода Java. Беглый вызов strtotime превращается в головокружительную комбинацию регулярного выражения и Joda-Time. PHP имеет тенденцию игнорировать нулевое значение и молча терпеть неудачу, в то время как Java имеет тенденцию быть очень чувствительным, бросая NullPointerException каждый раз, когда что-то немного не так. Эти проблемы усугубляются документами MongoDB без схемы, которые могут содержать все типы данных, смешанные с одними и теми же полями.

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

  1. Разработчики написали обширные модульные тесты, используя 95% охват строки кода в качестве начальной метрики, чтобы знать, когда остановиться.
  2. Затем наша команда QA написала набор автоматического тестирования с 1800 тестовыми примерами, которые сравнили результаты PHP и Java на предмет любых расхождений.
  3. Наконец, мы провели тестирование в режиме «только чтение» в производственной среде с использованием реальных клиентских данных.

ИЗМЕРИТЬ ВСЕ

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

Для Java-сервисов, таких как переписанная система рассылки, нам нравится использовать фреймворк Dropwizard Metrics. Он закачивает метрики прямо в Graphite, а затем мы используем инструмент Панель управления Grafana в качестве интерфейса для мониторинга того, что происходит в реальном времени. Нам нравится измерять все внешние зависимости, такие как различные базы данных, кеш и многие другие, чтобы мы могли предвидеть потенциальные узкие места в системе, а затем устранять их.

Часто задают вопрос о том, как оповещать на основе метрик Graphite. Мы используем простое решение PHP-скриптов, которые читают веб-API Graphite JSON, просматривают результаты и затем выполняют простую статистику, чтобы определить, превышен ли порог. Наконец, автоматизированный процесс вызовет предупреждение через PagerDuty API.

Измерение производительности в совокупности очень полезно, но иногда мы хотим погрузиться в детали того, какой именно код работает медленно. Чтобы решить эту проблему, мы используем Графики пламени. Этот инструмент визуального профилирования помогает с первого взгляда определить, какие методы использует процессор. Это помогло нам выявить довольно много узких мест в стандартной библиотеке Java, а также в нашем собственном коде.

Подобные изменения определенно являются микрооптимизациями, но они, как правило, накапливаются в стеке, поэтому с системного уровня мы максимально умны с нашими ресурсами и выполнением кода. Мы вернемся к графику пламени после внесения изменений, затем измерим и сравним производительность или повышение эффективности.

Вывод

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

В приведенном выше примере показана разница в производительности для кампании электронной почты среднего размера для ~ 500 тыс. Человек: в устаревшей системе потребовалось около 24 минут для создания и завершения отправки, но в системе с измененной архитектурой вся кампания завершается в течение 2 минут. Как отмечалось ранее, большая разница заключается в том, что новая система не блокируется на этапе запроса (также известного как «генерация»), поэтому отправка происходит параллельно с теми _id, которые извлекаются из базы данных.

Большинство других конкурирующих систем электронной почты, которые мы видели, требуют длительной предварительной генерации перед отправкой, как это когда-то делала наша старая система. Если маркетинговая команда хочет, чтобы кампания для 2 миллионов клиентов начала рассылаться к 10 утра, им часто придется все настраивать и готовить к 8 утра или раньше. Это серьезно ограничивает гибкость бизнеса, поскольку издатели не могут реагировать на последние новости, а компании электронной коммерции не могут адаптироваться к быстрым изменениям в их ассортименте продукции.

Напротив, новая система рассылки Sailthru имеет два больших преимущества:

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

Те же руководящие принципы, которые мы использовали для реорганизации почтовой системы, теперь также используются для ускорения подсистемы ночной агрегации в 10 раз. В дальнейшем команда планирует применить те же 3 принципа к другим подсистемам, которые больше всего нуждаются в повышении производительности и масштабируемости.

Роб Уильямс - старший инженер SRE в Sailthru. В его основные обязанности входит улучшение производительности / надежности существующего унаследованного кода, внедрение новых высокопроизводительных распределенных подсистем, которые заменяют старые, чтобы обеспечить большую масштабируемость системы, реагирование на инциденты при исполнении служебных обязанностей и исследование основных причин любых производственных проблем в сети. полный стек.