Если вы чаще раза в год ходите в наши магазины за спорттоварами или одеждой, скорее всего, у вас есть наша клубная карта (синяя, серебряная или золотая). Меня зовут Максим, я заместитель директора департамента разработки, внедрения и сопровождения ПО, и в этом посте мы с коллегами расскажем про становление клубной программы Спортмастера, про коллекцию собранных нами в процессе граблей и про то, чем наша клубная программа отличается от привычных скидочных карт других торговых сетей.



Тогда


На дворе стоял 2004-й год. Что было — клубная программа у Спортмастера и доллар по 27 рублей. Чего не было — нормального интернета на местах и стабильных каналов связи у магазинов.

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

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

Работали сами карточки довольно просто — на каждой карте просто накапливалось определенное число бонусов, которые её владелец мог радостно и с чувством потратить. То есть для базы это выглядело просто, грубо говоря, одна карта — одна циферка. С золотыми картами было немного сложнее — там кроме циферки с бонусами были ещё и сервисные баллы. Это когда ты купил велик, а через полгода захотел подтянуть цепь, проверить тормоза, звонок стал не так греть душу и отпугивать прохожих и прочее (или поточить коньки к новому сезону и поправить сноуборд, например).

При этом начисление бонусов проводилось мощностями решения на базе естественного интеллекта — у нас был специальный сотрудник, который писал селекты и делал выборки, потом начислял в специальной табличке бонусы, а система подтягивала эту табличку. И да, если этот человек вдруг решил чуток поболеть или что ещё — некоторые покупатели могли остаться без бонусов в эти нелёгкие дни.

Само собой, для бизнеса такая ситуация была довольно затратна, да и вообще — опасна и непредсказуема, и бизнес хотел перевести систему на нормальные (автоматические) рельсы.

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

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

Базы не успевали в магазины, иногда из-за этого магазины забывали их обновлять, и получалась откровенная каша — в магазине А человек купил что-то полезное и получил бонус, а магазин Б через пару дней всё ещё не в курсе, что человеку можно бы и бонусы списать с новой покупки.

И тогда Александр Афанасьев (ныне IT-директор другой компании) придумал, как всё это можно сделать самим, не покупая сторонний софт. Собрал от бизнеса ряд требований к этой системе с их стороны и прописал новые возможности. Сначала просто в виде приятных фич — к примеру, теперь бонусы это не просто одна цифра, а целая сложная система. Можно давать человеку бонусы на день рождения, можно отдельно дать бонус только на лыжи и связанные с ними товарные позиции, можно предложить бонусы только на товары бренда Columbia и в определенные периоды времени — и всё это комбинировать, комбинировать, комбинировать.

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

Третьей проблемой было проведение операций по сгоранию бонусов. У нас же как — ты можешь весь год задорно копить баллы, но 1 марта они всегда вероломно сгорают, если ты не успел их потратить.

Первая версия системы (под названием CARDS) могла их нормально учитывать, но вот когда она переходила в режим бонусосжигательного завода, начинались проблемы. Ведь сжигание бонусов — это полноценный проход по всей базе с изменениями. Учитывая размер базы, это могло занять дня 3-4. Причем в процессе она жутко тормозила и тупила, из-за чего иногда сжигание бонусов прерывали, и получалось, что в каком-то магазине у камрада Петрова, зашедшего за новыми шариками для пинг-понга, бонусы ещё есть, а у Сидорова, зашедшего за новым великом, уже, к сожалению, нет.

Новая версия системы


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

Кстати, о коммуникациях — мы с самого начала сделали так, что система лояльности сама в нужное время формировала тексты коммуникаций с клиентами, выцепляя из базы бонусные баллы, и сама отправляла их клиентам. Клиентов у нас было много, поэтому для рассылки СМС в то время использовали сторонних провайдеров.

Взаимодействие с ними проходило примерно так:

  • провайдер понимал, что перед ним крупный клиент, и начинал радоваться
  • крупный клиент в виде нас уточнял, осилит ли провайдер на самом деле такие рассылки
  • провайдер говорил, что осилит, само собой
  • провайдер получал задание на рассылку огромного количества СМС за короткий промежуток времени и принимал решение прилечь отдохнуть

Так вот, о прототипе. В принципе всю систему было решено сильно отреинженирить, потому что изначально она была заточена только под учёт бонусов, а не под работу онлайн, поэтому она ожидаемо перестала справляться с отдачей. Причём падала она, само собой, в моменты высокой нагрузки. То есть в самое вкусное для магазина время — Новый год, 8 марта, 23 февраля и прочие приятные даты.

Падает система —> падает настроение у бизнеса —> падает настроение у всех.

Вдвоем с коллегой мы переписали систему по следующему принципу.

Компонент 1. Препроцессинг, максимально оперативно отдающий магазинам ответ.
Компонент 2. Процессинг, тот самый магический ящик, сложно и хитро начисляющий бонусы на товарные чеки.
Компонент 3. Маркетинг, собирающий всё это воедино и формирующий тексты коммуникаций.

Плюс ко всему мы решили проблему сгорания бонусов. Новая система просто их не сжигала. Ведь если ты не заставляешь систему сжигать бонусы — у тебя нет проблем со сжиганием бонусов.

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

Старая система фактически просто вела учёт карточек и бонусов на этих карточках. Новая система ставит в приоритет не карточку, а именно аккаунт человека. Мы можем идентифицировать его по номеру телефона (эта штука у нас работает с самого старта, мы были одними из первых, кто внедрил авторизацию по номеру телефона).

Дополнительной фичей новой системы стали так называемые товарные бонусы, это работает вот так:

  • у каждого товара есть атрибуты (наименование, товарная категория, размер, цвет, вид спорта, прочее, прочее, прочее).
  • система комбинирует эти атрибуты, формируя логическое условие для начисления бонусов.
  • при прилетании чека такое условие всегда проверяется.

Мы показали этот прототип в работе бизнесу. Бизнес дал добро.

Писать систему мы начали 1 марта, внедрили в работу 27 октября 2013 года (писали вдвоём, да). На самом деле, плановой датой поставки было 1 сентября, но не успевал главный контрагент системы — розничные магазины. Магазины не успевали по ряду своих причин, плюс не у всех обновили кассовое ПО (а обновлять кассовое ПО в масштабах довольно крупной сети — это та ещё боль). Поэтому отложили, дождались их, и запустились 27 октября.

Идеология системы


Заложили главную идею — ни магазин, ни кассовое ПО больше не работают с логикой бонусов. Магазин теперь просто отправляет корзину покупателя в Центр, Центр всё это дело обрабатывает, отдаёт магазину расчёт по бонусам.

Сейчас бонусы размазываются так:

  • Прежде всего, бонусы размазываются по всему чеку равномерно, на все товарные позиции. Это и полезно для аналитики, и помогает в случае возврата товара.
  • Ввели понятие приоритета бонусов. Бонусы есть товарные, есть бонусы на дни рождения, у которых срок действия короткий, есть регулярные (самые живучие). Поэтому мы сначала списываем именно специфичные бонусы. То есть пришёл человек за лыжами — мы спишем прежде всего бонусы, которые у него есть именно на лыжи. А то получится, что пришёл он за лыжами, мы списали регулярные бонусы. Через неделю придёт за курткой, а мы ему — Мужик, у тебя только бонусы на лыжи есть. Хочешь лыжи? То же самое с покупками в периоды дня рождения, сначала списываем их, а потом — регулярные.
  • Разносим бэкофисные операции и фронт. Теперь магазины, приходящие с запросами, никак не влияют на работу и производительность сервиса, начисляющего бонусы, и наоборот

В общем, удалось забороть все старые проблемы, а вместо новых проблем добавить новых возможностей.

Вместо бэклога у нас была вот такая тетрадь Александра.





Запуск новой версии системы


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

Звучит неплохо, но на деле упирается в пару ограничений.

Во-первых, из-за большого количества магазинов (1200+) мы должны были успеть всё сделать за 3 часа. Пока один магазин в полночь в одном часовом поясе закрывается, в другом совсем иное время, а тут ещё и плюс круглосуточные магазины. В общем, чтобы сконвертировать все данные из старой системы, скормить новой, запуститься на трёх серверах сразу — 3 часа.

Подводные камни были вот такие:

  • Система врубалась сразу на всей сети. Если всё везде хорошо — всё работает по всей сети. Если что-то падает — да, падает по всей сети.
  • Новая система при включении должна содержать в себе все данные, которые были в старой системе на момент закрытия магазинов и выдачи самого свежего бонуса. Мы запускались на 4 страны разом. БД была более терабайта и хранила сотни миллиардов записей.
  • В 23.00 мы должны были выключить систему. Сконвертировать всё. Залить в новую систему. Включить всё. При этом всё должно работать.

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

Что немного отличалось от задуманной цифры в 3 часа.

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

Но всё равно, такой объём данных на стандартных машинах в установленный срок сделать не получалось.

И тут надо отметить Oracle Exadata. Ребята из Oracle сделали специальную железку, которая прекрасно работает с её же БД, да ещё и на флеш-дисках. В общем, было принято волевое решение использовать Exadata. С её помощью на тестах мы осилили сделать всё нужное за 2 часа вместо 9 и поняли — надо брать.

Так как мы ребята дотошные, в процессе настройки и работы мы выгребли кучу багов и завалили саппорт Оракла с запасом. Например, был один занятный баг — из-за ошибки во внутренней обработке запроса Oracle стал усиленно потреблять TEMP. Мы это вовремя заметили, и накинули ему еще TEMP’овых файликов, было очень интересно, когда же он нажрется. Но поскольку железяка оказалась очень толковой и знающей свое дело, она с чувством употребила 3 Тб TEMP за 10 минут, поняла, что больше нет, и ушла на покой. Пришлось придумывать обходы.

С одной стороны, было круто, что всё в плане конвертации у нас сделалось за 2 часа. С другой стороны, во всём процессе чистой конвертации 2 часа, а мы планировали ещё и:

  • перезалить все данные с серверов старой системы на exadata, потому что она дико быстро всё считает.
  • сконвертировать данные из старых структур в новые.
  • залить всё это сконвертированное добро на три разных сервера.

В каждой БД при этом находилась куча полезной служебной инфы типа тех же индексов, которые могли помочь при перестройке, но мы забили на это и решили перестроить всё заново уже на боевых серверах.

Подготовка


Мы готовились вовсю. Мы спали на работе. Мы увешались не только скриптами, но и множеством метрик.

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

Ясное дело, именно в день запуска что-то пошло не так.

К нашей чести, косяк был не на нашей стороне. Где-то тупо моргнула сеть. То есть сидишь себе такой, настраиваешь всё, чтобы комар не только носа не подточил, но даже не успел подумать об этом — и кто-то где-то просто дёргает не тот кабель.

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

Итого последний сервак запустился в 11 утра. Но так как мы строили систему таким образом, что всё было изолировано, всё заработало нормально.

Сейчас


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

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

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

Мы написали алгоритм, который получает от магазина чек, пробегается по товарным позициям в чеке, применяет к нему все возможные в данную дату и в данном городе акции и скидки, и выдаёт максимально выгодный для юзера результат. После чего возвращает всё это в магазин. И тут есть еще направления развития:

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

Благодарный клиент может в этот момент вспомнить, что он хотел купить ещё и носочки, и просит добавить их в чек. Само собой, добавление носочков (или чего угодно) требует полного пересчёта заново.

Но и с этим мы тоже разберемся. А в одном из будущих постов мы расскажем вам историю создания сайта Спортмастера.