понедельник, 26 сентября 2011 г.

[prog] Вспомнился случай, когда хотелось иметь горячую замену кода

Во время чтения вот этого обсуждения, в котором зашел разговор о применимости горячей замены кода (одна из важнейших фич языка Erlang), вспомнил ситуацию, когда такая замена не помешала бы мне в C++.

Есть в нашем SMS-шлюзе специальный компонент под названием send.bufferizator. Его задачей является сглаживание резких пиков в количестве исходящих сообщений. Например, происходит какое-то событие и генерируется пачка сообщений, объем которой многократно превышает разрешенную пропускную способность. Скажем, клиенту разрешено 100 sms/sec, а он однократно сгенерировал 500, а затем в течении 5-10 секунд больше ничего не отсылает. В такой ситуации send.bufferizator вбирает в себя всю пачку и отдает ее содержимое не превышая разрешенной скорости – т.е. эти 500 сообщений выйдут из send.bufferizator-а в течении 5 секунд по 100 в секунду.

Данные внутри send.bufferizator хранятся в ОП, не в БД. Хранение в долговременной памяти не нужно, т.к. наш протокол обмена сообщениями устроен так, что операция отсылки является безопасной (идемпотентной, если употребить умный термин) – до тех пор пока шлюз не пришлет подтверждение клиент может (и должен) повторять попытки отсылки. Поэтому, если send.bufferizator по какой-то причине перестает работать, то теряется содержимое его очередей. Но это не страшно, т.к. никаких подтверждений мы клиенту не отсылали и клиент вновь повторит их отправку. Тем не менее, при больших скоростях send.bufferizator может содержать у себя по несколько тысяч сообщений, поэтому прерывать его работу не очень хорошо.

Но это все преамбула. Теперь сама история. Однажды среди ночи меня разбудил телефонный звонок из нашей службы техподдержки с просьбой разобраться с проблемой, с которой раньше сталкиваться никому не приходилось. Было видно, что один из send.bufferizator-ов реагирует на внешние раздражители, но сообщения через себя не пропускает.

Проблема оказалась любопытная – при обработке одной из ситуаций я лишний раз декрементировал текущий показатель нагрузки и, поскольку это был unsigned int, значение переходило через ноль и получалось 0xffffffff. Из-за чего send.bufferizator считал, что нагрузка сейчас многократно превышена и новые сообщения должны приостанавливаться пока она не снизится. А снизится она, понятное дело, не могла.

Лекарство, как и во многих других случаях – “выйти и зайти”, т.е. рестартовать компонент. Однако, ошибка в коде оставалась, я ее быстро исправил. Но для обновления всех компонентов пришлось делать их рестарты, естественно, с потерей текущего содержимого ОП. И вот это был тот самый случай, когда горячая замена только кода работающего модуля, без потери его текущих данных, пришлась бы очень кстати. Т.к. тогда этот bugfix прошел бы вообще никем не замеченный.

Мораль сей басни такова. Если кто-то считает, что некая фича является бесполезной потому, что он не имеет перед глазами примеров ее применения, то это не значит, что фича действительно бесполезная. Скорее это просто отсутствие примеров здесь и сейчас. Но, в свою очередь, наличие примеров не делает фичу очень полезной и очень востребованной ;)

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

PPS. Когда-то на глаза попадалась статья Practical Dynamic Software Updating for C в которой описывалась технология накатов бинарных обновлений на работающий C-шный код. Правда, не могу судить о том, насколько все это жизнеспособно и применимо. Так же не знаю и о дальнейшей судьбе данного исследования.

PPPS. Не могу отказать себе в удовольствии с сарказмом пройтись по цитате, которую thesz вырвал из доклада “Running a startup on Haskell”: Deploying our code is a simple matter of redirecting a symlink, then bouncing the server. Downtime during a deploy is a fraction of a second. Ну прям бином Ньютона ребята выдумали, никто кроме хаскелистов до такого додуматься не смог бы, поэтому этим нужно непременно хвастаться! Хотя такому приему уже сто лет в обед и его переизобретает, имхо, любой разработчик, которому приходится плотно сталкиваться с проблемой развертывания бинарников.

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