Когда записи фиксируются на диске с помощью SQLite.swift?

Я изо всех сил пытаюсь увидеть, как некоторые INSERT отражаются в базе данных SQLite при использовании SQLite.swift.

Я отлаживаю свое приложение iOS с помощью точек останова и после вставки экспортирую контейнер приложения (с устройства) на жесткий диск хоста. Затем я проверяю базу данных SQLite, чтобы увидеть изменения. INSERT не отражаются таким образом.

Если я добавлю дополнительный код, такой как SELECT, в таблицу или перезапущу приложение, то запись будет там, поэтому похоже, что консолидация на диск на устройстве происходит не сразу, а в какой-то другой момент. Я использую один объект Connection и нет одновременных доступов, поэтому я не ожидаю проблем с потоками. Я также пытался использовать INSERT внутри транзакции, но все еще не отражал контейнер.

Когда изменения консолидируются на диск, чтобы он действительно сохранялся? Есть ли способ принудительно сбросить? Что произойдет, если приложение неожиданно завершится, могут ли данные быть потеряны, потому что на самом деле их не было в базе данных?


person atineoSE    schedule 12.07.2018    source источник


Ответы (2)


Как правило, на уровне C-API результаты кэшируются только при использовании транзакции. На этом уровне sqlite3_reset() и/или sqlite3_finalize() закроют оператор и вызовут его запись. Я предполагаю, что вы не используете транзакции.

Я нашел в Интернете несколько сообщений, в которых предлагается оставить открытым активный запрос к базе данных может помешать сбросу кеша в БД. Однако, не видя вашего кода, невозможно узнать, происходит ли это.

Вы можете явно BEGIN транзакцию и COMMIT принудительно сбросить, если хотите.

Еще нужно проверить значение PRAGMA SYNCHRONOUS (документы). Обычно он установлен на FULL (2), но вы можете попробовать установить его на EXTRA (3).

Обновлять:

Я попытался воспроизвести то, что вы описываете, с помощью SQLite.swift, но записи всегда мгновенны и сразу доступны для чтения из другого соединения. Вот код, который я написал:

import UIKit
import SQLite

class ViewController: UIViewController {

    private static let REUSE_DB_CONN:Bool = true
    private static let DB_NAME = "SQLiteFlushTester.sqlite3"
    let pragma:String? = "PRAGMA foreign_keys = ON;"

    var connection:Connection?

    let fooTable = Table("FooTable")
    let id = Expression<Int64>("id")

    func dbDefaultPath(name: String = ViewController.DB_NAME) -> String {
        let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!

        let fmgr = FileManager.default
        if (!fmgr.fileExists(atPath: path)) {
            print("Directory does not exist at \(path)")
            do {
                try fmgr.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
            } catch {
                print("Could not create directory \(path)")
            }
        }

        return "\(path)/\(name)"
    }

    func dbConn() -> Connection? {
        if let connection = connection {
            print("Recycling connection")
            return connection
        }

        let dbPath = dbDefaultPath()
        do {
            let dbConn = try Connection(dbPath)
            if let pragma = pragma {
                try dbConn.execute(pragma)
            }

            if ( ViewController.REUSE_DB_CONN ) {
                connection = dbConn
            }
            return dbConn
        } catch let error {
            print("Error opening DB \(error)?")
            return nil
        }
    }

    func createTestTable(_ db:Connection) throws {
        try db.run(fooTable.create { t in
            t.column(id, primaryKey: true)
        })
    }

    func insertIdIntoFoo(_ db:Connection) {
        let randval:Int64 = Int64(arc4random_uniform(10000))

        do {
            let insert = fooTable.insert(id <- randval)
            let rowid = try db.run(insert)
            print("Inserted to row id \(rowid)")
        } catch let error {
            print("Error inserting value: \(error)")
            return
        }
    }

    @IBAction func doAnInsert(_ sender: Any) {
        guard let db = dbConn() else {return}
        insertIdIntoFoo(db)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let db = dbConn() else { return }

        do {
            try createTestTable(db)
        } catch let error {
            print("Error creating table: \(error)")
            return
        }
    }
}

Я открыл БД из инструмента командной строки sqlite3 и наблюдал за обновлениями, нажимая кнопку на моем ViewController, которая запускала IBAction, и строка немедленно отражалась в БД. Повторное использование соединения (или нет) не имело значения (см. флаг REUSE_DB_CONN).

person David S.    schedule 12.07.2018
comment
Спасибо, вот еще информация. Дело в том, чтобы выполнять весь доступ через инфраструктуру SQLite.swift, поэтому я ожидаю, что мне не придется напрямую обращаться к sqlite3. Я предполагаю, что такие вещи идут под капотом. Я пробовал транзакцию, тот же результат. - person atineoSE; 12.07.2018
comment
Очень полезный пример кода. Спасибо. Незначительная вещь заключается в том, что вы пропустили конечную 1 в строке try createTestTable(db). - person atineoSE; 16.07.2018

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

  1. Убедитесь, что вы контролируете поток данных, сериализуя доступ к базе данных.
  2. Убедитесь, что база данных открыта все время, пока выполняется процесс вставки, иначе это создаст проблему. Если вы открываете и закрываете базу данных каждый цикл цикла, это проблема.

Хорошего дня!

person Jorge Cardenas    schedule 22.08.2018