Для собственного удовольствия у меня на личном компьютере крутится робот для Википедии (аккаунт1, аккаунт2, исходный код). Бот держит локальный кеш версий страниц Википедии — чтобы не ходить каждый раз на удалённый сервер за ними, а также набор специфичных данных, которые собирались последние пару лет и очень важны для работы бота. Данные собираются в базу данных под управлением Apache Derby, и, вместе с кешем, БД занимает около 50 Гб.
И вот, в один прекрасный выходной день, когда бот обрабатывал данные в 8 потоков на 4-х CPU, Abbyy Finereader распознавал 14-ый том русского биографического словаря под редакцией А. А. Половцева, а противники делали свой ход в Civilization Age of Kings… возник он — синий экран смерти. Давненько не виделись, подумал я, перезагружая компьютер. С причиной ладно — скорее всего проблемы с видеоадаптером на аппаратной почве. Вот только когда компьютер загрузился и я попробовал запустить бота ещё раз, возникло это:
ERROR XSDG2: Invalid checksum on Page Page
А прошлый бэкап, как обычно, датирован мартом месяцем…
Потратив полчаса-час на исследование проблемы (читай — поиск в Google), выяснил что:
- Утилит для восстановления данных нет. Никаких. Пользуйтесь backup'ами — это официальный единственный способ.
- Есть предложенные патчи, которые позволяют обойти проверку checksum для конкретной страницы. Например, DERBY-1648
- Наконец, есть способ просто игнорировать восстановление данных. Да, ваши глаза Вас не обманывают — именно восстановление. Дело в том, что после неправильного завершения Apache Derby читает лог транзакций и, как всякая уважающая себя ACID-совместимая СУБД пытается откатить незавершившиеся транзакции. Вот только не выходит у неё — вот она и ругается.
Как сделать последнее? Просто удалить логи! Однако, как предупреждает нас IBM (разработчик Cloudscape — то, что потом стало Derby):
Never delete or manipulate *ANY* files in a Derby database directory structure. Doing so will corrupt the database.
Итак, чем же я занимался в ночь с субботы на воскресение?
- Убедился, что на жёстком диске места больше, чем в 3 раза больше текущего размера базы.
- Сделал backup неработающей базы. Лучше поздно, чем никогда.
- Вознеся молитву Летающему Макаронному Монстру, удалил все файлы из папки logs (саму папку нужно оставить — без неё не загрузится).
- Зашёл через SQuirreL SQL Client в базу данных и убедился, что она работает.
Но это, разумеется, не всё. Нужно понимать, что удаление транзакционных логов, тем более с ошибками восстановления в базе данных, ни к чему хорошему привести не может. Работать такая база будет, но не долго — до первого встречного косяка в структуре. Надо как-то заставить базу данных перелопатить все данные во всех таблицах, желательно — перезаписать их в новый файлы построчно (в терминологии Derby — новые конгломераты).
Встроенный в Apache Derby backup-restore механизм нам не поможет — он тупо копирует файлы с данными, никак не проверяя их структуру. Однако, в состав поставки входит интересная утилита под названием ij. Кроме того, что она даёт консольный доступ к базе данных, она ещё и позволяет вызывать функции, которые отсутствуют при доступе к базе данных из других приложений. Нам потребуются две такие функции из набора Export & Import:
- SYSCS_UTIL.SYSCS_EXPORT_QUERY_LOBS_TO_EXTFILE
- SYSCS_UTIL.SYSCS_IMPORT_TABLE_LOBS_FROM_EXTFILE
Эти функции делают экспорт (и импорт) данных в стандартный текстовый формат. Дополнительный префикс LOBS_FROM_EXTFILE указывает на то, что двоичные и просто большие текстовые поля будут сохранены в отдельный файл. Это удобно, если хотите хотя бы краем глаза просмотреть данные на корректность.
Поэтому следующим этапом был запуск ij…
java -cp "derby.jar;derbytools.jar" -Dij.maximumDisplayWidth=120 org.apache.derby.tools.ij
конненкт к базе данных (не забывайте точку с запятой в конце команды для ij)
ij>connect secretarydb;
экспорт всех данных приложения (отдельные команды на каждую таблицу)
ij>CALL SYSCS_UTIL.SYSCS_EXPORT_TABLE_LOBS_TO_EXTFILE(null,'TABLENAME','c:dataTABLENAME.del',null,null,'UTF-8', 'c:dataTABLENAME.dat');
Кстати, команда «SHOW TABLES» в ij есть.
После завершения экспорта можно взять любимый текстовый редактор, который бы поддерживал соответствующие размеры данных, и проверить читаемость как минимум начала и конца файла. Из файла .del лишние строки можно удалить, если что-то окажется не так… в общем, ни в чем себе не отказывайте — это как раз тот момент, когда можно отредактировать баланс на счету паре абонентов.
После завершения удаляем базу данных. Если что, backup у нас есть. Потом мне было достаточно запустить бота ещё раз, и, сделанный на hibernate, он сам при запуске восстановил структуру таблиц. После этого сразу же останавливаем приложение и запускаем ij для импорта данных:
java -cp "derby.jar;derbytools.jar" -Dij.maximumDisplayWidth=120 org.apache.derby.tools.ij
ij>connect secretarydb;
ij>CALL SYSCS_UTIL.SYSCS_IMPORT_TABLE_LOBS_TO_EXTFILE(null,'TABLENAME','c:dataTABLENAME.del',null,null,'UTF-8', 1);
Единичка в конце указывает, что данные в таблице нужно перезаписать, если там что-то уже есть. Указывать имя файла с LOB-данными не нужно — ссылки на него есть в основном файле.
На этом всё. Данные восстановлены. Но, конечно, следующая функция, которая будет в боте — это автоматическая архивация наиболее важных данных при каждом запуске.
Автор: vlsergey