Обновление PostgreSQL с устранением серьёзных проблем с fsync
14 февраля 2019 года
Сформированы корректирующие обновления для всех поддерживаемых веток PostgreSQL: 11.2, 10.7, 9.6.12, 9.5.16 и 9.4.21, в которых исправлено около 70 ошибок. Наиболее значительным изменением стала переработка механизма использования вызова fsync() для обеспечения целостности записываемых на диск данных.
Оказалось, что вызов fsync() некорректно используется в PostgreSQL уже около 20 лет, что потенциально могло приводить в Linux, NetBSD и OpenBSD к потере записываемых данных в случае аппаратных сбоев. Разработчики PostgreSQL полагали, что успешно завершившийся вызов fsync() гарантирует, что поступившие данные записаны на постоянный носитель, но оказалось, что существуют ситуации когда это не так.
В случае когда ядро не может записать данные, например из-за сбоя буферизированного ввода/вывода вследствие аппаратной ошибки, некоторые операционные системы возвращают код ошибки в fsync() и очищают содержимое ожидающих записи буферов. Таким образом, ранее переданные данные отбрасываются, а блоки помечаются как очищенные. Получив код ошибки PostgreSQL опять попытается сбросить на диск данные и ещё раз вызывает fsync().
Так как буферы были очищены повторный вызов будет завершён успешно и PostgreSQL посчитает, что все данные записаны успешно. Но на деле, при чтении блоков, которые PostgreSQL полагает записанными, будет возвращено не то, что ожидается. Проблема усугубляется тем, что в разных операционных системах fsync() ведёт себя по разному и логичным кажется поведение, когда данные не отбрасываются при первой же неудачной попытке записи, а сохраняются в памяти в состоянии "dirty" для повторения попыток записи.
Начиная с выпусков PostgreSQL 11.2, 10.7, 9.6.12, 9.5.16 и 9.4.21 логика обработки ошибок fsync() изменена и PostgreSQL теперь не пытается после сбоя выполнения fsync() повторно вызвать fsync(), а завершается с выдачей фатальной ошибки. Данный шаг даёт возможность при перезапуске восстановить корректное состояние данных на основе WAL-лога, минуя скрытое повреждение содержимого базы. Подобная логика обработки ошибки может показаться неоптимальной, но разработчики сочли данное решение достаточным так как указанные проблемы возникают крайне редко.
Для систем, ядро которых не сбрасывает содержимое буфера записи после сбоя, в настройки добавлена опция data_sync_retry, позволяющая вернуть старое поведение с двойным вызовом fsync(). Например, FreeBSD не очищает состояние и при повторном вызове fsync() повторно выведет ошибку.
Поведение с очисткой буфера записи после ошибки объясняется тем, что в подавляющем большинстве случаев ошибки ввода/вывода возникают из-за удаления USB-накопителя без отмонтирования. Без очистки ситуация, когда какой-то процесс продолжает пытаться записать большой объём данных, приведёт к накапливанию страниц в состоянии "dirty", вплоть до исчерпания доступной памяти. Очистка же помогает сохранить работоспособность системы в подобных ситуациях.
Дополнительно можно отметить, что факт ошибки не всегда удаётся отследить. Например, если ошибка ввода/вывода возникла до открытия файла, то fsync() завершится успешно. Компания Google для обхода описанной проблемы использует альтернативный метод обработки ошибок ввода/вывода, основанный на сборе сведений об ошибках напрямую из ядра через netlink-сокет. Другим вариантом является использование прямого ввода/вывода (DIO), который предоставляет дополнительные механизмы для отслеживания сброса данных на диск и контроля за активностью ввода/вывода.
Источники
править
Любой участник может оформить статью: добавить иллюстрации, викифицировать, заполнить шаблоны и добавить категории.
Любой редактор может снять этот шаблон после оформления и проверки.
Комментарии
Если вы хотите сообщить о проблеме в статье (например, фактическая ошибка и т. д.), пожалуйста, используйте обычную страницу обсуждения.
Комментарии на этой странице могут не соответствовать политике нейтральной точки зрения, однако, пожалуйста, придерживайтесь темы и попытайтесь избежать брани, оскорбительных или подстрекательных комментариев. Попробуйте написать такие комментарии, которые заставят задуматься, будут проницательными или спорными. Цивилизованная дискуссия и вежливый спор делают страницу комментариев дружелюбным местом. Пожалуйста, подумайте об этом.
Несколько советов по оформлению реплик:
- Новые темы начинайте, пожалуйста, снизу.
- Используйте символ звёздочки «*» в начале строки для начала новой темы. Далее пишите свой текст.
- Для ответа в начале строки укажите на одну звёздочку больше, чем в предыдущей реплике.
- Пожалуйста, подписывайте все свои сообщения, используя четыре тильды (~~~~). При предварительном просмотре и сохранении они будут автоматически заменены на ваше имя и дату.
Обращаем ваше внимание, что комментарии не предназначены для размещения ссылок на внешние ресурсы не по теме статьи, которые могут быть удалены или скрыты любым участником. Тем не менее, на странице комментариев вы можете сообщить о статьях в СМИ, которые ссылаются на эту заметку, а также о её обсуждении на сторонних ресурсах.