Перейти к содержимому


- - - - -

Сбой при наличии большого числа обращений к несуществующим файлам


  • Авторизуйтесь для ответа в теме
Сообщений в теме: 6

#1 Alzent

Alzent

    Участник

  • Пользователи
  • PipPip
  • 12 сообщений

Отправлено 11.11.2009, 10:54

Доброго времени суток!

Во первых, позвольте поблагодарить за отличный скрипт! Очень нравится мне ваша разработка, и очень радует, что она сделана весьма качественно и управляемо. С такой приятно работать и дорабатывать.

Сейчас вот столкнулся со следующей проблемкой, спешу поделиться: судя по всему, она вызывается тем, что в случае наличия большого количества одновременных запросов к несуществующим файлам фатально лочится кеш (видимо, при одновременных обращениях к несуществующим файлам присуствует несинхронизированность залочки/разлочки кеша).

Как воспроизвести: добавьте в шаблон страниц несколько тегов стилей, скриптов, картинок - чего угодно, то вызывает новые запросы к серверу на несуществующие файлы. В этом случае запрос будет роутится в index.php и обрабатываться движком.
Затем стартаните сайт в браузере и максимально быстро нажмите несколько раз кнопку "обновить F5". Несколько запросов могут успеть пройти, но вскоре выпадет 503 Service Unavailable.
На последнем тесте у меня даже выскочила целая портянка:
Warning: file_get_contents(Y:\home\zebrum.my\www\zcontent\cache/6/6a992d5529f459a44fee58c733255e86.zcache) [function.file-get-contents]: failed to open stream: No such file or directory in Y:\home\zebrum.my\www\zengine\classes\zcache\backend\file.php on line 146

Warning: Cannot modify header information - headers already sent by (output started at Y:\home\zebrum.my\www\zengine\classes\zcache\backend\file.php:146) in Y:\home\zebrum.my\www\zengine\classes\zengine.php on line 67

Warning: Cannot modify header information - headers already sent by (output started at Y:\home\zebrum.my\www\zengine\classes\zcache\backend\file.php:146) in Y:\home\zebrum.my\www\zengine\classes\zengine.php on line 68
Service Unavailable

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

Как правило, последующие обращения к сайту таки проходят успешно (если их не слишком много), однако когда я только столкнулся с этой багой, у меня любое последующее обращение вызывало 503 из-за залоченного кеша (файлика /zcontent/cache/lock), что, очевидно, очень тревожно. Пока лок не удалил, сайт "лежал".

Если нужна помощь, я готов. Лишь бы скорее решили ;)

зы: да, забыл сказать, установлен патч из вот этой темы: http://zebrum.ru/for...p?showtopic=670

#2 support

support

    Активный участник

  • Главные администраторы
  • PipPipPip
  • 1 140 сообщений

Отправлено 11.11.2009, 12:08

Спасибо за информацию, буду смотреть, как можно обезопаситься от такой блокировки.

#3 Alzent

Alzent

    Участник

  • Пользователи
  • PipPip
  • 12 сообщений

Отправлено 11.11.2009, 12:57

Хм, вообще, я пока ещё не очень хорошо знаком с внутренним устройством зебрума и могу ошибаться, но всё же попробую высказать кое-какие соображения, возможно как-то поможет:

* больше всего меня беспокоит тот факт, что один запрос закончился полной блокировкой всего кеша, после чего сайт "лёг". Полагаю, что аналогичное событие могло быть причиной вот этого: http://zebrum.ru/for...p?showtopic=688 багрепорта. Возможно то, что это происходит свидетельствует о том, что осталась не закрытой разлочкой какая-то ветка выхода из алгоритма работы с кешем.

* повторюсь, что не достаточно знаком с зебрумом, но всё же: какова целесообразность залочки кеша при операциях чтения? В большинстве случаев (если не во всех?), мне кажется, по запросу можно понять, что он вызовет только чтение из кеша, а эта операция не изменяет состояний. Возможно, при этом не стоит лочить кеш, а использовать залочку только для операций изменения кеша. Тогда при чтении придётся только проверять наличие флага "идёт запись" и всё? Что касатся случаев, когда необходимо начать обновление кеша и гарантировать, что параллельная операция чтения не вернёт некогерентных данных, то при чтении можно проверять наличие флага "идёт запись" дважды - в начале и в конце работы с кешем. Если хочется обезопасится от возможности проскакивания записи между этими двумя проверками (что маловероятно, но всё же) то... Ну, раз уж мы привязаны к файлам, то можно на каждую операцию чтения создавать/удалять пустой уникальный файл-флаг в спец. папке. Соответственно, если есть такие файлы, скрипт обновления засыпает на некоторое время и проверяет условие позднее. В крайнем случае через несколько итераций все промежуточные операции чтения должны будут завершиться. Решение, конечно, не очень красивое, но не хуже хранения сессий в файлах.

#4 support

support

    Активный участник

  • Главные администраторы
  • PipPipPip
  • 1 140 сообщений

Отправлено 11.11.2009, 14:58

Просмотр сообщенияAlzent (11.11.2009, 12:57) писал:

Хм, вообще, я пока ещё не очень хорошо знаком с внутренним устройством зебрума и могу ошибаться, но всё же попробую высказать кое-какие соображения, возможно как-то поможет:
Спасибо за интерес к Zebrum Lite.

Просмотр сообщенияAlzent (11.11.2009, 12:57) писал:

* больше всего меня беспокоит тот факт, что один запрос закончился полной блокировкой всего кеша, после чего сайт "лёг". Полагаю, что аналогичное событие могло быть причиной вот этого: http://zebrum.ru/for...p?showtopic=688 багрепорта. Возможно то, что это происходит свидетельствует о том, что осталась не закрытой разлочкой какая-то ветка выхода из алгоритма работы с кешем.
Более детальное исследование показало, что в некоторых случаях после вызова функции удаления файла lock он оставался в системе. Либо какой-то процесс ухитрялся его создать и не удалить, либо что-то еще.

Я переписал механизм блокировки. Повторить полную блокировку сайта мне не удалось. Попробуйте, по результатам отпишитесь.

zengine/classes/zcache/backend/file.php
Прикрепленный файл  file.php.gz   1,78К   2 Количество загрузок:

Просмотр сообщенияAlzent (11.11.2009, 12:57) писал:

* повторюсь, что не достаточно знаком с зебрумом, но всё же: какова целесообразность залочки кеша при операциях чтения? В большинстве случаев (если не во всех?), мне кажется, по запросу можно понять, что он вызовет только чтение из кеша, а эта операция не изменяет состояний. Возможно, при этом не стоит лочить кеш, а использовать залочку только для операций изменения кеша. Тогда при чтении придётся только проверять наличие флага "идёт запись" и всё?
Сейчас посмотрел повнимательнее код и увидел, что действительно блокировка (хоть и небольшая по времени) идет при каждом обращении к странице в случае, если не запланировано будущих публикаций. Я это подправил и это изменение будет в 2.0.1.

В целом ситуация с блокировкой следующая. Блокировка используется в качестве реализации аналога транзакции для записи информации в кэш, т.к. в кэше хранится вся внутренняя структура сайта и дополнительная информация/индексы. Это исключает добавление одной и той же будущей страницы при наступлении ее времени публикации. Так же это значительно уменьшает нагрузку на сервер, т.к. параллельные запросы не будут "шерстить" по файловой структуре с целью построения/обновления индекса.

Просмотр сообщенияAlzent (11.11.2009, 12:57) писал:

Что касатся случаев, когда необходимо начать обновление кеша и гарантировать, что параллельная операция чтения не вернёт некогерентных данных, то при чтении можно проверять наличие флага "идёт запись" дважды - в начале и в конце работы с кешем. Если хочется обезопасится от возможности проскакивания записи между этими двумя проверками (что маловероятно, но всё же) то...
При работе уже непосредственно с репозиторием со страницами (кэшем), проблемы чтения нецелостных данных не столь критичны. В кэш сохнарение происходит в два этапа - создание файла с кэшем и переименование файла, поэтому считать битый кэш нельзя. Если предположить вероятность чтения битого кэша, то система посчитает, что его нет, и в большинстве случаев это приведет к отсутствию ссылки на страницу (или к чему этот файл кэша относился).

Просмотр сообщенияAlzent (11.11.2009, 12:57) писал:

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

#5 Alzent

Alzent

    Участник

  • Пользователи
  • PipPip
  • 12 сообщений

Отправлено 11.11.2009, 15:32

Просмотр сообщенияsupport (11.11.2009, 14:58) писал:

Я переписал механизм блокировки. Повторить полную блокировку сайта мне не удалось. Попробуйте, по результатам отпишитесь.

zengine/classes/zcache/backend/file.php
Прикрепленный файл file.php.gz

К сожалению, при интенсивных запросах 503 продолжает вылезать стабильно.
Полную блокировку сайта мне так же не удалось ещё ни разу повторить, но она, увы, имела место как минимум однажды. Попробую потерзать ещё, может быть придумаю, как повторить...

Просмотр сообщенияsupport (11.11.2009, 14:58) писал:

Сейчас посмотрел повнимательнее код и увидел, что действительно блокировка (хоть и небольшая по времени) идет при каждом обращении к странице в случае, если не запланировано будущих публикаций. Я это подправил и это изменение будет в 2.0.1.
Кстати, а когда можно 2.0.1 ожидать, ориентировочно?

Просмотр сообщенияsupport (11.11.2009, 14:58) писал:

Создание файла на каждую операцию чтения очень затратно по ресурсам.

Согласен. Но, по моему так или иначе сейчас именно так оно и работает. По крайней мере, когда я смотрел под отладчиком источник ошибки (как раз тот самый раз, когда сайт лежал), создание lock пыталось выполнится каждый раз из beginTransaction() в zrepository::updateIndex()... О, стоп! Я смотрю, там есть защита от того, чтобы делать это каждый раз через if (zdate::time() < $this->index['nextUpdateTime']) {       return;      }.
Но в тот момент, когда сайт лёжал, эта проверка всегда оканчивалась неудачей, я там ходил, помню. К сожалению, я тогда не догадался посмотреть значения переменных. Возможно, при каких-то условиях значение index['nextUpdateTime'] не сохраняется правильным образом в конфиге, что и имело место в тот раз.

В качестве гипотез:
А если, допустим, запрос к несуществующему файлу приходит как раз в тот момент, когда проверка if (zdate::time() < $this->index['nextUpdateTime']) должна окончится неудачей, тогда после    $this->beginTransaction(); сразу происходит обнуление  $this->index['nextUpdateTime'] = null;. Дальше судя по коду, не во всех случаях $this->index['nextUpdateTime'] получает правильное значение. Здесь где-то может быть бага?
И второй момент: что доступ идёт к несуществующему файлу, когда становится ясно? Возможно, если это случается в момент пересчёта кеша внутри этой части кода и при условии вылета с исключением, то commitTransaction() не произойдёт и кеш останется залоченным.
Сейчас попробую прогнать обращение к несуществующему файлу чтобы не сработало условие if (zdate::time() < $this->index['nextUpdateTime'])....

#6 support

support

    Активный участник

  • Главные администраторы
  • PipPipPip
  • 1 140 сообщений

Отправлено 11.11.2009, 15:55

Просмотр сообщенияAlzent (11.11.2009, 15:32) писал:

Кстати, а когда можно 2.0.1 ожидать, ориентировочно?
Если не появится потребность в каких-либо важных изменениях, то завтра в первой половине дня.

Просмотр сообщенияAlzent (11.11.2009, 15:32) писал:

Согласен. Но, по моему так или иначе сейчас именно так оно и работает. По крайней мере, когда я смотрел под отладчиком источник ошибки (как раз тот самый раз, когда сайт лежал), создание lock пыталось выполнится каждый раз из beginTransaction() в zrepository::updateIndex()... О, стоп! Я смотрю, там есть защита от того, чтобы делать это каждый раз через if (zdate::time() < $this->index['nextUpdateTime']) {       return;      }.
Но в тот момент, когда сайт лёжал, эта проверка всегда оканчивалась неудачей, я там ходил, помню. К сожалению, я тогда не догадался посмотреть значения переменных. Возможно, при каких-то условиях значение index['nextUpdateTime'] не сохраняется правильным образом в конфиге, что и имело место в тот раз.
В этой переменной хранится время ближайшей будущей публикации страницы. В предыдущем сообщении я говорил об изменении в блокировке, так вот в это условие было добавлено еще одна проверка:

if (!$this->index['nextUpdateTime'] || zdate::time() < $this->index['nextUpdateTime']) {

Если незапланировано ни какой публикации на будущее, то значение переменной будет равно null.

Попробуйте внести и это изменение и посмотреть на результаты.

#7 Alzent

Alzent

    Участник

  • Пользователи
  • PipPip
  • 12 сообщений

Отправлено 11.11.2009, 16:10

Просмотр сообщенияsupport (11.11.2009, 15:55) писал:

Если не появится потребность в каких-либо важных изменениях, то завтра в первой половине дня.
Ого. Круто!

Просмотр сообщенияsupport (11.11.2009, 15:55) писал:

В этой переменной хранится время ближайшей будущей публикации страницы. В предыдущем сообщении я говорил об изменении в блокировке, так вот в это условие было добавлено еще одна проверка:

if (!$this->index['nextUpdateTime'] || zdate::time() < $this->index['nextUpdateTime']) {

Если незапланировано ни какой публикации на будущее, то значение переменной будет равно null.

Попробуйте внести и это изменение и посмотреть на результаты.
Да, сейчас проблема с блокировкой и последующим 503 исчезла. Спасибо :)

Что касается перманентной блокировки, - ещё подумаю, как бы её повторить...




Количество пользователей, читающих эту тему: 1

0 пользователей, 1 гостей, 0 скрытых пользователей