Проблемы с вводом-выводом на диск: что может пойти не так

Проблемы с вводом-выводом на диск: что может пойти не так
Photo by benjamin lehman / Unsplash

Разработка приложений, особенно тех, которые работают с файлами в транзакционных контекстах, требует учета различных сценариев, которые могут привести к ошибкам. Это особенно важно, когда речь идет о целостности данных, например, при редактировании данных на месте, а не при использовании подхода "копирование при записи". Рассмотрим несколько ситуаций, которые могут возникнуть:

  1. Данные, которые вы записываете, никогда не достигают диска.
  2. Данные записываются не в то место на диске.
  3. Данные считываются не с того места на диске.
  4. Данные повреждаются на диске.

Также стоит рассмотреть, как реальные системы управления данными справляются с этими сценариями (если вообще справляются).

Если не указано иное, речь идет о поведении в Linux.

Терминология

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

Сектор

Наименьший объём данных, который может быть прочитан и записан атомарно аппаратурой. Раньше это было 512 байт, но на современных дисках это часто 4 КиБ. Нельзя делать никаких предположений о размере сектора, несмотря на стандартные значения файловых систем. Необходимо проверять диски, чтобы узнать размер сектора.

Блок (видение файловой системы/ядра)

Обычно устанавливается равным размеру сектора, так как только этот размер блока является атомарным. По умолчанию в ext4 это 4 КиБ.

Страница (видение ядра)

Блок диска, который находится в памяти. Любые чтения/записи меньше размера блока будут читать весь блок в память ядра, даже если меньше данных отправляется обратно в пользовательское пространство.

Страница (видение базы данных/приложения)

Наименьший объём данных, с которым работает система (база данных, приложение и т.д.) при чтении или записи или при хранении в памяти. Размер страницы является кратным размеру блока файловой системы/ядра (включая кратность, равную 1). Размер страницы по умолчанию в SQLite — 4 КиБ, в MySQL — 16 КиБ, в Postgres — 8 КиБ.

Что может пойти не так

Данные не достигли диска

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

Успешное возвращение из write() не гарантирует, что данные были зафиксированы на диске. На некоторых файловых системах, включая NFS, это не гарантирует даже успешного резервирования места для данных. В этом случае некоторые ошибки могут быть отложены до будущего вызова write(), fsync(2) или даже close(2). Единственный способ быть уверенным — вызвать fsync(2) после завершения записи всех данных.

Если вы не вызываете fsync на Linux, данные не обязательно будут надёжно записаны на диск, и если система сбоит или перезагрузится до того, как диск запишет данные в неизменяемое хранилище, вы можете потерять данные.

С O_DIRECT запись файлов считается успешной, когда данные скопированы хотя бы в кэш диска. Альтернативно можно открыть файл с O_DIRECT|O_SYNC (или O_DIRECT|O_DSYNC) и отказаться от вызовов fsync.

fsync на macOS является пустышкой (no-op).

Postgres, SQLite, MongoDB, MySQL по умолчанию выполняют fsync данных перед тем, как считать транзакцию успешной. RocksDB этого не делает.

fsync был вызван, но не удался

fsync не гарантирует успех. И когда он терпит неудачу, нельзя сказать, какая именно запись не удалась. Это может быть даже не ошибка записи в файл, который открыл ваш процесс:

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

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

Единственный способ узнать, какая именно запись не удалась, — это открыть файл с O_DIRECT|O_SYNC (или O_DIRECT|O_DSYNC), хотя это не единственный способ обработки неудач fsync.

Данные были повреждены

Если вы не проверяете контрольные суммы данных при записи и не проверяете их при чтении (а также периодически не проводите проверку, как это делает ZFS), вы никогда не узнаете, если и когда данные были повреждены, и вам придётся восстанавливать их из резервных копий, если вы вообще заметите проблему.

ZFS, MongoDB (WiredTiger), MySQL (InnoDB) и RocksDB по умолчанию проверяют контрольные суммы данных. Postgres и SQLite этого не делают (хотя базы данных, созданные из Postgres 18+, будут).

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

Read more

Cold pull — как вытащить засор из сопла и не сломать мозг

Cold pull — как вытащить засор из сопла и не сломать мозг

Знаете, что общего у принтера Flashforge 5M Pro и кофемашины, которая внезапно перестала варить кофе? Правильно — если в узком месте что-то застряло, всё остальное тут же идёт по одному месту. В 3D-принтерах это место — сопло. И когда оно забивается, без паники. Есть старый добрый способ — cold pull. А теперь по-человечески:

Самохостниг (часть 10) - AudiobookShelf

Самохостниг (часть 10) - AudiobookShelf

AudiobookShelf - это бесплатный сервер аудиокниг и подкастов с открытым исходным кодом. Он позволяет вам организовать вашу коллекцию аудиокниг и подкастов, следить за прогрессом прослушивания и синхронизировать его между устройствами. В этой статье мы рассмотрим, как установить AudiobookShelf с помощью Docker Compose и настроить его работу через Traefik. Предварительные требования

Обзор reMarkable 2

Обзор reMarkable 2

В мире технологий появляются устройства, которые не только выполняют свои функции, но и становятся символами статуса и стиля. Одним из таких устройств является reMarkable 2 — уникальный гибрид электронной читалки и записной книжки, который произвёл фурор на Западе и теперь доступен в России. Давайте разберёмся, что делает этот девайс таким особенным