История одного бага или почему следует знать не только РНР

    Все началось с того, что стал падать мемкеш, вернее мемкешДб. И падал он как-то хитро. Переустановка мемкеш+мемкешДб+BerkeleyDb ничего не дала. После некоторых эмперических вычислений стало понятно, что падает на методе MultiGet, при том очень интересен тот факт, что падение зависит от порядка задания ключей и кол-во ключей должно быть более 3х.


    Ошибка падения: #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, переустанавливаем, пересобираем РНР и счастливы...

    Оказывается, сообщение об ошибке может содержать совсем иные мотивы и чтоб устранить простой баг мало быть просто РНР программистом... А в прочем, может Хабражители найдут еще и пару иных полезных мыслей.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 5

      +2
      s/имперических/эмпирических/g;
        0
        Ага, знание регекспов тоже не помешает
          0
          спс
        –1
        Так кто в итоге виноват и почему контент по логам отдавался весь?
          0
          Попытка #4. — после переустановке libmemcached и пересборке РНР все заработало.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое