Особенности построения saas-сервиса на Symfony

Хорошее видео с не очень хорошим звуком. Сначала посмотрите.

RetailCRM - специализированная crm-система для электронной коммерции.

2500 тысячи подключенных магазинов, 10 миллионов товаров, 25 миллионов SKU (на момент презентации).

Используемые технологии и инструменты:

  • Symfony2
  • PHP7
  • Go
  • PostgreSQL
  • Redis
  • Memcache
  • Beanstalkd
  • Deployer
  • gossha

При реализации любого SaaS-проекта возникает вопрос хранения пользовательских аккаунтов. Существует два основных подхода:

  • Храним все аккаунты в одной большой базе данных
  • Храним каждый аккаунт в отдельной базе данных
  • Вариации первых двух методов

Аккаунты в RetailCRM не связаны и не взаимодействуют друг с другом - выбрали второй вариант.

Первая задача, которая возникает какой бы фреймоврк мы не использовали - научить наше приложение динамически подключаться к разным базам данных в зависимости от текущего аккаунта. Это касается как веб-части приложения, так и консольных команд. Если проект реализуется на Yii 2.x, один из вариантов такой реализации предложил Дмитрий Елисеев. Ребята из RetailCRM используют Symfony и попробовали несколько вариантов реализации.

1-ый метод AppKernel::registerContainerConfiguration

Достоинства:

  • Простая реализация

Недостатки:

  • Для каждого аккаунта свой контейнер
  • Разбухание кеша если аккаунтов много
  • Проблема прогрева кеша

2-ой метод Единственный контейнер с динамическими параметрами. Для этого варианта пришлось реализовать свой PhpDumper. Хотя есть похожие готовые решения: Использование Expression Language или DynamicParametrsBundle

Преимущества:

  • Фиксированное время деплоя
  • Не забивается OpCache

Проблема с динамическим выбором базы данных решена. Однако, по мере роста проекта, все создаваемые базы данных уже не умещаются на одном сервере. Возникает необходимость в добавление новых серверов. Теперь наше приложение должно знать не только название базы данных, но и сервер, на котором она находится. Для хранения подобного “маппинга” используется простой php-файл вида:


<?php

return [

‘Account_1’ => [

‘H’ => ‘dbserv_1’, //host name

‘D’ => ‘db_1’ //databse name

]

……….

];

Разработан сервис, отвечающий за регистрацию новых аккаунтов и управления ими. В момент регистрации сервис определяет на каком из серверов создать базу данных для нового клиента. После чего делает запись в “файл-маппинг” и раскладывает новую версию файла по всем application-серверам. Используется именно статичный маппинг, а не хеш-функция для определения сервера баз данных. Причин несколько:

  • Аккаунты очень неравномерны по объему данных и по нагрузке
  • Разные сервера могут содержать разное количество аккаунтов
  • Имеется возможность переноса аккаунта между серверами

С ростом количества баз данных на одном сервере возникает новая проблема - лимиты на количество одновременных соединений с PostgreSQL-сервером. Задача решается подключением PgBouncer и переходом к схеме, когда для каждого нового аккаунта создается не отдельная база данных, а отдельная схема. На каждом СУБД-сервере разворачивается несколько баз данных, каждая из которых содержит по 30 схем для аккаунтов.

Следующая проблема - обновление проекта и накат миграций (deploy). Поскольку баз данных много - накат новых миграций может занять продолжительное время. Может возникнуть ситуация, когда код приложения уже обновился, но миграции “раскатились” еще не по всем базам данных. Т.е. произойдет рассогласованность версий кода и баз данных. Решение довольно простое - каждый аккаунт работает со своей версией кода. Создается символическая ссылка, которая указывает на определенную версию. Deployer последовательно переключает символьную ссылку для каждого аккаунта и накатывает миграции. Таким образом, каждый аккаунт становится независимым и по коду, и по базе данных.

Итого. Минусы.

  • Приложение должно уметь работать с несколькими базами данных
  • Необходимо создавать базу под каждого нового клиента
  • Отдельный VACUUM для каждой базы данных
  • Очень много одновременных коннектов к СУБД-серверу
  • Сложно делать сквозные отчеты

Итого. Плюсы.

  • Шардинг баз данных без дополнительных ухищрений
  • Перенос аккаунтов между серверами
  • Восстановление любого аккаунта из бекапа
  • Удаление мертвых аккаунтов - просто удаляем БД
  • Плавный накат миграций
  • Разные аккаунты на разных версиях приложения