fsync(fd) доводит до носителя содержимое файла. А rename() меняет каталог, и эта запись остаётся в page cache ровно как любая несохранённая страница. Чтобы переименование пережило отключение питания, нужно синхронизировать ещё и каталог — строки, которой почти нигде нет.Я не стал спорить, а измерил. dm-log-writes — цель device-mapper в ядре Linux — складывает копию каждой записи с отметками сброса кэша. Журнал воспроизводится на чистое устройство до каждой точки отказа, устройство монтируется и отрабатывает настоящее восстановление журнала ext4, xfs или btrfs. Не модель POSIX — то, что блочный слой действительно отправил вниз.Двенадцать способов сказать «сохранено» — от голого writeFileSync до SQLite и git commit. Десять теряют подтверждённую запись, на всех трёх файловых системах.Среди них write-file-atomic — 102 миллиона загрузок в неделю, его использует сам npm CLI, когда пишет ваш package-lock.json. SQLite на настройках по умолчанию. git commit даже при core.fsync=all: шесть вызовов fsync, и все на файлы, ни одного на каталог.Внутри — как устроено измерение, почему ширину окна потери задаёт интервал фиксации журнала (и почему совет «поставьте commit=60» расширяет это окно в двенадцать раз), и отдельный раздел про четыре ошибки, которые я допустил в собственном инструменте. Включая вывод, сделанный из одного зелёного прогона CI — в инструменте, написанном ради тезиса «один зелёный прогон ничего не доказывает».Инструмент: gray, MIT. Читать далее