Это третья часть цикла «Масштабирование Wix до 100 миллионов пользователей». Вступление и второй пост.

Wix начинался с одного сервера, который обеспечивал весь функционал: регистрацию пользователей, редактирование веб-сайтов, обслуживание опубликованных веб-сайтов и загрузку изображений. В свое время этот единственный сервер был правильным решением, поскольку он позволил нам быстро расти и применять гибкие методы разработки. Однако к 2008 году начались периодически повторяющиеся проблемы с развертыванием, которые вели к незапланированным простоям как в создании новых сайтов, так и в обслуживании уже созданных.

Развертывание новой версии нашей системы в некоторых случаях требовало изменения схемы MySQL. Поскольку Hibernate не прощает несовпадений между ожидаемой им схемой и реальной схемой базы данных (БД), мы использовали общую практику развертывания программного обеспечения: плановая двухчасовая остановка в период наименьшего трафика (полночь в США на выходных). За время этой плановой остановки мы должны были остановить сервис, выключить сервер, внести изменения в схему MySQL, развернуть новую версию и перезапустить сервер.

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

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

А потом нас осенило


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

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

Технологический стек, выбранный для выстраивания публичного сегмента, был намеренно простым. Мы больше не использовали Hibernate, мы отказались от любых форм кэша, и мы начали использовать Spring MVC 3.0. Важным принципом проектирования было сделать сегменты не связанными друг с другом в терминах программного обеспечения, цикла релизов и хранения данных, а также сделать программный стек простым для понимания и оптимизированным для обслуживания сайтов.

Явным признаком такой несвязанности был процесс публикации (потомки которого по-прежнему присутствуют в ядре Wix), который копировал данные из БД редакторского сегмента в БД публичного сегмента. В ходе этого процесса структуры данных трансформировались от представления, эффективного для редактирования, в представление, которое наилучшим образом подходит для опубликованного сайта.



В результате развертывания публичного сегмента стали редкими и низкорискованными. Он по-прежнему функционирует в системе Wix, спустя шесть лет после первого развертывания (хотя кое-что изменилось с тех пор).

Чему мы научились


Мы уже понимали, что цикл релиза связан с риском, но теперь осознали, что две наши ключевые бизнес-функции – строительство и обслуживание сайтов – по-разному подвержены риску. Мы поняли, что нам нужно обеспечить разные уровни сервиса для этих функций и что именно вокруг них мы должны выстраивать нашу систему. Что это за разные уровни сервиса? Мы рассматривали следующие аспекты: доступность, производительность, рискованность изменений и время восстановления после сбоя. Публичный сегмент, который затрагивает всех пользователей и все сайты Wix, должен иметь наивысший уровень сервиса по этим аспектам. Но в редакторском сегменте сбой затрагивает только пользователей, непосредственно занятых созданием сайта, поэтому последствия для бизнеса менее значительные. Поэтому мы несколько поступились высоким уровнем сервиса ради большей гибкости, что позволило снизить усилия, необходимые для разработки.

Главный архитектор программного обеспечения конструктора сайтов Wix,
Йоав Абрахами
Оригинал статьи: блог инженеров компании Wix

Комментарии (11)


  1. youROCK
    20.04.2016 17:53
    +4

    > мы использовали общую практику развертывания программного обеспечения: плановая двухчасовая остановка в период наименьшего трафика
    Вам очень повезло, что у вас вообще есть минимум трафика. К примеру, у нас «минимум трафика» представляет из себя трафик в 2 раза меньше, чем в пике. То есть, планово останавливать сервис на 2 часа нельзя никогда.


    1. batment
      20.04.2016 19:51

      Бывают ли у вас изменения схемы данных? Если да, то как вы их внедряете?


      1. youROCK
        20.04.2016 21:31
        +1

        Данные пользователей у нас расшардены по маленьким (1-10к пользователей) табличкам. Альтерятся они прямо «на живую», и каждый альтер идет несколько секунд. Код, соответственно, заранее подготавливается к тому, чтобы уметь работать с обеими версиями схемы одновременно. Если это не полтзовательские данные, то pt-online-schema-change, если таблица большая и важная, либо просто alter, если данных мало или мы можем подождать


        1. youROCK
          20.04.2016 21:44

          Извините за дубль, мобильное приложение хабра странно отправляет комментарии, что они некоторое время не отображаются на странице (репликация :)?)


        1. aktuba
          20.04.2016 23:07

          Если у вас код может работать с обеими версиями БД — почему не поднимать копии нужных таблиц на момент миграции?


          1. youROCK
            20.04.2016 23:25

            Я, скорее всего, не очень понял вопрос, но например pt-online-schema-change на момент альтера собственно создает копию таблицы и постепенно наполняет её данными, причём с помощью триггеров обеспечивается дублирование изменений, произошедших в исходной таблице, чтобы данные были консистентны. Как вы понимаете, это сильно увеличивает нагрузку на базу.

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


      1. youROCK
        20.04.2016 21:42
        +1

        Данные пользователей у нас расшардены на небольшие (1-10к пользователей) таблички. При необходимости делать альтер, сначала подготавливается код таким образом, чтобы он мог работать с обеими версиями схемы одновременно (на момент перехода), а потом непосредственно выполняется альтер. Альтер делается блокирующим, но он идет весьма быстро, поскольку таблички небольшие. Если требуется проальтерить схему у таблиц, которые не расшардены таким образом, то это делается либо с использованием утилиты pt-online-schema-change от Перконы, либо тоже «на живую», если блокирующий альтер в этом случае не затрагивает пользователей. Обычный альтер обычно идет сильно быстрее онлайн-варианта, поэтому мы его часто используем.


        1. ZOXEXIVO
          21.04.2016 11:06

          Не кажется, что в данном случае SQL не самое лучшее решение. Были ли эксперименты с другими хранилищами?


          1. youROCK
            21.04.2016 11:14

            Я бы не стал путать SQL и «автоматический шардинг. Проекты вроде vitess и cockroachdb как раз служат примером обратного.
            Одной из основных (но не единственной) причин для выбора MySQL было отсутствие вменяемых вльтернатив на момент создания сервиса (2006 год). Еще одна причина — MySQL обеспечивает транзакции и надежность хранения данных при условии исполтзования InnoDB, и транзакциями мы пользуемся очень часто. Обычно NoSQL решения не предоставляют ни транзакций, ни джойнов, поэтому это явно не способствует ускорению разработки. Мы пробовали MongoDb для одного проекта, и вроде как особых проблем с монго не испытывали. Тем не менее, сейчас MongoDb мы не используем, потому что тот проект „не полетел“. Наша архитектура почти вся построена на MySQL и сишных демонах, и с точки зрения поддержки, как мне кажется, сложно придумать что-то лучше для хайлоад-проекта с многолетней историей.


            1. ZOXEXIVO
              21.04.2016 12:31

              Понятно, просто по расскажу сложилось впечатление, что вы мучаетесь и вас что-то не устраивает.
              Раз все хорошо, то вопросов нет.


              1. youROCK
                21.04.2016 22:08

                Я бы сказал, что в большой системе наличие схемы — это скорее плюс, чем минус. Да, выполнение альтеров (неатомарное в пределах кластера) создает нам проблемы при автоматической миграции пользователей, к примеру, но без схемы было бы еще хуже. В целом, MySQL подходит под наши условия очень хорошо и отказываться мы от него не собираемся в ближайшем будущем.