Восстановление SQLite через Deserialize в Go: пошаговое руководство

    При работе с SQLite в Go разработчики часто сталкиваются с ситуацией, когда метод Deserialize не восстанавливает базу данных из среза байт, хотя сохранение в файл и последующее восстановление через NewRestore работают корректно. Это происходит из-за особенностей внутреннего состояния соединения и неправильного использования API. В этой статье разберём, как правильно выполнить десериализацию SQLite без обращения к файловой системе.

    Почему Deserialize не работает?

    Ключевая проблема в исходном коде - вызов Deserialize на уже открытом соединении dw.DB. По документации modernc.org/sqlite, метод Deserialize предназначен для замены содержимого базы данных в памяти, но он требует, чтобы соединение было создано с нуля, либо чтобы база была пустой. При вызове на активном соединении с существующими данными операция может игнорироваться или завершаться ошибкой.

    Дополнительно, использование conn.Raw с передачей driverConn не гарантирует, что внутреннее состояние SQLite будет корректно сброшено. В результате Deserialize не применяется, и база остаётся в прежнем виде.

    Правильный способ: создание нового соединения

    Чтобы восстановить базу через Deserialize, необходимо открыть новое соединение к базе в памяти и сразу применить десериализацию. Вот рабочий пример:

    func (dw *databaseWrapper) restoreBackup(ctx context.Context, data []byte) error {
        // Создаём новое in-memory соединение
        db, err := sql.Open("sqlite", ":memory:")
        if err != nil {
            return err
        }
        defer db.Close()
    
        conn, err := db.Conn(ctx)
        if err != nil {
            return err
        }
        defer conn.Close()
    
        err = conn.Raw(func(driverConn any) error {
            return driverConn.(sqliteBackuper).Deserialize(data)
        })
        if err != nil {
            return err
        }
    
        // Заменяем текущее соединение на новое
        dw.DB.Close()
        dw.DB = db
        return nil
    }

    Здесь мы создаём отдельную in-memory базу, десериализуем в неё данные и затем заменяем исходное соединение. Это гарантирует, что Deserialize применится к пустой базе.

    Альтернатива: сброс и повторное открытие

    Если вы не хотите создавать новое соединение, можно закрыть текущее и открыть заново с использованием sql.Open с тем же источником данных. Однако для in-memory баз этот подход не подходит, так как данные теряются. Поэтому рекомендуется первый вариант.

    Проверка результата

    После десериализации убедитесь, что данные корректно загружены. Выполните простой запрос, например, SELECT count(*) FROM sqlite_master. Если таблицы присутствуют - восстановление прошло успешно.

    Распространённые ошибки

    • Использование Deserialize на уже заполненной базе - данные не перезаписываются, метод игнорируется.
    • Игнорирование ошибки возврата - Deserialize может вернуть ошибку, которую нужно обработать.
    • Работа с закрытым соединением - убедитесь, что соединение открыто до вызова Raw.

    Заключение

    Метод Deserialize в modernc.org/sqlite - мощный инструмент для быстрого восстановления базы из памяти. Главное правило: применяйте его только к новому, пустому соединению. Это избавит от лишних операций ввода-вывода и ускорит работу приложения.

    Часто задаваемые вопросы