Все началось с того, что стал падать мемкеш, вернее мемкешДб. И падал он как-то хитро. Переустановка мемкеш+мемкешДб+BerkeleyDb ничего не дала. После некоторых эмперических вычислений стало понятно, что падает на методе MultiGet, при том очень интересен тот факт, что падение зависит от порядка задания ключей и кол-во ключей должно быть более 3х.
Ошибка падения: #16 — неполный прием данных. Ага, значить барахлит мемкеш. Лезем в логи, анализируем логи на лету и видим заветное END — значить контент по логам отдается весь. Здесь следует раскрыть некоторые детали протокола обмена.
Существует два типа протокола обмена: бинарный и текстовый. В соответствии с текстовым протоколом сообщение состоит и
заголовка и собственно тела. Рассмотрим две основные команды, которые могут всем понадобиться при отладке: set и get.
Ошибка падения: #16 — неполный прием данных. Ага, значить барахлит мемкеш. Лезем в логи, анализируем логи на лету и видим заветное END — значить контент по логам отдается весь. Здесь следует раскрыть некоторые детали протокола обмена.
Существует два типа протокола обмена: бинарный и текстовый. В соответствии с текстовым протоколом сообщение состоит и
заголовка и собственно тела. Рассмотрим две основные команды, которые могут всем понадобиться при отладке: set и get.
set <имя ключа> [noreply]\r\n
.... тело сообщения длинной length байт
если не установлен параметр noreply, то вернется STORED\r\n или ERROR\r\n
flags - флаги управления
expiraton - время, до которого кеш актуален
length - длинна
далее идет сообщения длинной length байт
get <имяключа>
должны получить строку:VALUE <имя ключа> .... тело сообщения длинной length байт
END
в случае мультигет:
gets <имяключа1> <имяключа2> <имяключа3> ...
должны получить строки:VALUE \r\n
.... тело сообщения длинной length байт
VALUE \r\n
.... тело сообщения длинной length байт
VALUE \r\n
.... тело сообщения длинной length байт
END
Более подробно в файле protokol.txt папки doc дистрибутива memcached
пробуем отладку из консоли:
telnet localhost 11211
через раз выдает то полный контент (заветный END), то останавливается почти у конца. Вопрос со времен Чернышеского: "Что делать?". Однако мой глаз падает на результаты вывода. Лезут сплошные кроказаблы - и не удивительно, ведь в мемкеши данные хранятся в зазиповонном формате. Очевидно, один из символов просто воспринимался терминалом как служебный и терминал подивсал. По логам - всё Ok!
Попытка #2. Итак повторю, наша цель определить что багует. А баговать может:
- memcached
- BerkeleyDb ( как составная часть memcachedb )
- libmemcache (c-клиент)
- memcached pecl расширение PHP.
Пишем PHP программу из 10 строчек, которая реализует упрощенный нативный memcached протокол:
- установить соединение
- записать в сокет строку: "gets <имяключа1> <имяключа2> <имяключа3>"
- прочитать строку (до символов \r\n)
- ответ вывести на экран, распарсить, получить длинну сообщения
- пропустить n-байт
- перейти к пункту 3.
Результат вывода программы приблизительно следующий:
VALUE key_1 67 678 178468
VALUE key_2 67 526 178468
VALUE key_3 67 349 178468
END
Проверяем на 1000 случайных ключей (уже сгенерированных данных и записанных в кеш)
Ага, все в порядке, значит из списка уже выпадает два пункта.
Попытка #3.
Пересобираем memcached экстеншен. Безрезультатно. Следует заметить, что она была пропатчена и поэтому можно было грешить на патч. Остается последовательно копать. Попробуем поискать в libmemcached.
Попытка #4.
Переустанавливаем libmemcached. Берем из каталога test тестовый пример и переписываем его на мультигет. Компилим с опцией включенной отладки и запускаем. Наблюдаем Sigmentation fault. Падает в методе формирования хешей. Заходим на офф. сайт, читаем багреппорты, качаем новую версию libmemcached, переустанавливаем, пересобираем РНР и счастливы...
Оказывается, сообщение об ошибке может содержать совсем иные мотивы и чтоб устранить простой баг мало быть просто РНР программистом... А в прочем, может Хабражители найдут еще и пару иных полезных мыслей.