ты не один

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

В рамках работ по выводу одного “неповоротливого асфальтоукладчика” на земную орбиту были проведены ряд его модернизаций. Испытания на тестовом полигоне показали что машина стала ездить на порядок быстрее и было решено доработки включить в работающий прототип(вроде и не первая космическая, но уже есть чем гордиться!). Но на первом же серьёзном показе зверь-машина с чудо{вищным} грохотом рассыпалась у всех на глазах и я сел в лужу…

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

Первоначальный вариант выглядел примерно так:

if (!$data = cache_get($key)) {
   $data = data_from_db();
   cache_set($key,$data,$expire_time)
}

Без излишеств, просто и со вкусом. Но позже выснилось что “всё не так просто” и в зависимости от некоторых космических характеристик и результатов выборки из базы будет меняется ещё одна переменная (меняется переменная… ого!).

if (!$data = cache_get($key)) {
   $data = data_from_db();
   cache_set($key,$data,$expire_time);
   $other_data = some_function($data,$env);
   cache_set($other_key,$other_data,$expire_time);
} else {
   $other_data = cache_get($other_key);
}

Аляповато, но, чёрт возьми, работает…
Работало…
Должно было по идее работать…
Но, почему-то оказавшись на боевом, изрядно нагруженном, сервере это не сработало. Вернее это работало…, но не всё время… По истечении $expire_time периодически пропадали данные для $other_key. При отключении и/или очистке кеша работоспособность восстанавливалась… Кто виноват? Что делать? Кто за всё это ответит?!? Пришлось срочно пошевелить опилками.

Как мы все наверное догадываемся – реальный сервер обслуживает нереально много клиентов, причём не по очереди. И нет совершенно никаких гарантий на то, что между записью $key и $other_key не встрянет какой-либо маленький, но шустрый процесс (хотя нам это и не особо страшно), так же нет никаких оснований полагать что между выборкой $key и $other_key кто-то умный и большой не очистит кеш (целиком, или только $other_key – в данном эпизоде это не имеет значения).
Оснований не было, но я был почему-то твёрдо уверен, что “это если и происходит, то с кем-то другим”. С пониманием проблемы конечно же пришло и простое решение, но ошибка, согласитесь, совсем не тривиальная.

Так что мораль сей басни: помни – ты не один!

23.09.08  |   | стань первым

Заборостроительный факультет

Давно хотел уже "выкрикнуть душою", а тут предоставился удобный случай. Итак курс лекций "Вселенское твердолобие. Методы борьбы.".

Заборы в коде.

Вот cтрочка кода из первого попавшегося поста:

setTimeout('setOpacity(\''+obj+'\', '+i+')',50*i);

Вот кто может объяснить наличие этих треклятых наклонных реек? Тут виноватого, мне кажется, не найти. Эти заборы встречаются практически в каждой бумажной(и не только) книжонке связанной с программированием. Смело желаю их авторам жидко обосраться… Запоминаем (а лучше записываем): в нашем распоряжении имеется два вида «кавычек» – (' и "), и властью данной мн ой не так во имя чита вы вольны их использовать в произвольном порядке.

setTimeout('setOpacity("'+obj+'", '+i+')',50*i);

Прежде чем нарисовать забор пойдите выпейте чаю. Если желание не прошло – налейте ещё…
Аналогичный же случай с pcre – развею миф о том, что ограничителями регулярного выражения могут быть только заборные рейки? Сравните:

$re = '/(\d{2})\/(\d{2})\/(\d{4})/';

и

$re = '~(\d{2})/(\d{2})/(\d{4})~';

P.S.

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

setTimeout(function(){setOpacity(obj,i);},50*i);

И никогда более не пишите setTimeout("func()",100);, иначе у вас будут ломкими ногти, появиться перхоть и насмерть замучают кариозные монстры.

setTimeout(func,100);

P.P.S.
Я надеюсь NoNseNs не примет близко к сердцу данный опус и поймёт всё правильно. Спасибо за внимание.

25.01.08  |   | 12 comments

NULL без палочки

Второй раз сталкиваюсь с неспортивным поведением со стороны MSSQL. И дабы встретив третий не тратить на него полдня документирую.

Имеется:

  1. два слинкованных MSSQL-сервера
  2. подключение к одному из них
  3. хранимая процедура на другом

Требуется:

  • вызвать эту процедуру c набором параметров и получить результат.

Решение:

exec LINKED_SERVER.SELECTED_DATABASE.dbo.procedure_name @param_2=value, @param_1=value, @param_3=value…

Ничего не предвещает беды. Небо чистое, коннект уверенный, лаг умеренный… и тут… растёт напряжение, давит тишина, в жилах стынет кровь и появляется леденящий душу ПИЗДЕЦ!

Я не случайно параметры написал “вразнобой”. Казалось бы ничего криминального – они же именованы. И всё отлично покуда все параметры принимают какие-либо значения. Но как только окажется что @param_2=null (или @param_1) вы будете приятно удивлены загадочными сообщениями вроде “а почему это параметр_2 не передан?” или “зачем параметр_1 ты передал мне два раза?”. И будете долго биться в истерике, когда окажется, что выполняя запрос сразу на нужном сервере, вы получаете нормальный результат без каких-либо сообщений об ошибках. Мистика?

Как удалось выяснить – параметр, который null на линкованный сервер попадает уже без имени (т.е. не “@param_2=null, @param_1=value, @param_2=value”, a “NULL, @param_1=value, @param_3=value” и как следствие – этот самый NULL в данном случае будет считаться первым входным параметром в процедуре…

Решение было тривиальным(и глупым) – вынести все параметры, которые могут быть null, в конец объявления. Но если кто-то объяснит суть описываемого явления, и красивый выход из такого положения – буду очень признателен.