Сформированы (https://www.postgresql.org/about/news/1920/) корректирующие обновления для всех поддерживаемых веток PostgreSQL: 11.2 (https://www.postgresql.org/docs/current/static/release-11-2....), 10.7 (https://www.postgresql.org/docs/current/static/release-10-7....), 9.6.12 (http://www.postgresql.org/docs/current/static/release-9-6-12...), 9.5.16 (http://www.postgresql.org/docs/current/static/release-9-5-16...) и 9.4.21 (http://www.postgresql.org/docs/current/static/release-9-4-21...), в которых исправлено около 70 ошибок. Наиболее значительным изменением стала переработка механизма использования вызова fsync() для обеспечения целостности записываемых на диск данных. Оказалось (https://fosdem.org/2019/schedule/event/postgresql_fsync/), что вызов fsync() некорректно используется в PostgreSQL уже около 20 лет, что потенциально могло приводить к потере записываемых данных в случае аппаратных сбоев (проблема свойственна как Linux, так и некоторым BSD-системам). Разработчики PostgreSQL полагали, что успешно завершившийся вызов fsync() гарантирует, что поступившие данные записаны на постоянный носитель, но оказалось, что существуют ситуации когда это не так.
В случае когда ядро не может записать данные, например из-за сбоя буферизированного ввода/вывода вследствие аппаратной ошибки, некоторые операционные системы возвращают код ошибки в fsync() и очищают содержимое ожидающих записи буферов. Таким образом, ранее переданные данные отбрасываются, а блоки помечаются как очищенные. Получив код ошибки PostgreSQL опять попытается сбросить на диск данные и ещё раз вызывает fsync(). Так как буферы были очищены повторный вызов будет завершён успешно и PostgreSQL посчитает, что все данные записаны успешно. На деле, при чтении блоков, которые PostgreSQL полагает записанными, будет возвращено не то, что ожидается.
Начиная с выпусков PostgreSQL 11.2, 10.7, 9.6.12, 9.5.16 и 9.4.21 логика обработки ошибок fsync() изменена и PostgreSQL теперь не пытается после сбоя выполнения fsync() повторно вызвать fsync(), а завершается с выдачей фатальной ошибки. Данный шаг даёт возможность при перезапуске восстановить корректное состояние данных на основе WAL-лога, минуя скрытое повреждение содержимого базы. Подобная логика обработки ошибки может показаться неоптимальной, но разработчики сочли данное решение достаточным так как указанные проблемы возникают крайне редко.
Для систем ядро которых не сбрасывает содержимое записи после сбоя в настройки добавлена опция data_sync_retry, позволяющая вернуть старое поведение с двойным вызовом fsync(). Можно отметить, что компания Google для обхода описанной проблемы использует альтернативный метод обработки ошибок ввода/вывода, основанный на сборе сведений об ошибках напрямую из ядра через netlink-сокет.
URL: https://www.postgresql.org/about/news/1920/
Новость: https://www.opennet.ru/opennews/art.shtml?num=50148