пятница, 16 мая 2014 г.

Session storage benchmark

           Уже очень давно я бьюсь над проблемой: а как настроить Redis "чтобы оно работало". Казалось бы - хранить сессии в Redis - что может быть проще ? Это ведь как раз то что доктор прописал.  Хер там был.  Чем больше я еб#### с этим редисон, тем больше я начинаю ненавидеть все что начинается на NoSQL.  Для меня скоро NoSQL будет синонимом - "не работает".  Взять к примеру Couchbase/CouchDB. На бумаге все прекрасно. Казалось-бы, вот оно счатье - настало. Все само реплицируется, партиционируется и масштабируется. Хер там был. Оно просто не работает. Если при более-менее высокой нагрузке  начнется решардинг - наступает пиз###. Производительность всего кластера настолько деградирует что это равносильно даунтайму.  И это не говоря о случайных спадов в производительности - когда у нг внутри какой-то буффер переполняется и время ответа ни с того ни с сего увеличивается в 10 раз. А через минуту все снова охеренно. Но за минуту ты легко можешь отхватить и 10 и 100 тысяч запросов.  Какая-то часть из них умрет по таймауту и покажет 50x ошибку пользователю. В погоне за успехом в кратковременных бенчмарках, которые любят размещать в блогах  - NoSQL писатели такую херню делают, что диву даешься.  Неужели они не понимают что гарантированная производительность(пусть немного медленне) - это гораздо лучше чем 100000 запросов в секунду в пике,  с временными спадами до 10 запросов в минуту.  
             Итак вернемся к нашему редису.  На первый взгляд идеальное решение для хранения сессий. Но есть маленькое но.... Он однопоточный.... То есть если вы запустили какую-нибуть тормозную команду - например KEYS, то до тех пор пока команда не выполнится - ни один коннект не будет обработан. Просто такой маленький stop the world.  Представьте что у вас 10GB база и кто-то запустил KEYS по ошибке... сколько у вас коннектов отвалится по таймауту ?? Они еще зачем-то прикручивают туда Lua интерпретатор... Если у вас однопоточный сервер  с c event-loop, то все что сложнее чем GET/SET вам просто противопоказано!  Иначе забыть можно про сколько нибуть предсказуемое время ответа. 
                Вторая проблема - RDB checkpointing.  Документация нам говорит что раз в какое-то время Redis форкает процесс, который его сохраняет на диск. Вроде все охеренно... сохранение происходит в отдельном процессе... никто никому не мешает...  Но как то честно говорит документация - на время создания снапшота все блокируется...  То есть раз в какое-то время ваш Redis будет подвисать... Причем чем больше ваша база - тем больше подвисание. Вот подвисли мы на 30 секунд - а кто запросы то обрабатывать будет ?? Хуй в пальто ? Что тут еще скажешь ... Прелестно блять, просто прелестно.  Писали писали приложение, только оно начало работать... база выросла - и на тебе жопу. И что хочешь тут то и делай. Broken by design.
               В общем вы поняли что Redis идеален если у вас маленькая база и нет нагрузки. Во всех остальных конфигурациях - ебитесь как хотите. Но спрашивается - если у вас 10 сессий в час, и 3 хромых пользователя онлайн - нахера вам Redis ??? Храните сессии в файлах и не выпендривайтесь. 
              Итак, слюни в сторону - что дальше то делать? Если Redis то работает, то не работает - нужно поставить рядом два редиса и переключатся между ними... Выглядит все просто... но чтобы поддерживать оба редиса синхронизированными - нужно настроить репликацию между ними... Если вдруг один Redis задумался - нужно переключится... И при этом не забыть развернуть репликацию... мы же не можем в slave писать... Причем переключить нужно оба сервера... а если первый сервер в серьез "задумался" ?? Он и на пинги -то не отвечает, а ты хочешь чтобы он на более сложные команды ответил... В общем мы рискуем получить рассинхронизацию - когда оба сервера будет считать себя master, а ты потом разбирайся - что и куда у тебя записалось... В общем классический split brain.  Легкая на первый взгляд задача оказалась тем еще гемороем.  Для того чтобы решить эту проблему Redis предлагает Sentinel. Охеренно, подумал читатель. Но не тут-то было:
Sentinel is currently developed in the unstable branch of the Redis source code at Github. 
         Документация честно говорит - что Redis Sentinel - он ... ну как сказать... не так уж прям чтобы очень стабильный был. Но то и понятно, задача которую он решает - не из простых. Поставили мы два редиса, два Sentinel... но тут мы понимаем что для того чтобы избежать split brain нам нужно минимум 3 инстанса .... окей, это тоже решаемо... подогнали еще сервак, воткнули туда редис с сентинелем и ждем. Чего ждем непонятно. Ну переключит Sentinel мастера на другую ноду, но нашему приложению-то кто об этом скажет ? Либо нужно писать "умных клиент", который сам будет из 3-х предложенных инстансев выбирать мастера.... либо ставить load balancer....  с недавних пор (версии 1.5.x dev) HA proxy умеет выбирать мастера из N-бэкендов. Вообще пользуюсь случаем хочу сказать - HA proxy охеренен. Просто ты работаешь и чувствуешь как там все продумано, все до мелочей. Начиная от конфигурации и заканчивая логгированием. Сразу видно что HA proxy писался для решения реальных задач в production системах, а не как очередная гиковская игрушка. Dev версии HA proxy более стабильны чем большинство релизных версий современного софта.  Сразу видно чо писал его человек старой закалки с огромным опытом. Кстати для тех кто не знает - на сайте HA proxy есть коллекция довольно занимательных статей.   В общем при правильной настройке HA proxy - то единственный компонент получившейся системы, за который я могу быть спокоен. 
             Итак, вернемся к нашим котятам. Теперь получившееся нечто надо протестировать. По возможности реальной нагрузкой... Для этих целей и был написан session_storage_bench. Этот скрипт пытается в точности эмитировать поведение php-fpm процесса: заблокировал сессию, прочитал, подождал 200ms (время обработки запроса), записал обратно, снял блокировку. Он умеет разговаривать с memcache(и другими memcache-like системами, такими как Couchbase) и с Redis. Думаю не составит большого труда написать адаптер для любого другого хранилища сессий. Также он умеет имитировать persistent коннекшены.  В общем - пользуйтесь :-)          

Комментариев нет:

Отправить комментарий