![]() |
|
|||
WebMoney: WMZ Z294115950220 WMR R409981405661 WME E134003968233 |
Visa 4274 3200 2453 6495 |
База данных SQLite очень стойкая к повреждениям.
Если сбой приложения или катастрофа операционной системы, или даже перебой в
питании происходит посреди транзакции, частично написанная транзакция
должна быть автоматически отменена до прежнего уровня в следующий раз, когда
к файлу базы данных получают доступ. Процесс восстановления полностью
автоматический и не требует никакого действия со стороны
пользователя или приложения. Хотя SQLite стойкий к повреждению базы данных, это не неуязвимо.
Этот документ описывает различные пути, которыми база данных SQLite
могла бы быть разрушена. Файлы базы данных SQLite это обычные дисковые файлы. Это означает, что
любой процесс может открыть файл и переписать его мусором.
Нет ничего, что библиотека SQLite может сделать, чтобы защитить от этого. Мы видели многократные случаи, где дескриптор файла был открыт, затем
этот дескриптор файла был закрыт и вновь открылся на базе данных SQLite.
Позже некоторый другой поток продолжал писать в старый
дескриптор файла, не понимая, что оригинальный файл уже был закрыт.
Но потому что дескриптор файла был вновь открыт SQLite, информация, которая
была предназначена, чтобы войти в оригинальный файл, закончила тем, что
переписала части базы данных SQLite, приведя к повреждению
базы данных. Один пример этого произошел приблизительно 2013-08-30 на каноническом
хранилище для Fossil DVCS.
В таком случае дескриптор файла 2 (стандартная ошибка) ошибочно закрывался
(stunnel, мы подозреваем) до того, как
дескриптор файла, используемый sqlite3_open_v2()
для файла хранилища данных, равнялся 2.
Позже, прикладная ошибка вызвала assert(), чтобы выпустить сообщение об
ошибке вызова write(2,...). Но так как дескриптор файла 2 был теперь связан с
файлом базы данных, сообщение об ошибке переписало часть базы данных.
Чтобы принять меры против этого вида проблемы,
SQLite version 3.8.1 (2013-10-17)
и позже отказались использовать дескрипторы файлов с низким номером для
файлов базы данных. См.
SQLITE_MINIMUM_FILE_DESCRIPTOR для подробностей. О другом примере, вызванном при помощи закрытого дескриптора файла
сообщили инженеры Facebook в сообщении в блоге от 2014-08-12. О другом примере этой ошибки сообщили для
Fossil 2019-07-11.
Дескриптор файла был открыт для отладочной информации, но потом закрыт и
вновь открыт SQLite. Но логика отладки продолжала писать в оригинальный
дескриптор файла. Посмотрите
дискуссию на
форуме для отчета об ошибках и ссылки с исправлением. Системы, которые управляют автоматическими резервными копиями в фоновом
режиме, могли бы попытаться сделать резервную копию файла базы данных SQLite,
в то время как это посреди транзакции. Резервная копия тогда могла бы
содержать некоторое старое и новое содержание, и таким
образом быть непоследовательной. Лучший подход, чтобы сделать надежные резервные копии базы данных SQLite
должен использовать backup API, который является
частью библиотеки SQLite. Безопасно сделать копию файла базы данных SQLite,
пока нет никаких происходящих транзакций никаким процессом. Если предыдущая
транзакция потерпела неудачу, то важно, чтобы любой журнал обратной
перемотки (файл *-journal) или журнал с упреждающей записью (файл
*-wal) был скопирован вместе с самим файлом базы данных. SQLite обычно хранит все содержание в одном файле.
Однако, выполняя транзакцию, информация, необходимая, чтобы восстановить базу
данных после катастрофы или перебоя в питании, хранится во вспомогательных
файлах журнала. Такие файлы журнала описаны как
"горячие".
У файлов журнала есть то же самое имя, как у оригинального файла базы данных
с добавлением суффикса -wal или -journal. SQLite должен видеть файлы журнала, чтобы оправиться от катастрофы или
перебоя в питании. Если
горячие файлы журнала перемещены, удалены
или переименованы после катастрофы или перебоя в питании, то автоматическое
восстановление не будет работать, а база данных может быть повреждена. Другое проявление этой проблемы
повреждение базы данных, вызванное
непоследовательным использованием 8+3 имен файлов. Предыдущий пример это конкретный случай более общей проблемы:
состояние базы данных SQLite управляет файлом базы данных и файлом журнала.
В состоянии покоя файл журнала не существует и работает только файл базы
данных. Но если файл журнала действительно существует, он должен держаться
вместе с базой данных, чтобы избежать накладок.
Следующие действия вероятно могут привести к повреждению: SQLite применяет блокировку файла на файле базы данных и на файлах
журнала с упреждающей записью или
WAL, чтобы скоординировать доступ между параллельными процессами.
Без координации два процесса могли бы попытаться внести несовместимые
изменения в файл базы данных в то же время, приведя к
повреждению базы данных. SQLite зависит от основной файловой системы, чтобы сделать захват,
поскольку в документации говорится, что это будет работать.
Но некоторые файловые системы содержат ошибки в своей логике захвата,
таким образом, что блокировки не всегда ведут себя, как рекламируется.
Это особенно верно для сетевых файловых систем и NFS в частности.
Если SQLite используется в файловой системе, где примитивы захвата содержат
ошибки, и если два или больше потока или процесса пытаются получить доступ к
той же самой базе данных в то же время, то ожидаемо
повреждение базы данных. Механизм блокировки по умолчанию, используемый SQLite на платформах Unix,
является консультативной блокировкой POSIX. К сожалению, у консультативной
блокировки POSIX есть причуды дизайна, которые делают ее подверженной
неправильному употреблению. В частности любой поток
в том же самом процессе с дескриптором файла, который держит консультативную
блокировку POSIX, может отвергнуть эту блокировку, используя различный
дескриптор файла. Одна особенно пагубная проблема состоит в том, что
системный вызов close() отменит все консультативные блокировки POSIX
на том же самом файле для всех потоков и дескрипторов файлов в процессе. Так, например, предположите, что у процесса есть два или больше потока
с отдельными соединениями с базой данных SQLite к тому же самому файлу базы
данных. Тогда третий поток приходит и хочет прочитать что-то из того же
самого файла базы данных самостоятельно, не пользуясь библиотекой SQLite.
Третий поток делает open(), read(), затем close().
Можно было бы думать, что это будет безопасно. Но системный вызов
close() снимает блокировку базы данных всеми другими потоками.
У тех других потоков нет способа знать, что их блокировки сняты.
POSIX не обеспечивает механизма, чтобы определить это и таким образом, они
продолжают работать с предположением, что их блокировки все еще
действительны. Это может привести к двум или больше потокам
или процессам, пытающимся написать базу данных в то же время,
приведя к закономерному повреждению. Вот как такое называется? Обратите внимание на то, что для двух или больше потоков
совершенно безопасно получить доступ к тому же самому файлу базы
данных SQLite, пользуясь библиотекой SQLite. Драйверы Unix для SQLite знают о
консультативных причудах блокировки POSIX. Эта проблема возникает только,
когда поток пытается обойти библиотеку SQLite и прочитать файл
базы данных непосредственно. Как указано в предыдущем параграфе, SQLite предпринимает шаги, чтобы
избавиться от причуд консультативной блокировки POSIX.
Часть этой работы включает хранение глобального списка (mutex protected)
открытых файлов базы данных SQLite. Но, если многочисленные копии SQLite
будут связаны в то же самое приложение,
то будут многократные копии этого глобального списка.
Соединения с базой данных, открытые с использованием одной копии библиотеки
SQLite, будет не знать о соединениях с базой данных, открытых с
использованием другой копии и будут неспособны работать нормально.
Операция close() на одной связи могла бы бессознательно очистить
блокировки на ином соединении с базой данных, приведя к сбою. Сценарий выше кажется неправдоподобным. Но разработчики SQLite знают о по
крайней мере одном коммерческом продукте, который был выпущен с точно этой
ошибкой. Продавец приехал к разработчикам SQLite, обращающимся за помощью в
разыскивании некоторых нечастых проблем повреждения
базы данных, которые они видели на Linux и Mac. Проблема была в конечном
счете прослежена до того, что приложение связывалось с двумя
отдельными копиями SQLite. Решение состояло в том, чтобы изменить сборку. Механизм блокировки по умолчанию, используемый SQLite на платформах
Unix, является консультативной блокировкой POSIX,
но есть другие варианты. Выбирая альтернативу
sqlite3_vfs, используя sqlite3_open_v2(),
приложение может использовать другие протоколы блокировки, которые могли бы
более соответствовать определенным файловым системам. Например, точечный
захват файла мог бы быть избран для использования в применении, которое
должно работать в файловой системе NFS, которая не поддерживает
консультативную блокировку POSIX. Важно, чтобы все связи с тем же самым файлом базы данных использовали
тот же самый протокол. Если одно приложение будет использовать
консультативные блокировки POSIX, а другое использует точечный захват файла,
то они не будут видеть друг друга и не будут в состоянии скоординировать
доступ к базе данных, возможно приводя к повреждению базы данных. Если у двух процессов будут открыты
связи с тем же самым файлом базы данных, и один процесс закрывает свою связь,
затем создает новый файл базы данных на его месте с тем же самым именем и
вновь открывает новый файл, то два процесса будут общаться с различными
файлами базы данных с тем же самым именем. Обратите внимание на то, что это
возможно только на системах Posix и Posix-like, которые разрешают файлу
расцепляться, в то время как это все еще открыто для чтения и написания.
Windows не позволяет этому происходить.
Так как журналы обратной перемотки и файлы WAL основаны на названии файла
базы данных, два различных файла базы данных разделят тот же самый журнал
обратной перемотки или файл WAL. Обратная перемотка или восстановление для
одной из баз данных могли бы использовать содержание от другой базы данных.
Подобная проблема происходит, если файл базы данных переименован, в то время
как это открыто, и новый файл создается со старым названием. Другими словами, расцепление или переименование открытого файла базы
данных приводят к поведению, которое не определено и вероятно нежелательно. Начиная с SQLite version 3.7.17
(2013-05-20), интерфейс OS Unix пошлет сообщения
SQLITE_WARNING в журнал ошибок, если файл базы
данных будет расцепляться, в то время как это все еще используется. Если у файла единой базы данных есть многократные связи,
тогда это просто другой способ сказать, что у файла есть многократные имена.
Если два или больше процесса откроют базу данных, используя различные имена,
то они будут использовать различные журналы обратной перемотки и файлы WAL.
Это означает, что, если один процесс терпит крах, другой процесс будет
не способен отменить происходящую транзакцию, потому что это будет смотреть в
неправильном месте для соответствующего журнала. Другими словами, открывая и используя файл базы данных, у которого есть
два или больше имени, поведение не определено и вероятно нежелательно. Начиная с SQLite version 3.7.17
(2013-05-20), интерфейс OS Unix пошлет сообщения SQLITE_WARNING в
журнал ошибок, если у файла базы данных будут
многократные жесткие ссылки. Начиная с SQLite version 3.10.0
(2016-01-06), интерфейс OS Unix
попытается решить символьные ссылки и открыть файл базы данных его
каноническим именем. До версии 3.10.0 открытие файла базы данных через
символьную ссылку было подобно открытию файла базы данных, который имел
многократные жесткие ссылки и вело к неопределенному поведению. Не открывайте соединение с базой данных SQLite, затем fork(),
чтобы затем использовать то соединение с базой данных в дочернем процессе.
Все виды проблем блокировки закончатся, и можно легко закончить с
поврежденной базой данных. SQLite не разработан, чтобы поддержать такое
поведение. Любое соединение с базой данных, которое используется в дочернем
процессе, должно быть открыто в дочернем процессе, не
унаследовано от родителя. Даже не вызывайте sqlite3_close()
на соединении с базой данных от дочернего процесса, если связь была
открыта в родителе. Безопасно закрыть основной дескриптор файла, но
sqlite3_close()
мог бы вызвать действия очистки, которые удалят содержание у
родителя, приводя к ошибкам и возможно даже повреждению базы данных. Чтобы гарантировать, что файлы базы данных всегда последовательны, SQLite
будет иногда просить, чтобы операционная система
сбросила дисковые буферы, затем будет ждать окончания этого процесса.
Это достигается, используя системный вызов fsync() под Unix и
FlushFileBuffers() под Windows. Синхронизирующая операция может считаться барьером I/O.
Если синхронизация действует в качестве барьера I/O, а не в качестве истинной
синхронизации, то перебой в питании или системная катастрофа могли бы вызвать
отмену одной или нескольких транзакций (в нарушении "durable" в "ACID"),
но база данных, по крайней мере, продолжит быть последовательной, и именно
об этом заботится большинство людей. К сожалению, большая часть устройств массового хранения потребительского
сорта врут о синхронизации.
Дисководы сообщат, что содержание находится в безопасности на носителе,
как только оно достигает буфера, но на диск при этом все не записано.
Это заставляет дисководы работать быстрее (что жизненно важно для
производителя так, чтобы они могли показать хорошие эталонные числа в
торговых журналах). И честно сказать, это обычно не наносит ущерба, пока нет
никаких потерь мощности или жесткой перезагрузки. Но если потери мощности или
жесткая перезагрузка действительно происходят, и если это приводит к
содержанию, которое было не было написано на диск на самом деле, то вот он,
сбой базы данных. USB flash memory stick, кажется, особенно пагубны в этом плане.
Можно легко видеть это, передавая большую транзакцию
базе данных SQLite на карте памяти USB. Команда COMMIT возвратится
относительно быстро, указывая, что карта памяти сказала операционной
системе, а операционная система сказала SQLite, что все содержание находится
в безопасности в постоянном хранении, а все же запись будет идти
в течение еще нескольких секунд, что очень хорошо видно по индикатору.
Вытаскивание карты памяти, в то время как LED все еще вспыхивает, будет часто
приводить к повреждению базы данных. Обратите внимание на то, что SQLite должен верить тому, что операционная
система и аппаратные средства говорят ему о статусе синхронизирующих
запросов. Нет никакого пути SQLite, чтобы обнаружить реальное положение дел.
Однако, SQLite в режиме WAL
намного более прочен к таким вещам, чем в режимах журнала обратной перемотки
по умолчанию. В режиме WAL единственное время, когда неудавшаяся
синхронизирующая операция может вызвать сбой базы данных, во время операции
по контрольной точке.
Сбой синхронизации во время COMMIT мог бы привести к потере длительности, но
не к поврежденному файлу базы данных. Следовательно, одна линия обороны
против сбоя базы данных из-за неудавшихся синхронизирующих операций должна
использовать SQLite в режиме WAL. Синхронизирующие операции, которые SQLite выполняет, чтобы помочь
гарантировать целостность, могут быть отключены во время выполнения,
используя synchronous pragma.
Устанавливая PRAGMA synchronous=OFF, все синхронизирующие операции опущены.
Это заставляет SQLite работать быстрее, но он также позволяет операционной
системе свободно переупорядочивать записи, что может привести к сбою
базы данных, если перебой в питании или жесткая перезагрузка происходят. Для максимальной надежности и для надежности против повреждения
базы данных, SQLite нужно всегда управлять с его синхронизацией по умолчанию,
то есть FULL. База данных SQLite может стать поврежденной
, если содержание файла изменяется из-за отказа дисковода или флэш-памяти.
Это очень редко, но бывает. Нам говорят, что в некоторых диспетчерах флэш-памяти выравнивающая
изнашивание логика может нанести случайный ущерб файловой системы, если
питание прервано во время записи. Это может проявить, например, как случайные
изменения посреди файла, который даже не был открыт во время потери питания.
Так, например, устройство написало бы содержание в файл MP3 во флэш-памяти,
когда потери мощности происходят, и это могло привести к базе данных SQLite,
испорченной даже при том, что база данных даже не использовалась во
время потери мощности. Есть много мошеннических карт с интерфейсом USB в обращении, которые
сообщают о большой емкости (например, 8GB),
но действительно способны к хранению только намного меньшего объема
(например, 1GB). Попытки написать на эти устройства будут часто приводить к
несвязанным переписываемым файлам.
Любое использование мошеннического устройства флэш-памяти может легко
привести к повреждению базы данных. SQLite это C-библиотека, которая работает в том же самом адресном
пространстве, где приложение, которому это служит. Это означает, что
случайные указатели, переполнение буфера, сбои
кучи или другие сбои в приложении могут испортить внутреннюю структуру
данных SQLite и в конечном счете привести к поврежденному
файлу базы данных. Обычно эти виды проблем проявляются как segfaults до
любого появления повреждения базы данных, но были случаи, где
ошибки кода приложения заставили SQLite работать со сбоями тонко, чтобы
испортить файл базы данных вместо паники. Проблема повреждения памяти становится более острой, используя
memory-mapped I/O.
Когда весь или часть файла базы данных будут отображены
в адресное пространство приложения, случайный указатель, который переписывает
любую часть того пространства, немедленно испортит файл базы данных. Иногда операционные системы будут показывать нестандартное поведение,
которое может привести к проблемам. Иногда это нестандартное поведение
преднамеренное, и иногда это ошибка во внедрении.
Но в любом случае, если работа выполняется по-другому по сравнению с
ожиданиями SQLite, возможность повреждения базы данных существует. Некоторые старые версии Linux пользовались библиотекой LinuxThreads для
поддержки потоков. LinuxThreads подобен Pthreads, но тонко отличается
относительно обработки консультативных блокировок POSIX. SQLite с версии
2.2.3 по 3.6.23 распознает, что LinuxThreads используются во время выполнения
и принимает соответствующие меры, чтобы работать против
нестандартного поведения LinuxThreads. Но самые современные версии Linux
используют более новое и правильное внедрение NPTL Pthreads.
Начиная с SQLite version 3.7.0
(2010-07-21), использование NPTL принято. Никакие проверки не осуществлены.
Следовательно, последние версии SQLite будут тонко работать со сбоями и могут
испортить файлы базы данных, если используется в многопоточном приложении,
которое работает на более старых системах Linux, которые
используют LinuxThreads. Там существует некоторая тонкая проблема с mmap() в QNX, таким образом,
что второй вызов mmap() для
единственного дескриптора файла может обнулить память, полученную
первым вызовом mmap(). SQLite в unix применяет mmap(),
чтобы создать регион общей памяти для операционной координации в
режиме WAL и это вызовет mmap() многократно для
больших транзакций. QNX mmap() запросто портит базу данных.
Инженеры QNX знают об этой проблеме и работают над решением, проблема,
возможно, была уже решена к тому времени, когда вы читаете это. Работая на QNX, рекомендуется, чтобы
memory-mapped I/O не применялся.
Кроме того, чтобы использовать режим WAL,
рекомендуется, чтобы запросы использовали
исключительный режим блокировки
, чтобы использовать WAL без общей памяти.
Так как базы данных SQLite это обычные дисковые файлы, любой сбой в
файловой системе может испортить базу данных. Файловые системы в современных
операционных системах очень надежны, но ошибки действительно все еще
происходят. Например, 2013-10-01 база данных SQLite, которая хранит
Wiki for Tcl/Tk, накрылась
спустя несколько дней после того, как главный компьютер был переведен на
другое ядро linux, у которого были проблемы в слое файловой системы.
В этом случае файловая система в конечном счете стала так ужасно испорченной,
что машина была непригодна, но самый ранний признак проблемы был испорченной
базой данных SQLite. У SQLite есть много встроенных мер защиты от сбоев
базы данных. Но многие из этих мер защиты могут быть отключены параметрами
конфигурации. Если меры защиты отключены, повреждение
базы данных может произойти. Ниже приводятся примеры выведения из строя встроенных
механизмов защиты SQLite: Установка
PRAGMA synchronous=OFF может повредить базу данных, если есть катастрофа
операционной системы или перебой в питании, хотя это урегулирование безопасно
от повреждения из-за сбоев приложения. Изменение
PRAGMA schema_version в то время, как другие соединения с
базой данных открыты. Использование
PRAGMA journal_mode=OFF или
PRAGMA journal_mode=MEMORY и сбой приложения
посреди транзакции записи. Установка
PRAGMA writable_schema=ON, а затем изменение схемы базы данных, используя
запросы DML, могут создать абсолютно не читабельную базу данных, если не
сделаны тщательно и грамотно. SQLite очень тщательно проверен, чтобы
гарантировать, что это максимально без ошибок.
Среди многих тестов, которые выполняются для каждой версии SQLite, есть
тесты, которые моделируют перебои в питании, ошибки I/O и ошибки
out-of-memory (OOM), которые проверяют, что никакое повреждение
базы данных не происходит во время какого-либо из этих событий.
SQLite также проверен на практике приблизительно с двумя миллиардами
активного развертывания без серьезных проблем. Тем не менее, никакое программное обеспечение не на 100% надежно.
Было несколько исторических ошибок в SQLite (теперь они исправлены),
которые могли вызвать повреждение базы данных. И может быть еще еще много,
которые остаются неоткрытыми. Из-за обширного тестирования и широкого
использования SQLite, ошибки, которые приводят к повреждению базы данных,
склонны быть очень неясными. Вероятность применения, сталкивающегося с
ошибкой SQLite, маленькая.
Чтобы иллюстрировать это, ниже сделан перечень всех ошибок со сбоями
базы данных, найденных в SQLite за четырехлетний период с 2009-04-01 до
2013-04-15. Этот перечень должен дать читателю интуитивный смысл видов ошибок
в SQLite, которые удается уменьшить через процедуры тестирования. Если база данных написана версией 3.7.0 SQLite или позже и затем
написана снова версией 3.6.23 или ранее таким способом, чтобы сделать размер
файла базы данных меньше, то в следующий раз, когда версия 3.7.0 SQLite
получает доступ к файлу базы данных, это могло бы сообщить, что файл базы
данных поврежден. Файл базы данных не поврежден,
как бы то ни было. Версия 3.7.0 просто была чрезмерно рьяна в
своем обнаружении сбоев. Проблема была решена 2011-02-20. Исправление сначала появляется в
SQLite version 3.7.6 (2011-04-12). Неоднократно переключая базу данных SQLite в и из
режима WAL с выполнением команды VACUUM
между переключениями в одном процессе или потоке можно
заставить другой процесс или поток, у которого есть открытый файл базы
данных, пропустить то, что база данных изменилась.
Тот второй процесс мог бы тогда попытаться изменить базу данных,
используя несвежий кэш и повредить базы данных. Эта проблема была обнаружена во время внутреннего тестирования и никогда
не наблюдалась в дикой природе. Проблема была решена 2011-01-27 и в версии
3.7.5. Если операционная система возвращает ошибку I/O, пытаясь получить
определенную блокировку на общей памяти в режиме WAL,
SQLite мог бы не перезагрузить его кэш, который мог привести к повреждению
базы данных, если далее выполняется запись. Обратите внимание на то, что эта проблема происходит только, если попытка
приобрести блокировку привела к ошибке I/O. Если блокировку просто не
предоставят (потому что некоторый другой поток
или процесс уже держат конфликтную блокировку), тогда никакое повреждение
никогда не будет происходить. Мы не знаем ни о каких операционных системах,
которые потерпят неудачу с ошибкой I/O, в то время как попытка получить файл
соединяет общую память. Таким образом, это теоретическая проблема, а не
настоящая проблема. Само собой разумеется, эта проблема никогда не
наблюдалась в дикой природе. Проблема была обнаружена, делая
стресс-тестирование SQLite, которое моделирует ошибки I/O. Эта проблема была решена 2010-09-20 для версии 3.7.3 SQLite. Когда содержание удалено из базы данных SQLite, страницы, которые
больше не используются, добавляются к списку свободных
и снова используются, чтобы добавить содержание последующими вставками.
Ошибка в SQLite, которая присутствовала в версиях с 3.6.16 до 3.7.2, могла бы
заставить страницы пропадать из списка свободных, когда используется
incremental_vacuum.
Это не вызвало бы потерю данных. Но это привело бы к файлу базы данных,
который больше, чем необходимо. И это заставило бы
integrity_check pragma
объявлять страницы пропавшими без вести из списка свободных. Эта проблема была решена 2010-08-23 для версии 3.7.2 SQLite. SQLite version 3.7.0 ввела много новых улучшений формата файла базы
данных SQLite (например, WAL).
Это был выпуск встряски для этих новых особенностей. Мы ожидали находить
проблемы и не были разочарованы. Если бы база данных была первоначально создана, используя версию
3.7.0 SQLite, то после записи версией 3.6.23.1 SQLite, таким образом, что
размер файла базы данных увеличен, а потом снова написан версией 3.7.0
SQLite, файл базы данных мог быть поврежден. Эта проблема была решена 2010-08-04 для версии 3.7.1 SQLite. SQLite version 3.7.16.2 исправляет тонкое состояние состязания в логике
блокировки на системах Windows. Когда файл базы данных нуждается в
восстановлении, потому что предыдущий процесс, пишущий его,
потерпел крах посреди транзакции, и два или больше процесса пытаются открыть
базу данных в то же время, тогда состояние состязания могло бы заставить один
из тех процессов получать ложное показание, что
восстановление уже закончилось, разрешив тому процессу продолжить
использовать файл базы данных без восстановления сначала. Если тот процесс
пишет файл, то этот файл мог быть поврежден.
Это состояние состязания, по-видимому, существовало во всех предыдущих
версиях SQLite для Windows до 2004 года.
Но гонка была очень трудна. В сущности вам нужна быстрая многоядерная машина,
на которой вы начинаете два процесса, чтобы управлять восстановлением
одновременно на двух отдельных ядрах.
Этот дефект был на системах Windows только и не затрагивал
posix-интерфейс OS. Когда вложенная транзакция начата, используя
SAVEPOINT, SQLite
использует вторичный журнал обратной перемотки, чтобы отследить изменения для
вложенной транзакции, в случае, если внутренняя транзакция должна быть
отменена. Вторичные журналы не вовлечены в защиту базы данных от
повреждения из-за катастроф программы или отключений электроэнергии.
Вторичные журналы играют роль, только отменяя внутреннюю транзакцию
до прежнего уровня вложенной транзакции. Эти вторичные журналы могут быть проведены в памяти или как временные
файлы на диске. Поведение по умолчанию должно сохранить их на диске.
Но это может быть изменено, используя выбор времени компиляции
-DSQLITE_TEMP_STORE
или во время выполнения, используя
PRAGMA temp_store. Ошибка возникает только, когда вторичные
журналы проводятся в памяти. В SQLite version 3.35.0
(2021-03-12) была добавлена новая оптимизация так, чтобы, когда SQLite держит
вторичные журналы в памяти, меньше памяти использовалось.
К сожалению, проверка границы в новой логике была закодирована неправильно.
Оператор "<" написали как "<=". Эта ошибка могла бы заставить вторичный
журнал входить в непоследовательное состояние, если это когда-либо отменяется
до прежнего уровня. Если дополнительные изменения внесены, и внешняя
транзакция в конечном счете передается, базу данных можно было бы
оставить в непоследовательном состоянии. Эта проблема была обнаружена
независимым
исследователем, который пытался найти ошибки в SQLite использованием
fuzzer. fuzzer заметил сбой в assert(),
который используется, чтобы помочь проверить внутреннее состояние вторичного
журнала. Ошибка была достаточно неясным
случаем, поэтому она, возможно, много лет оставалась незамеченной. Эта проблема была
решена
в version 3.37.2 (2022-01-06).
Choose any three.
Обзор
1. Файл переписывается вражеским процеесом
1.1. Продолжение использования дескриптора файла после того,
как это было закрыто
1.2.
Сделайте копию или восстановите, в то время как транзакция работает
1.3.
Удаление горячего журнала
1.4.
Файлы базы данных и горячие журналы повреждены
2. Проблемы захвата файла
2.1. Файловые системы со сломанными или
недостающими внедрениями блокировки
2.2. Консультативные блокировки Posix, отмененные отдельным
потоком через close()
2.2.1. Многочисленные копии SQLite связаны в то же самое приложение
2.3. Два процесса работают, используя
различные протоколы блокировки
2.4. Расцепление или переименование файла базы данных
во время работы
2.5.
Многократные связи с тем же самым файлом
2.6. Перенос открытого соединения с базой данных через fork()
3. Сбои синхронизации
3.1. Дисководы, которые не соблюдают синхронизирующие запросы
3.2.
Отключение синхронизации через PRAGMA
4.
Дисковод и отказы флэш-памяти
4.1. Диспетчеры флэш-памяти небезопасны по питанию
4.2.
Ложные возможности USB
5. Сбои памяти
6.
Другие проблемы операционной системы
6.1. Потоки Linux
6.2.
Сбои mmap() в QNX
6.3. Сбои файловой системы
7.
Ошибки конфигурации SQLite
8. Ошибки в SQLite
8.1. Ошибки из-за сжатия базы данных
8.2. Повреждения после переключения между
обратной перемоткой и WAL
8.3. Ошибка I/O, получая блокировку
8.4.
Страницы базы данных просачиваются из списка свободных страниц
8.5. Сбой после перехода записи с 3.6 на 3.7
8.6.
Состояние состязания в восстановлении на системах Windows
8.7. Ошибка граничного значения во вторичных
журналах с вложенными транзакциями