TL; DR: Если ядро Linux теряет буферизованное ввод-вывод ввода/вывода, есть ли способ для поиска приложения?
Я знаю, что вы должны fsync()
сохранить файл (и его родительский каталог) для долговечности. Вопрос в том, что ядро потеряет грязные буферы, ожидающие записи из-за ошибки ввода-вывода, как приложение может обнаружить это и восстановить или прервать?
Подумайте о приложениях для баз данных и т.д., где критический момент записи и записи может иметь решающее значение.
Lost пишет? Как?
Блочный уровень ядра Linux в некоторых случаях может потерять буферизованные запросы ввода-вывода, успешно отправленные write()
, pwrite()
и т.д., с ошибкой, например:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(См. end_buffer_write_sync(...)
и end_buffer_async_write(...)
в fs/buffer.c
).
В более новых ядрах вместо ошибки будет содержаться "потерянная страница для асинхронной страницы" , например:
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Так как приложение write()
уже вернулось без ошибок, похоже, что нет способа сообщить об ошибке в приложение.
Обнаружение их?
Я не знакомы с источниками ядра, но думаю, что он устанавливает AS_EIO
в буфере, который не был выписан, если он пишет асинхронную запись:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
но мне непонятно, если и как приложение может узнать об этом, когда позже fsync()
файл будет подтвержден на диске.
Похоже, wait_on_page_writeback_range(...)
в mm/filemap.c
может do_sync_mapping_range(...)
в fs/sync.c
, который называется turn sys_sync_file_range(...)
. Он возвращает -EIO
, если один или несколько буферов не могут быть записаны.
Если, как я предполагаю, это распространяется на результат fsync()
, тогда, если приложение панически реагирует, если оно получает ошибку ввода-вывода от fsync()
и знает, как повторно выполнять свою работу при перезапуске, что должно быть достаточной защитой?
По-видимому, нет возможности для приложения узнать, какие байтовые смещения в файле соответствуют потерянным страницам, чтобы он мог переписать их, если он знает, как это сделать, но если приложение повторяет всю свою ожидающую работу с момента последнего успешного fsync()
файл и перезаписывает любые грязные буферы ядра, соответствующие потерянным файлам записи, которые должны очищать любые ошибки ввода-вывода на потерянных страницах и разрешать следующее fsync()
- право?
Существуют ли тогда какие-либо другие, безобидные обстоятельства, в которых fsync()
может возвращать -EIO
, где выкапывание и повторная работа были бы слишком резкими?
Почему?
Конечно, таких ошибок не должно быть. В этом случае ошибка возникла из-за неудачного взаимодействия между по умолчанию драйверами dm-multipath
и кодом смысла, используемым SAN, чтобы сообщить о сбое в распределении хранилища с тонким резервированием. Но это не единственное обстоятельство, в котором они могут случиться - я также видел сообщения об этом из тонкого подготовленного LVM, например, как libvirt, Docker и т.д. Критическое приложение, такое как база данных, должно пытаться справиться с такими ошибками, а не слепо вести себя, как будто все хорошо.
Если ядро думает, что это нормально, чтобы потерять записи, не умирая с паникой ядра, приложения должны найти способ справиться.
Практическое воздействие заключается в том, что я обнаружил случай, когда проблема многолучевости с SAN вызвала потерянные записи, которые вызвали повреждение базы данных, потому что СУБД не знали, что ее записи потерпели неудачу. Не весело.