Site icon AppTractor

Восстановление базы для миллионов пользователей

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

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

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

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

Для пользователей мессенджеров, особенно нашего, сообщения были воспоминаниями. Потеря сообщений означала потерю ценных воспоминаний.

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

Однако базы данных SQLite в резервных копиях были сломаны. Они не загружались ни через Sqlite на Android, ни через любой Sqlite-браузер на Mac OS. Однако их можно было открыть в SQLite Shell. И до тех пор, пока запросы не пытались затронуть битую строку, они работали успешно. Работали даже агрегатные запросы типа SELECT COUNT(*).

В одном случае нарушались ограничения первичного ключа. В нескольких других случаях встречались неправильные символы Юникода. Хотя я пытался разобраться в ситуации, мне не удалось найти причину этих ошибок. Дешевые телефоны Android — странные звери. Я регулярно сталкивался с ошибками памяти, поэтому эти повреждения базы данных меня не удивили.

Но как найти и устранить такие поврежденные записи программно?

RTFM — прочитайте наконец руководство

Я продолжал читать руководство по SQLite Shell снова и снова. Пока не понял, что в принципе могу сделать дамп базы данных SQLite в виде последовательности SQL-команд, которая точно воспроизводит базу данных.

Получение дампа выглядел следующим образом.

BEGIN TRANSACTION
CREATE TABLE ...
INSERT INTO ...
... (100,000-1,000,000 rows)
END TRANSACTION

И этот дамп работал даже для тех сломанных баз данных!

Мы решили, что потеря нескольких плохих сообщений ради восстановления всего остального будет приемлемым компромиссом. Но как это сделать?

Что, если я смогу определить плохие строки и удалить их? В сообщениях пользователя содержатся новые строки, поэтому я не могу аккуратно отделить их программно. А поскольку все происходило в транзакции, она откатывалась, как только возникала первая ошибка.

Продолжая копаться в руководстве, я обнаружил:

-- The .bail off command tells SQLite to continue executing statements even if errors occur
.bail off

Вуаля!

Исправление

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

// Dump database into SQL commands using SQLite shell
// Remove the first 'BEGIN TRANSACTION' and the last 'END TRANSACTION'
// Setup an empty database with all the tables and correct constraints
// Start SQLite shell in `.bail off` mode
// Restore the SQL commands from step 2 into the database using SQLite shell
if (failedToRestore) {
  setupEmptyDatabase();
  startSqliteShellAndRecover();
}

Это происходило, когда пользователь сталкивался с повреждением базы данных, и завершалось в течение 5-60 секунд.

Это действительно сработало

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

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

Финальный слой восстановления базы данных состоял из ~200 строк кода. Возможно, самый маленький в мире. Сегодня он установлен на миллиардах устройств, готовых спасти их пользователей от повреждения базы данных.

Источник

Exit mobile version