Снова задачка для программистов

11.10.2010

Давайте подумаем вместе, при каких условиях и на каком этапе данный алгоритм не отрабатывает как задумано и данные всё равно слетают? Я в тупике, честно говоря. Не понимаю.

Да, у меня снова epic fail, иронизируйте.

Эта статья начинает цикл заметок из четырёх частей об эволюции функций чтения/записи в Daos. В следующем материале я рассказываю о стресс-тестировании. Задача не решена, но решена проблема.

Комментарии

  1. # Sam

    Хватит мучаться с файлами. Заюзай нормальную СУБД.

  2. # Тормоз

    Думаешь, ты первый? Я знаю про СУБД и что их можно заюзать. Мне нужно рабочее решение с файлами.

    Говорил уже, но придётся и тут повторить, а то замучают вопросами. Почему файлы? Главный фактор — простота установки.

  3. # IAD

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

  4. # Sam

    Заюзай хотя-бы SQLite. Он есть в PHP из коробки.

  5. # Тормоз

    IAD, сдаётся мне, что ничего хорошего в таких ожиданиях нет, тормозить будет и нагрузку давать не хилую. Да и не очень понятно, чем твой вариант принципиально отличается от моего.

    Именно поэтому я прошу подумать, что именно происходит с моим кодом, на каком участке есть сбой и почему.

    А то вариантов куча, все предлагают что-то. Я вот в этом тоже был уверен, а вышло так, что не работает как надо. Нужно добить алгоритм, вернее, добиться понимания, где именно узкое место.

    Sam, SQLite будет, но в Daos 2.0, в нынешнем Daos слишком много придется переписывать для этого.

  6. # sokol_jack: 

    А что тут иронизировать?
    Когда Alek$ тебе на узкие места твоего кода указывал (http://brokenbrake.biz/2010/10/08/Daos-1.9.2#c013204) – ты уверенно отметал все «неудобные» замечания – «В любом случае», «вряд ли будет существенным, чтобы сильно заморачиваться и менять рекурсию на цикл», «крайне маловероятно»…

    Не надоело каждые 2 недели открывать для себя новые горизонты? ;)

  7. # Тормоз

    Конкретнее. Внимательное рассмотрение какого именно замечания гарантированно решает проблему?

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

  8. # Sam

    Так и скажи, что заморачиваться не хочется и нужен костыль :)

  9. # Тормоз

    И вообще, я счастлив, что регулярно открываю новые горизонты :) Век живи — век учись. Мне лично приятно чувствовать пополнение багажа знаний.

  10. # надежный SEO хостинг: 

    ? clearstatcache — Clears file status cache

  11. # Бутылк.Ус

    Тормоз, там слетели и стили. Внутренние, конечно, которые пишутся у тебя в аттрибутах.

    По ходу, слетела не только статистика, но и настройки. Хотя настройки биллингов на месте.

    Имхо лучше все-таки сделать вставку через яваскрипт полноценного <style></style> вместе с самой строкой.

    И довольно неудобно реализованы стили в целом – у меня три разных стиля страниц (сам сайт, форум и сервисы). Везде стили разные. Пришлось рыться в движке, что бы очистить аттрибуты, а потом заморачиваться с !important в родных стилях. Но это дело наживное…

    Пока пришлось откатить до предыдущей версии =(

  12. # Sam

    Кстати, зачем изобретать велосипед на тему include_flock, если есть нативный flock, который отлично работает?

  13. # Тормоз

    Уважаемый Надёжный SEO хостинг, спасибо за помощь, но это же не повод спамить. Ссылку удалил, в следующий раз придётся внести в чёрный список.

    Почему вы считаете, что clearstatcache спасёт? Очистка кэша играет роль во второй функции, там используется кэшируемая file_exists. Однако, если файл действительно существует, file_exists не вызывается второй раз.

    Sam: Так и скажи, что заморачиваться не хочется и нужен костыль :)

    Заморачиваться не хотят те, кто предлагает СУБД. Разобраться, что конкретно и почему происходит — та ещё заморочка.

    Пока пришлось откатить до предыдущей версии =(

    Зачем? И где ты её взял? :)

    Кстати, зачем изобретать велосипед на тему include_flock, если есть нативный flock, который отлично работает?

    Который нихрена не работал.

  14. # sokol_jack: 

    Тормоз, пополнять багаж знаний можно по-разному.
    Можно при появлении проблемы прочитать необходимый минимум для решения текущей проблемы и на неделю-две успокоиться.
    А можно при появлении проблемы полностью изучить предметную область, советы «старших товарищей» и потом на подобные проблемы не натыкаться.

    Это я к тому, что если придуманное тобой решение (костыль ;) ) заработало как надо у тебя и еще на 1 компе, это вовсе не значит, что это решение – правильное.

    Но в одном точно сходимся – получение новых знаний – благо. :)

  15. # Тормоз

    Да изучал я дофига решений. Сперва был flock, который вроде бы для таких случаев и предназначен. Не работало оно.

    Сейчас вот, видишь, атомарные операции переименования вроде бы должны были спасать, это рекомендации от разработчиков различных счётчиков и т. п. Видишь, сбрасывается.

    Я хочу понять. И пока не понимаю. Мне нужно чётко знать, на каком месте происходит этот сброс и почему.

    Одна голова хорошо, а несколько — лучше. Про то, что в PHP кэшируются результаты многих функций для работы с файловой системой, я, к своему стыду, вообще не знал, например. За это знание спамеру спасибо уже сказал :)

  16. # Sam

    flock можно использовать по-разному. Если одним способом не заработало, мб просто где-то была ошибка.

  17. # Тормоз

    Да, была ошибка, но я её устранил.

  18. # polonskiy: 

    Мне не понятны некоторые моменты:
    1. Зачем использовать рекурсию? Как вариант можно использовать цикл for и в случае успешного чтения сделать break.
    2. Что у тебя хранится в это файле, php-код? Я, просто, никогда не юзал var_export. Для удобного хранения данных использую serialize/unserialize и file_put/get_contents.
    3. В чем собственно проблема? Оказывается пустой файл?

  19. # Тормоз

    Про рекурсию мне уже говорили в прошлый раз, два раза даже. Я согласен, что не лучший вариант, рекурсия там скорее просто потому что мне хотелось написать первую в жизни рекурсивную функцию :) Теперь узнал, что цикл лучше, но может ли быть причина сбросов в рекурсивной функции? Если считаешь, что да, объясни, как и почему это возможно.

    В файле действительно хранится PHP-код. Serialize/unserialize не оправдали возлагаемого на них доверия, много было глюков, в частности если кавычка в данные попадает. То есть нужно делать всякие дополнительные обработки, а зачем, если можно решить махом все проблемы с сериализацией, отказавшись от неё полностью? +Тогда файлы с данными можно просматривать и редактировать.

    Проблема в том, что исчезают данные из файла. Он сам не становится пустым, обнуляется массив с данными.

  20. # polonskiy: 

    Не пойму что это: global ${$name};
    ты эту переменную делаешь глобальной? Смысл не ясен, т.к. ты и так передаешь ее include_flock($name, $count). Может у тебя она существует вне функции и таким образом перезаписывается? Что бы сделал я:
    1. заменил рекурсию циклом;
    2. вместо кучи костылей использовал бы tmpfile();
    Код бы стал в 2 раза меньше. Меньше кода – меньше шансов спрятаться багу :)
    Можешь привести пример данных, содержащихся в файле? Т.е. что содержится здесь $data = «<?php \$$type = $data;»?

  21. # Jungle

    до сих пор не решена проблема :), попробуй так :
    1 способ, основан на cron‘е, делаешь папочку (очередь) туда записываешь файлы, по команде cron‘ом читаешь все файлы в папке (очереди) и заносишь в главный файл все изменения. т.е. получается 1 writer.

    2 способ, основан на создании демона. создаешь демона, вешаешь его на определенный порт и когда надо записать файл спрашиваешь у демона, а можно ли записать или ещё кто-то пишет, или кидаешь демону задание он пишет в файл изменения, т.е. получается 1 writer.

    вроде всё ;)

    в строке
    24 rename($file, $tmpFile);
    у тебя нет проверки на успешность переименовки отсюда и проблема (другой процесс уже переименовал его)
    хотя переимновка не решит твою проблему, как узнать что файл уже переименован?

  22. # Тормоз

    Polonskiy, в PHP просто не видны глобальные переменные внутри функций, если не поставить перед именем global. Она, естественно, существует вне функции.

    Про tmpfile() не знал, спасибо! Пример файла можно скачать.

    Но переписывать наугад всё я не хочу. Повторяю, я хочу понять, где именно происходит сброс, почему точно это происходит.

    Иначе так можно бесконечно переписывать, меня и так уже матерят, наверно, из-за постоянных обновлений.

    Jungle, твои варианты вообще никуда не годятся. CRON, демоны. Может ещё вообще всё на Ассемблере или Си написать и продавать рекламный модуль к PHP? :)

  23. # Тормоз

    А про 24 строку ты не прав. Как другой процесс мог переименовать файл? Откуда этот другой процесс про $tmpFile узнает?

  24. # Jungle

    ой точнее 18 строка, перепутал
    ну вам батенька вообще не угодишь, изобретайте велосипед дальше

    ну нету атомарной функции в PHP для файла

  25. # Тормоз

    И каждый вместо решения задачки стремится избежать её решения :) Относись к этому как к логической игре. Что происходит, почему и в каком моменте?

    ОК, 18 строка. Допустим даже, что к моменту rename файла нет. И что? Всё равно данные запишутся во временный файл и после временный переименуется в stat.php.

  26. # samlowry

    spectator.ru/technol…

    Не благодари меня!

  27. # Тормоз

    А не за что. Я это читал полгода назад. А вот ты не посмотрел на реализацию моих функций. Используется как раз rename.

  28. # Jungle

    да, точно, хотя не понятно как ты обновляешь данные?
    и потом 24 строка если 2 процесса переименуют друг за другом файлы, то значения одного из них потеряются

    значит ошибка где-то в другом месте

  29. # polonskiy: 

    Я знаю, что переменные не видны вне функций :) В том-то и дело, что в данном случае нет надобности делать переменную $name глобальной. Смотри: ф-ции include_flock передается имя файла, т.к. ты объявил $name глобально, то при рекурсивном вызове include_flock (строка 63) ты перезапишешь (??? я не уверен) $name во всем скрипте (т.е. и за пределами функции).
    Почему ты не закрываешь пхп-код? т.е. $data = «<?php \$$type = $data;»; здесь не должно быть ?> в конце?
    Зачем записывать имя массива? Обычно делается так: file_put_content(‘file.php’, ‘<?php return(’. var_export($array, true) .’); ?>’);
    Потом получаем так: $array = include(‘file.php’);
    Можно юзать serialize, можно XML (тоже удобно редактировать).

  30. # Тормоз

    Jungle, данные в глобальной переменной, так что они в коде и обновляются. Сброс там крайне-крайне маловероятен, не буду же я делать unset всему массиву. А примерно это и происходит почему-то.

    Как это всё устроено, можешь посмотреть в AvisoDaos, там код такой же, просто удалены некоторые куски (в которых, опять же, не может быть такого сбоя).

    Polonskiy, можешь пояснить, чем именно перепишется $name при рекурсивной функции? Все остальные замечания несущественные, к проблеме точно не относятся.

    Кстати, замыкающая фигурная скобка не обязательна, а в Zend даже запрещён. Проще говоря, она не нужна.

  31. # samlowry

    Тормоз: у тебя проблема вынесена в ф-цию (или в две). Ты пишешь данные в связке «ключ+данные», передавая их в ф-цию. Тут же ты утверждаешь, что под sqlite надо много переписывать. Для конкретного этого случая элементарно пофиксить, записывая всё это в sqlite.

  32. # Тормоз

    Мы обсуждаем здесь конкретную задачу, а не методы её обхода. Мы здесь не лечим головную боль отрубанием головы.

  33. # Тормоз

    Тем более, что работа с данными внутри Daos далеко не так тривиальна, как тебе кажется, перевести это всё на СУБД далеко не «элементарно». SQLite и MySQL в нынешней версии в любом случае не будет.

  34. # polonskiy: 

    Ты сказал что обнуляется массив с данными. В файл записывается пустой массив? Может в ф-цию vw приходит уже пустой масиив?

  35. # Тормоз

    Да не факт, что туда пустой массив записывается. Возможно, файл просто корёжится, и потому создаётся новый, но данные ему брать неоткуда.

    В vw скорей всего пустой и приходит, но не потому что где-то там внутри он обнулился, просто после include_flock правильных данных не было.

    Парни, я понимаю ваши подозрения о том, что сброс может быть на других участках кода, а не в этих функциях. И на 100% утверждать не буду, но на 99% я уверен.

    Хотя бы потому, что после первого обновления, связанного с этой проблемой, сбросы стали происходить гораздо реже.

  36. # Jungle

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

  37. # polonskiy: 

    Я так понимаю, что это происходит иногда, но воспроизвести ситуацию ты не можешь. Верно?

  38. # Тормоз

    Jungle, а это может быть зацепочкой! Буду ещё думать.

    Polonsky, конечно, иначе я бы давно уже нашёл причину.

  39. # Тормоз

    У меня вообще только один раз был сброс за весь год.

  40. # Jungle

    надо еще делать блокировку на rename, пока данные читаются

  41. # Тормоз

    Спасибо. Сейчас попробую. А ещё попробую сделать мощный локальный тест, чтобы он хотя бы за 10 минут вызывал сброс.

  42. # NameOrNickname: 

    Хм, NoSql ?

  43. # Alek$

    Хм. Есть у меня одна идея на тему блокировок в PHP. Сегодня постараюсь успеть проверить ее жизнеспособность.

  44. # Миша: 

    Тормоз, не читал комменты, извини. Но ты серьезно должен, обязан заюзать СУБД. Решение которые ты предоставляешь уже выросло из штанишек, если люди хотят ставить его на высоконагруженные проекты то файлы ну никак не подходят. Разделяемая память, мемкеш, базы данных, ключ-значение хранилище, но не файлы.
    Простота установки это иллюзия. Для простоты установки существуют проверки системных требований и системы автоматического развертывания. А для всех остальных будет сервис.
    Флоки не всегда работают и не везде, ничего у тебя не получится, хватит тратить время, понял? Проект из штанишек вырос а ты все еще за них держишься, давай уже, снимай штаны блеать, будь мужиком!

  45. # Alek$

    Миша, зря ты комменты не читал.
    Про СУБД Тормозу уже успели только раз посоветовать, что я бы на его месте слово СУБД объявил матерным =)

  46. # Миша: 

    @Alek$: я понимаю, но он должен понять что другого решения не будет. не будет. не будет и точка. разве я не прав? или возрастает нагрузка изза сложных алгоритмов, когда каждый чих в свои файлы, или пишется свое подобие хранилища, или используется хранилище. помоему тут уже даже чинка вычинки не стоит, не то что непрактично…

  47. # Миша: 

    Опять таки, коменты не читал, но можно было бы задействовать еще один файл для лока
    Тоесть, грубо говоря, создавать пустой файл и его присутствие уже служило бы признаком лока, если бы в PHP были какие-то более менее атомарные функции для этого, но со всеми этими zend_parse_parameters увы
    А использовать system ты врядли захочешь, доступ к нему закрыт, наверное, чаще чем отсутствует БД
    Хотя можно было б touch & unlink попробовать, но я бы так не делал
    Короче, хватит биться головой о стену, да

  48. # Jungle

    думаю это решение твоей проблемы
    seodiver.ru/2010/10/…

    точно сказать не могу поможет или нет, но попробуй потестить вдруг поможет ;)

  49. # Миша: 

    не поможет

  50. # Jungle

    @Миша проверял?

  51. # Тормоз

    Jungle, ну вот, у тебя там премодерация зачем-то. В общем, тестить лучше своё решение, и я сейчас не могу выразить, что именно не нравится в твоём.

    Миша, давай с аргументами.

  52. # Jungle

    Тормоз, давая я напишу модифицированную версию твоих функций чтений и записи, а ты просто заменишь и протестируешь.

  53. # Тормоз

    Ладно. Только ты скачай AvisoDaos и проверь свои функции по крайней мере на работоспособность.

  54. # Jungle

    ок, как напишу сразу, отпишу здесь

  55. # Jungle

    вот написал и протестировал seodiver.ru/2010/10/…

    а твой код должен так выглядеть pastie.org/1216327 вырезка функций

  56. # Тормоз

    А я сейчас как раз свой вариант продумываю, перечёркиваю одно решение за другим.

  57. # Jungle

    мой метод работает, бери на вооружение ;)

  58. # Тормоз

    Спасибо. Мой тоже! На время сна запускал тест — «ни единого разрыва!!!» :) Классно. Твой потестирую позже, но почему-то заранее уверен, что у него время выполнения больше будет. Проверим.

  59. # Jungle

    опиши свой метод, интересно знать какой он. как сравнишь оба метода дай знать, ну очень интересно каковы результаты :)

  60. # Тормоз

    У меня теперь создаётся и удаляется блокирующий пустой микрофайлик для каждой операции чтения или записи. Вот так сейчас — http://pastie.org/1217232
    Я ещё отдельно в блоге напишу про этот рефакторинг.

  61. # Jungle

    так у тебя получается что только 1 читает или пишет, а надо только 1 может писать или N могут читать если нет записи.

  62. # Тормоз

    Нет, не надо. Тогда статистика просмотров будет неточной.

  63. # Alek$

    Наконец довел до ума метод, о котором упоминал выше. Кто-то назовет решение извращением, но ведь так оно и есть ;-)

  64. # Тормоз

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

  65. # Alek$

    На самом деле это очень удобная и используемая технология.
    Например, простую авторизацию с их помощью делать – милое дело.

  66. # Тормоз

    Почитаю доки, спасибо.

Комментирование этой статьи закрыто.

Интересное Покупки ТехникаРазное Отдых Статьи Строительство Услуги Общество Хобби Культура Советы Уют