Comments 55
Вопрос на засыпку. Что будет с файлом, если Вы сделали flock, пишите в этот файл, и тут произошла исключительная ситуация, например из-за того, что на диске нет места?
Понимаете, всему свое время и место под Солнцем. Если возможна множественная конкурентная запись в один ресурс из нескольких потоков, то нужно использовать предназначенные для этого инструменты. В идеале - базу данных с поддержкой транзакций. Ведь файловая система сама по себе не предназначена для таких задач, поэтому более правильно было бы, чтобы запись делалась из одного процесса-менеджера, как это делается в случае с базой данных.
Про творчество Котерова: для начинающих - неплохо, такую литературу тоже можно использовать для одноразового прочтения, но спек это не заменит, и я всем рекомендую изучать технологии и языки программирования по родным спекам, туториалам, и профильным статьям от разработчиков и признанных авторитетных экспертов.
Минус Вам я ставить не буду. Так как Вы еще не разобрались, к сожалению. Потом разберетесь, мне спасибо скажете.
Понимаете, всему свое время и место под Солнцем. Если возможна множественная конкурентная запись в один ресурс из нескольких потоков, то нужно использовать предназначенные для этого инструменты. В идеале - базу данных с поддержкой транзакций. Ведь файловая система сама по себе не предназначена для таких задач, поэтому более правильно было бы, чтобы запись делалась из одного процесса-менеджера, как это делается в случае с базой данных.
Про творчество Котерова: для начинающих - неплохо, такую литературу тоже можно использовать для одноразового прочтения, но спек это не заменит, и я всем рекомендую изучать технологии и языки программирования по родным спекам, туториалам, и профильным статьям от разработчиков и признанных авторитетных экспертов.
Минус Вам я ставить не буду. Так как Вы еще не разобрались, к сожалению. Потом разберетесь, мне спасибо скажете.
Прежде чем обвинять меня в незнании азов, сначала ответьте пожалуйста на вопрос, который я задал выше. Что будет с файлом, если Вы сделали flock, пишите в этот файл, и тут произошла исключительная ситуация и Ваш скрипт прервал работу?
Далее. Я не говорил что файловая система не предназначена для хранения. С чего Вы такое взяли? Как раз наоборот, файловая система предназначена исключительно для хранения файлов, но не более чем. Я лишь утверждаю, что файловая система сама по себе не предназначена для безопасного управления файлами при условии конкурентного доступа на запись к одним и тем же файлам со стороны разных процессов. Она слишком низкоуровнева для этого, по определению.
Далее. Я не говорил что файловая система не предназначена для хранения. С чего Вы такое взяли? Как раз наоборот, файловая система предназначена исключительно для хранения файлов, но не более чем. Я лишь утверждаю, что файловая система сама по себе не предназначена для безопасного управления файлами при условии конкурентного доступа на запись к одним и тем же файлам со стороны разных процессов. Она слишком низкоуровнева для этого, по определению.
В моем случае, если нет свободного места, то новый конфиг создан не будет, а старый конфиг останется целым. В Вашем же случае конфиг станет файлом нулевой длины. Не верите - проверьте.
Но вообще, случай с местом на диске - это лишь один вариант исключительной ситуации из десятков. Понимаете, при любой исключительной ситуации в процессе записи в Вашем случае, например, если скрипт просто прервет работу при записи, будет тот же самый плачевный результат - получится файл нулевой длины. В моем же случае старый конфиг останется цел.
База данных. Если нет места на диске, может вести себя по-разному, но данные однозначно не будут потеряны, а в Вашем - будут. Заметьте, я не призываю использовать именно базу данных, но, если нужна надежность, при условии конкурентного доступа использовать ее однозначно предпочтительнее, чем Ваше решение.
Пожалуйста, все же пробуйте рассмотреть ситуацию более глубоко, и не спешите писать обвинения в невежестве, я все это прошел уже лет 15 назад, и тоже в свое время допускал те же самые ошибки.
Но вообще, случай с местом на диске - это лишь один вариант исключительной ситуации из десятков. Понимаете, при любой исключительной ситуации в процессе записи в Вашем случае, например, если скрипт просто прервет работу при записи, будет тот же самый плачевный результат - получится файл нулевой длины. В моем же случае старый конфиг останется цел.
База данных. Если нет места на диске, может вести себя по-разному, но данные однозначно не будут потеряны, а в Вашем - будут. Заметьте, я не призываю использовать именно базу данных, но, если нужна надежность, при условии конкурентного доступа использовать ее однозначно предпочтительнее, чем Ваше решение.
Пожалуйста, все же пробуйте рассмотреть ситуацию более глубоко, и не спешите писать обвинения в невежестве, я все это прошел уже лет 15 назад, и тоже в свое время допускал те же самые ошибки.
послушайте, вы говорите про исключительную ситуацию как про конец света. ее МОЖНО обработать и написать СВОЮ логику по срочному принятию мер вплоть до внешнего логирования, если уж совсем паранойа замучила.
Написать-то свою логику для обработки некоторых исключительных ситуации можно. Но увы, не всегда эта логика может работать. Например, какой логикой сможете защититься от смерти скрипта, например, при выключении питания компьютера?
Пример. Открыли файл на запись, начали в него писать, и тут вдруг скрипт умер. Что случится с файлом? Очень большая вероятность, то он будет или нулевого размера, или вообще запорчен. А если это был конфиг, то работать проект уже не сможет, конфиг-то запорчен.
Согласны?
Пример. Открыли файл на запись, начали в него писать, и тут вдруг скрипт умер. Что случится с файлом? Очень большая вероятность, то он будет или нулевого размера, или вообще запорчен. А если это был конфиг, то работать проект уже не сможет, конфиг-то запорчен.
Согласны?
читайте мой комментарий (копия ниже):
знаете, все уже давно украдено до вас. ПЕРЕД тем, как записать в старый файл переименовываете его с расширением bak (слышали про такое?), и дальше работаете с новым файлом, с корректным именем. ну, а если настал конец света - старый файл всегда под рукой.
а теперь объясните, чем ваша эпопея с временными файлами, копированием, переименовками лучше?
скучно, ей богу.
1) в случае выключения питания нет вообще никакой гарантии, что бы вы не делали абстрактная ОС не обязана вносить изменения на диск тут же
2) я сделаю КОПИЮ конфига перед тем, как вносить какие-то изменения, а если уж случилось страшное, то постараюсь восстановить конфигурацию из бэкапа или буду использовать настройки по умолчанию
3) Не согласен (см выше)
знаете, все уже давно украдено до вас. ПЕРЕД тем, как записать в старый файл переименовываете его с расширением bak (слышали про такое?), и дальше работаете с новым файлом, с корректным именем. ну, а если настал конец света - старый файл всегда под рукой.
а теперь объясните, чем ваша эпопея с временными файлами, копированием, переименовками лучше?
скучно, ей богу.
1) в случае выключения питания нет вообще никакой гарантии, что бы вы не делали абстрактная ОС не обязана вносить изменения на диск тут же
2) я сделаю КОПИЮ конфига перед тем, как вносить какие-то изменения, а если уж случилось страшное, то постараюсь восстановить конфигурацию из бэкапа или буду использовать настройки по умолчанию
3) Не согласен (см выше)
В своей статья я умышленно не затронул вопрос копий (bak). Это обычно само собой подразумевается, причем, часто делается не одна копия, а несколько, с ротацией версий. Но к теме это отношения не имеет.
Повторюсь. Делать копию - недостаточно. Нужно еще и минимизировать возможное появление вероятных проблем. Если Вы сделали копию, и пишите прямо в файл, и скрипт умер по сигналу, то что будет с файлом? Он может быть нулевым, или записанным не полностью. Вам придется добавлять довольно сложную логику по проверке целостности этого файла при каждом обращении к нему, возможно, писать дополнительный скрипт для автоматического восстановления из bak-копии (а то система будет полностью неработоспособна все то время, пока, например, конфиг запорчен), и т.д. Думайте, пожалуйста.
Повторюсь. Делать копию - недостаточно. Нужно еще и минимизировать возможное появление вероятных проблем. Если Вы сделали копию, и пишите прямо в файл, и скрипт умер по сигналу, то что будет с файлом? Он может быть нулевым, или записанным не полностью. Вам придется добавлять довольно сложную логику по проверке целостности этого файла при каждом обращении к нему, возможно, писать дополнительный скрипт для автоматического восстановления из bak-копии (а то система будет полностью неработоспособна все то время, пока, например, конфиг запорчен), и т.д. Думайте, пожалуйста.
полностью поддерживаю kkamkou
автор, похоже, слабо представляет, о чем пишет
автор, похоже, слабо представляет, о чем пишет
В моём понимании, автор говорит о реализации некоторой транзакционности при сохранении в файл, чтобы эту "транзакцию" можно было отменить, если что не так. А вы говорите об отдельной теме блокировке и множественном доступе. При желании (необходимости), что мешает совместить и то, и другое?
Кстати, я не совсем согласен с автором имхо, это не единственный метод, и не всегда нужно делать именно так, но задуматься на тему ошибок при сохранении не помешает.
Ещё вопрос, а не будет ли при такой реализации проблем с правами доступа у файлов?
Кстати, я не совсем согласен с автором имхо, это не единственный метод, и не всегда нужно делать именно так, но задуматься на тему ошибок при сохранении не помешает.
Ещё вопрос, а не будет ли при такой реализации проблем с правами доступа у файлов?
Да, это конечно же не единственный способ. Это одна из простейших альтернатив прямой записи в файл, но в то же время более безопасная.
Про права доступа. При переименовании идет, по сути, затирание старого файла новым, поэтому, при создании нового файла нужно ему давать те же пермиссии, что и старому (конечно, помимо всего прочего, право на запись должно быть обязательно, как и в случае, если нужно просто писать в файл напрямую, старым способом). Тогда при переименовании проблем с правами доступа не будет.
Про права доступа. При переименовании идет, по сути, затирание старого файла новым, поэтому, при создании нового файла нужно ему давать те же пермиссии, что и старому (конечно, помимо всего прочего, право на запись должно быть обязательно, как и в случае, если нужно просто писать в файл напрямую, старым способом). Тогда при переименовании проблем с правами доступа не будет.
Я об этом и говорю, то есть предложенный вами метод должен быть чуть более сложным нужно ещё скопировать права доступа. Кроме того, если я ничего не путаю, бывают ещё права у директории на создание-удаление файлов, то есть в некоторых ситуациях могут возникнуть отдельные грабли.
Большинство языков программирования включают в себя функции или методы, которые сразу позволяют задавать пермиссии при создании нового файла. Но в статья я это не упомянул, так как не везде это нужно делать (например, для Windows Desktop applications).
Про права директории на создание-удаление файлов. Если Вы имеете в виду Unix/Linux, то там все устроено так. Если права директории не позволяют создавать в ней файлы, но один из файлов имеет права на запись, то можно в другой, временной директории создать новый файл, затем переименовать его в ту закрытую для создания файлов директорию, заменив старый. То есть проблем в нашем случае - не будет, это и теоретически так, и на практике проверено неоднократно.
Про права директории на создание-удаление файлов. Если Вы имеете в виду Unix/Linux, то там все устроено так. Если права директории не позволяют создавать в ней файлы, но один из файлов имеет права на запись, то можно в другой, временной директории создать новый файл, затем переименовать его в ту закрытую для создания файлов директорию, заменив старый. То есть проблем в нашем случае - не будет, это и теоретически так, и на практике проверено неоднократно.
Сдаюсь, далеко мне до вашего кунг-фу :)
cat rename.c:
#include
#include
#include
int main(int argc, char** argv) {
if (rename("a", "b/c")) {
perror("main");
return 1;
} else {
return 0;
}
}
$ ls -ld b
dr-xr-xr-x 2 oleg users 512 14 июл 19:54 b
$ ls -l b
total 0
-rw-r--r-- 1 oleg users 0 14 июл 19:54 c
$ ls -l a
-rw-r--r-- 1 oleg users 0 14 июл 19:54 a
$ ./rename
main: Permission denied
$ ls -ld b
drwxr-xr-x 2 oleg users 512 14 июл 19:56 b
$ ./rename
$
Бред рассказываете, однако.
#include
#include
#include
int main(int argc, char** argv) {
if (rename("a", "b/c")) {
perror("main");
return 1;
} else {
return 0;
}
}
$ ls -ld b
dr-xr-xr-x 2 oleg users 512 14 июл 19:54 b
$ ls -l b
total 0
-rw-r--r-- 1 oleg users 0 14 июл 19:54 c
$ ls -l a
-rw-r--r-- 1 oleg users 0 14 июл 19:54 a
$ ./rename
main: Permission denied
$ ls -ld b
drwxr-xr-x 2 oleg users 512 14 июл 19:56 b
$ ./rename
$
Бред рассказываете, однако.
Спасибо, подловили! Но я все же попробую доказать свою точку зрения.
Все дело в термине "переименование". Функция rename, которую Вы используете, работает так - удаляет предыдущий файл, и заменяет новым. Это функция низкоуровневая, и используя только ее действительно невозможно переименовать файл в директорию, пермиссии которой не дают возможности создавать новые файлы.
Но есть и другие способы переименования (точнее сказать, переноса).
Это более сложные способы, при которых используется rename, и, если rename сделать невозможно, то производится перекачка данных из файла-источника в файл в закрытой директории, с последующим удалением файла-источника.
Как пример, так работает функция move модуля File::Copy языка Perl. Вот, показываю:
$ ls -l a
-rw-r--r-- 1 tvv tvv 1 Jul 14 19:33 a
$ ls -ld b
dr-xr-xr-x 2 tvv tvv 4096 Jul 14 19:27 b
$ ls -l b/c
-rw-r--r-- 1 tvv tvv 0 Jul 14 19:27 b/c
$ perl -e "use File::Copy; move('a','b/c')"
$ ls -l a
ls: a: No such file or directory
$ ls -l b/c
-rw-r--r-- 1 tvv tvv 1 Jul 14 19:33 b/c
А вот слово "бред" тут IMHO неуместно. Единственная моя ошибка, точнее, опечатка, что я использовал слово "переименование" вместо сходного слова "перенос". Тактичнее, тактичнее...
Все дело в термине "переименование". Функция rename, которую Вы используете, работает так - удаляет предыдущий файл, и заменяет новым. Это функция низкоуровневая, и используя только ее действительно невозможно переименовать файл в директорию, пермиссии которой не дают возможности создавать новые файлы.
Но есть и другие способы переименования (точнее сказать, переноса).
Это более сложные способы, при которых используется rename, и, если rename сделать невозможно, то производится перекачка данных из файла-источника в файл в закрытой директории, с последующим удалением файла-источника.
Как пример, так работает функция move модуля File::Copy языка Perl. Вот, показываю:
$ ls -l a
-rw-r--r-- 1 tvv tvv 1 Jul 14 19:33 a
$ ls -ld b
dr-xr-xr-x 2 tvv tvv 4096 Jul 14 19:27 b
$ ls -l b/c
-rw-r--r-- 1 tvv tvv 0 Jul 14 19:27 b/c
$ perl -e "use File::Copy; move('a','b/c')"
$ ls -l a
ls: a: No such file or directory
$ ls -l b/c
-rw-r--r-- 1 tvv tvv 1 Jul 14 19:33 b/c
А вот слово "бред" тут IMHO неуместно. Единственная моя ошибка, точнее, опечатка, что я использовал слово "переименование" вместо сходного слова "перенос". Тактичнее, тактичнее...
Угу, только вот rename() - операция атомарная, которая либо целиком успешна, либо целиком неуспешна, а вот копирование данных - нет. И в случае копирования данных мы наступаем на те самые грабли, которых вы боялись (кончившееся невовремя место на диске, прибитый по сигналу скрипт, и т.п.).
Я так и знал что Вы это напишите. И не могу не согласиться, Вы правы.
Именно поэтому не стоит лишать пермиссий на создание файлов директорию, в которой расположен файл, который нужно изменять.
Впрочем, все равно такое сохранение копии + "неатомарное" перемещение лучше, чем просто прямая запись в файл.
Ведь при прямой записи в файл в случае исключительной ситуации мы теряем данные навсегда, а при копировании из копии при исключительной ситуации как минимум остается эта сама копия, из которой файл может быть потом восстановлен. То есть это все равно надежнее.
Но в любом случае, не поймите меня неправильно, хоть этот способ с пермиссиями и надежнее, это все же частный случай, которого желательно избегать.
Именно поэтому не стоит лишать пермиссий на создание файлов директорию, в которой расположен файл, который нужно изменять.
Впрочем, все равно такое сохранение копии + "неатомарное" перемещение лучше, чем просто прямая запись в файл.
Ведь при прямой записи в файл в случае исключительной ситуации мы теряем данные навсегда, а при копировании из копии при исключительной ситуации как минимум остается эта сама копия, из которой файл может быть потом восстановлен. То есть это все равно надежнее.
Но в любом случае, не поймите меня неправильно, хоть этот способ с пермиссиями и надежнее, это все же частный случай, которого желательно избегать.
Видите ли, то, что вы предлагаете - это называется "дырявая абстракция" (хоть Спольски и пишет иногда бред, но некоторые статьи у него великолепны). Она не выдерживает столкновения с реальностью. Зайдите в свой /etc, и вы там наверняка обнаружите кучу файлов, которые можно изменять (за счет g+w), но нельзя удалить. Это я еще не упомянул про хардлинки и всякие kqueue/epoll, которыми не особо умные авторы демонов любят отлавливать изменения в своих конфигах для их последующей перезагрузки. Но насчет резервных копий вы почти правы, только вот rename() использовать ни к чему - inode number файлов менять без крайней нужды ну совершенно не следует - многие вещи мгновенно поломаются.
Привет, Вадим. Я думаю тебе стоит добавить секцию "Update" к топику и вынести в него упомянутые нюансы.
Думаю, стоит даже блокировку упомянуть (хоть она действительно напрямую к теме не относится), для новичков — чтобы они про неё помнили, и не пытались блокировку ставить на изменяемый или временный файл, поленившись для этого заводить отдельный файл.
Из того, что приходит в голову, забыли пока упомянуть разве что возможные проблемы с "атомарным rename" между разными подмонтированными FS.
Что касается конкретно конфиг-файлов, то лично мне нравится подход с конфиг-директорией, где каждая отдельная опция находится в отдельном файле. Стиль ./env/ в софте djb, например, или каталогов /etc/*.d/ в некоторых дистрибутивах. Это не отменяет необходимости в использовании описанного в топике подхода, но очень упрощает жизнь при необходимости изменить одну опцию независимо от других, упрощает выполнение таких операций в sh-скриптах, уменьшает вероятность конфликтов при одновременном изменении опций разными процессами.
Думаю, стоит даже блокировку упомянуть (хоть она действительно напрямую к теме не относится), для новичков — чтобы они про неё помнили, и не пытались блокировку ставить на изменяемый или временный файл, поленившись для этого заводить отдельный файл.
Из того, что приходит в голову, забыли пока упомянуть разве что возможные проблемы с "атомарным rename" между разными подмонтированными FS.
Что касается конкретно конфиг-файлов, то лично мне нравится подход с конфиг-директорией, где каждая отдельная опция находится в отдельном файле. Стиль ./env/ в софте djb, например, или каталогов /etc/*.d/ в некоторых дистрибутивах. Это не отменяет необходимости в использовании описанного в топике подхода, но очень упрощает жизнь при необходимости изменить одну опцию независимо от других, упрощает выполнение таких операций в sh-скриптах, уменьшает вероятность конфликтов при одновременном изменении опций разными процессами.
Читайте как работает flock на нелокальных FS, например на gfs
в целом то всё верно, но... Невозможность сохранить конфиг для некоторых приложений означает, что работать нормально уже не получится. И от себя я бы ещё добавил, что не помешает делать обработку ошибок при загрузке конфига.
Мое мнение - если есть конфигурационный файл, и много процессов его могут изменять одновременно, то в этом случае лучше вообще отказаться от использования конфигурационного файла в пользу базы данных, или другого специализированного механизма.
Описанный мной в статье пример в первую очередь актуален для систем, которые пишут в файл только из одного процесса, например, Desktop applications.
Описанный мной в статье пример в первую очередь актуален для систем, которые пишут в файл только из одного процесса, например, Desktop applications.
Через реестр вы добьетесь синхрнизации на уровне отдельных полей, но не на уровне конфигурации своего приложения в целом. Так что реестр - только для совсем простых случаев.
мдя... так правильно, говорите? а я вот готов поспорить. более это просто ужасно - то, о чем вы пишете. и у меня создается впечателение, что это вам стоит заняться самообразованием по крайней мере, а не аудировать чужой код.
Голословное утверждение. Аргументируйте, пожалуйста. В чем я не прав? Какой Ваш вариант, какие аргументы?
знаете, все уже давно украдено до вас. ПЕРЕД тем, как записать в старый файл переименовываете его с расширением bak (слышали про такое?), и дальше работаете с новым файлом, с корректным именем. ну, а если настал конец света - старый файл всегда под рукой.
а теперь объясните, чем ваша эпопея с временными файлами, копированием, переименовками лучше?
а теперь объясните, чем ваша эпопея с временными файлами, копированием, переименовками лучше?
Если Вы планируете вручную делать восстановление после возможного сбоя, то вариант с bak достаточен. Но если Вы хотите минимизировать возможность сбоя, то этот способ не является достаточным.
В своей практике я постоянно использую bak файлы, но данная статья была не об этом, а о минимизации ситуаций, когда возможна порча файла, в который делается запись. bak-файл не помогает в этом никак, к сожалению.
В своей практике я постоянно использую bak файлы, но данная статья была не об этом, а о минимизации ситуаций, когда возможна порча файла, в который делается запись. bak-файл не помогает в этом никак, к сожалению.
мамадарагайа,
а объясните, как выбранная стратегия может повлиять на вероятность сбоя? ваш алгоритм обеспечит более надежное функционирование электростанций и долгую жизнь винту?
а объясните, как выбранная стратегия может повлиять на вероятность сбоя? ваш алгоритм обеспечит более надежное функционирование электростанций и долгую жизнь винту?
Моя цель - минимизировать возможность сбоя программы, который может привести к ее полной неработоспособности. Надеюсь, это понятно?
ну, возможно я вас просто не понимаю.
не дорос, возможно. только мне все кажется, что программа должна болше восстанавливаться после сбоя, чем не допускать его (в разумных пределах, разумеется). неужели вы не понимаете, что свой вклад в порчу данных вносит не только ваше приложение? и не факт, что это будет оно, которое напакостит.
не дорос, возможно. только мне все кажется, что программа должна болше восстанавливаться после сбоя, чем не допускать его (в разумных пределах, разумеется). неужели вы не понимаете, что свой вклад в порчу данных вносит не только ваше приложение? и не факт, что это будет оно, которое напакостит.
Программа действительно должна включать в себя логику восстановления после сбоя в работе, с этим никто не спорит. Но минимизировать возможность потери (пусть и временной) своих данных, особенно конфигов, не менее важно.
Приведу пример. Скажем, у Вас есть скрипт, который при каждом реквесте обращается к конфигу на чтение, то есть очень-очень часто. Один из процессов иногда, сравнительно редко, вносит в этот конфиг какие-то изменения. Допустим, при очередном изменении конфига, приозошел сбой, и в конфиге оказались не все данные, которые там должны быть. Получается, пока не произойдет восстановления из бакапа (а в данном случае нужно будет еще и распознать, что в конфиге не все что нужно), скрипт будет работать неправильно с запорченным конфигом, что может привести к печальным последствиям.
Вот как раз описанный мной способ и минимизирует возможность порчи изменяемого файла. Соответственно, система работает более устойчиво. А восстановление после сбоя - это отдельная логика, не является частью данной статьи, так как речь сейчас не об этом.
Ведь согласитесь, что лучше не восстанавливать свою систему после сбоя, а просто запустить ее чтобы она продолжила работу как ни в чем ни бывало, если это конечно возможно, и сбой не фатальный.
Приведу пример. Скажем, у Вас есть скрипт, который при каждом реквесте обращается к конфигу на чтение, то есть очень-очень часто. Один из процессов иногда, сравнительно редко, вносит в этот конфиг какие-то изменения. Допустим, при очередном изменении конфига, приозошел сбой, и в конфиге оказались не все данные, которые там должны быть. Получается, пока не произойдет восстановления из бакапа (а в данном случае нужно будет еще и распознать, что в конфиге не все что нужно), скрипт будет работать неправильно с запорченным конфигом, что может привести к печальным последствиям.
Вот как раз описанный мной способ и минимизирует возможность порчи изменяемого файла. Соответственно, система работает более устойчиво. А восстановление после сбоя - это отдельная логика, не является частью данной статьи, так как речь сейчас не об этом.
Ведь согласитесь, что лучше не восстанавливать свою систему после сбоя, а просто запустить ее чтобы она продолжила работу как ни в чем ни бывало, если это конечно возможно, и сбой не фатальный.
извините, мы друг друга не понимаем. нет смысла дальше продолжать.
Ну что же, жаль что не поняли. И еще более жаль читать фразы типа Вашей: "автор, похоже, слабо представляет, о чем пишет", которые некоторые сначала пишут, а потом начинают разбираться в вопросе.
Хотя тут еще возможно, что я, будучи начинающим автором, недостаточно четко изложил свою мысль, и тем, кто статью прочитал, было не все интуитивно понятно. То есть мне в есть над чем работать... Ну ладно, надеюсь, может быть хоть кому-то данная статья оказалась полезной.
Хотя тут еще возможно, что я, будучи начинающим автором, недостаточно четко изложил свою мысль, и тем, кто статью прочитал, было не все интуитивно понятно. То есть мне в есть над чем работать... Ну ладно, надеюсь, может быть хоть кому-то данная статья оказалась полезной.
Вадим, прочитав комментарии я совсем запутался.
1. Сначала создаётся временный файл и в него пишутся все данные
2. Затем если исключительные ситуации не произошли, то временный файл переименовывается и заменяет собой основной.
Я не достаточно знаком с поведением FS в таких ситуациях, поэтому интересуюсь…
В случае сбоя на втором этапе наш основной файл остаётся в любом случае?
Что будет с временным файлом? Он сохранится?
1. Сначала создаётся временный файл и в него пишутся все данные
2. Затем если исключительные ситуации не произошли, то временный файл переименовывается и заменяет собой основной.
Я не достаточно знаком с поведением FS в таких ситуациях, поэтому интересуюсь…
В случае сбоя на втором этапе наш основной файл остаётся в любом случае?
Что будет с временным файлом? Он сохранится?
В случае сбоя при переименовании останется или основной файл, или временный, или оба останутся. Даже наблюдал такое пару раз, когда проводил стресс-тестирование. Это действительно очень зависит от FS. Но это довольно быстрая операция, и шанс того, что произойдет исключительная ситуация, небольшой.
Хотя, гипотетически могу себе представить, что может сложиться ситуация и оба файла пропадут. Я не исключаю такой возможности, но это уже будет скорее проблема неустойчивой работы FS, причем маловероятная. Я такого ни разу не встречал на практике.
На втором этапе также возможен сбой, но при этом риск того, что не останется ни основного файла, ни временного — крайне минимален. Останется или основной, или временный, или оба останутся. Это действительно очень зависит от FS.
Ваш подход защищает от ошибок функции записи файла (атомарной для интерпретатора), заменой её на функцию переименования файла (атомарную для ОС).
Но так ли велика разница для 20 килобайтного файла конфига, который перезаписывается, ну, может 10 000 раз за время жизни проекта. Какова вероятность, что один подход сработает, и не сработает другой.
База данных даёт гораздо больше гарантий, если вам действительно нужны гарантии. SQLite весит килобайт под 300.
Но так ли велика разница для 20 килобайтного файла конфига, который перезаписывается, ну, может 10 000 раз за время жизни проекта. Какова вероятность, что один подход сработает, и не сработает другой.
База данных даёт гораздо больше гарантий, если вам действительно нужны гарантии. SQLite весит килобайт под 300.
Про функции записи. Сохранить что-то в файл — ни в коем случае не атомарная операция. Так, при, например, отсутствии места на диске, файл нормально откроется на запись, затем при попытке записи будет выдано сообщение о невозможности записи. А потом, когда посмотрите файл, окажется, что он нулевой длины. И это лишь одна из ситуаций, хотя и довольно часто встречающаяся.
Это все в комментах выше мы уже обсуждали. И про базу данных тоже, кстати, это были мои же слова что это более безопасно. Пожалуйста, можете просмотреть комменты выше на эту тему, если интересно (жаль, что ряд комментов удалилось).
Это все в комментах выше мы уже обсуждали. И про базу данных тоже, кстати, это были мои же слова что это более безопасно. Пожалуйста, можете просмотреть комменты выше на эту тему, если интересно (жаль, что ряд комментов удалилось).
Вы это уже объясняли, все комменты я прочёл. Но в том то и дело что я сказал с оговоркой — атомарная на уровне интерпретатора. Понятно, что файл может быть записан наполовину, только ошибки в программе не могут к этому привести.
Для примера, строки в одинарных кавычках обрабатываются интерпретатором быстрее, чем в двойных. Вопрос в том, насколько ли быстрее, чтоб вообще уделять этому внимание. Здесь то же самое, ваш подход безопаснее, но настолько ли безопаснее, чтобы вообще говорить о нём?
Допустим, поменяли запись на переименование, но исправлена лишь очень малая часть потенциальных ошибок. Зато мы спокойны, всё сделали грамотно.
Просто вы так спокойно всё объясняете, хотел понять, может я чего-то не понял.
Для примера, строки в одинарных кавычках обрабатываются интерпретатором быстрее, чем в двойных. Вопрос в том, насколько ли быстрее, чтоб вообще уделять этому внимание. Здесь то же самое, ваш подход безопаснее, но настолько ли безопаснее, чтобы вообще говорить о нём?
Допустим, поменяли запись на переименование, но исправлена лишь очень малая часть потенциальных ошибок. Зато мы спокойны, всё сделали грамотно.
Просто вы так спокойно всё объясняете, хотел понять, может я чего-то не понял.
Дело в том, что исправляется не малая часть потенциальных ошибок, а очень большая часть. Более того, в ряде случаев это позволяет избежать потери данных, а не увеличение производительности на 0.00005% как в Вашем примере. Ведь не важно, что операция выполняется атомарно на уровне интерпретатора или не нет, а важно, какие именно действия эта операция производит в системе, и к чему эти действия могут в тех или иных случаях привести.
Или же, я не понимаю Вашей фразы «атомарная на уровне интерпретатора», что Вы имеете в виду? Можете ли пожалуйста пример кода привести на любом языке, где сохранение файла — это «атомарная операция на уровне интерпретатора»? Хотя попробую догадаться. Судя по тому, что Вы упомянули про одинарные и двойные кавычки (это сравнение с данной темой, кстати, не совсем уместно), наверное Вы имеете в виду PHP. Раз так, значит, Вы имеете в виду функцию file_put_contents? Тогда, по Вашему, любую функцию, даже которая может последовательно делать ряд сложных растянутых по времени операций, можно считать атомарной… наверное, я все же что-то не понял, можете пояснить пожалуйста?
Или же, я не понимаю Вашей фразы «атомарная на уровне интерпретатора», что Вы имеете в виду? Можете ли пожалуйста пример кода привести на любом языке, где сохранение файла — это «атомарная операция на уровне интерпретатора»? Хотя попробую догадаться. Судя по тому, что Вы упомянули про одинарные и двойные кавычки (это сравнение с данной темой, кстати, не совсем уместно), наверное Вы имеете в виду PHP. Раз так, значит, Вы имеете в виду функцию file_put_contents? Тогда, по Вашему, любую функцию, даже которая может последовательно делать ряд сложных растянутых по времени операций, можно считать атомарной… наверное, я все же что-то не понял, можете пояснить пожалуйста?
В догонку. Если все же Вы имеете в виду file_put_contents, то она далеко не так безопасна, как хотелось бы видеть. Можете глянуть в мануал ua.php.net/file_put_contents и пожалуйста там обратите внимание на третий коммент сверху, где описывается, как эту проблему решить (как сделать эту функцию действительно atomic). Там как раз приводится пример описанного мной способа с временным файлом.
Да! Я имею в виду file_put_contents(), и я знаю что она не атомарна. Даже атомы не атомарны, не то что вызов file_put_contents или rename.
Если я делаю так: file_put_contents($this->filename, $this->toString()), то ни одно моё ошибочное действие в скрипте не сможет прервать процесс. Я уже защищён от промежуточных действий с моей стороны, где вероятность ошибки особенно велика.
Вероятность реального сбоя для вашего частного случая низка с обеими функциями. Мне кажется это тот случай, где преимущество скорее теоретическое, чем практическое. Отсюда и пример с кавычками — там тоже преимущество одного из вариантов налицо, но игра не стоит свеч.
Но, там где не получится использовать базу, всё равно буду использовать ваш подход, оно так спокойнее. Спасибо за статью, хорошо что подняли тему.
Если я делаю так: file_put_contents($this->filename, $this->toString()), то ни одно моё ошибочное действие в скрипте не сможет прервать процесс. Я уже защищён от промежуточных действий с моей стороны, где вероятность ошибки особенно велика.
Вероятность реального сбоя для вашего частного случая низка с обеими функциями. Мне кажется это тот случай, где преимущество скорее теоретическое, чем практическое. Отсюда и пример с кавычками — там тоже преимущество одного из вариантов налицо, но игра не стоит свеч.
Но, там где не получится использовать базу, всё равно буду использовать ваш подход, оно так спокойнее. Спасибо за статью, хорошо что подняли тему.
Действительно, ни одно ВАШЕ ошибочное действие в скрипте не сможет прервать процесс. Но зато какое-либо НЕ ВАШЕ действие, например действие или состояние системы, может этот процесс прервать. Например, возможна смерть процесса в момент записи, или просто выключение питания компьютера, или отсутствие места на диске.
Вот о чем я толкую. Ведь одно дело сохранять данные куда-нибудь в файловый кеш, где потеря данных ничему не грозит (там я бы и использовал бы просто file_put_contents), а другое дело обновлять конфиг, потеря или порча которого грозит смерти системе до ручного вмешательства программиста или админа.
Проблемы бывают не только от ошибочных действий программиста, но и от бездействий, если программист решает задачи простым способом в лоб, не учитывая исключительных ситуаций. IMHO может быть, конечно, это и паранойя с моей стороны. Но по крайней мере я высказал свою мысль, может быть кому и пригодится.
Вот о чем я толкую. Ведь одно дело сохранять данные куда-нибудь в файловый кеш, где потеря данных ничему не грозит (там я бы и использовал бы просто file_put_contents), а другое дело обновлять конфиг, потеря или порча которого грозит смерти системе до ручного вмешательства программиста или админа.
Проблемы бывают не только от ошибочных действий программиста, но и от бездействий, если программист решает задачи простым способом в лоб, не учитывая исключительных ситуаций. IMHO может быть, конечно, это и паранойя с моей стороны. Но по крайней мере я высказал свою мысль, может быть кому и пригодится.
Sign up to leave a comment.
Как правильно сохранять файлы?