Как вызвать новое соединение с базой данных из соединения с базой данных только для чтения в GRDB? - PullRequest
1 голос
/ 09 июля 2019

Функция ниже возвращает запись Ledgers.Большую часть времени он находит его в необязательной переменной _currentReceipt или при поиске в базе данных, там не нужно писать.Я хотел бы использовать подключение базы данных GRDB только для чтения.Соединения с базой данных только для чтения могут выполняться параллельно в разных потоках.

В редких случаях первые два шага не выполняются, я могу создать книгу по умолчанию.Вызов try FoodyDataStack.thisDataStack.dbPool.write { writeDB in ... вызовет фатальную ошибку. Соединения с базой данных не реентерабельны.Я ищу способ сохранить этот регистр по умолчанию без необходимости заключать всю функцию в соединение для чтения и записи.

Могу ли я вызвать NSOperation в отдельной очереди из блока GRDB .read?

class func getCurrentReceipt(db: Database) throws -> Ledgers {
        if let cr = FoodyDataStack.thisDataStack._currentReceipt {
            return cr
        }
        // Fall through
        do {
            if let cr = try Ledgers.filter(Ledgers.Columns.receiptClosed == ReceiptStatus.receiptOpen.rawValue).order(Ledgers.Columns.dateModified.desc).fetchOne(db) {
                FoodyDataStack.thisDataStack._currentReceipt = cr
                return cr
            } else {
                throw FoodyDataStack.myGRDBerrors.couldNotFindCurrentReceipt
            }
        } catch FoodyDataStack.myGRDBerrors.couldNotFindCurrentReceipt {
            // Create new receipt with default store
            let newReceipt = Ledgers()
            newReceipt.dateCreated = Date()
            newReceipt.dateModified = Date()
            newReceipt.receiptStatus = .receiptOpen
            newReceipt.receiptUrgency = .immediate
            newReceipt.dateLedger = Date()
            newReceipt.uuidStore = Stores.defaultStore(db).uuidKey
            FoodyDataStack.thisDataStack._currentReceipt = newReceipt
            return newReceipt
        } catch  {
            NSLog("WARNING: Unhandled error in Ledgers.getCurrentReceipt() \(error.localizedDescription)")
        }
    }

Редактировать: я оставляю этот вопрос здесь, но я думаю, что я иду к преждевременной оптимизации.Я собираюсь попробовать dbQueue вместо dbPool и посмотреть, какова производительность.Я вернусь к dbPool, если скорость этого потребует.

1 Ответ

1 голос
/ 23 июля 2019

Методы доступа к базе данных GRDB не являются реентерабельными (методы чтения и записи в DatabaseQueue и DatabasePool).

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

Первый уровень не доступен для остальной части приложения. Все его методы принимают аргумент db: Database.

class MyStack {
    private var dbQueue: DatabaseQueue

    private func fetchFoo(_ db: Database, id: Int64) throws -> Foo? {
        return try Foo.fetchOne(db, key: id)
    }

    private func setBar(_ db: Database, foo: Foo) throws {
        try foo.updateChanges(db) {
            $0.bar = true
        }
    }
}

Методы второго уровня доступны для остальной части приложения. Они заключают в себе методы первого уровня в read и write методах доступа к базе данных:

class MyStack {
    func fetchFoo(id: Int64) throws -> Foo? {
        return try dbQueue.read { db in
            try fetchFoo(db, id: id)
        }
    }

    func setBar(id: Int64) throws {
        try dbQueue.write { db in
            guard let foo = try fetchFoo(db) else {
                throw fooNotFound
            }
            try setBar(foo: foo)
        }
    }
}

Методы первого уровня могут быть настолько низкими, насколько это необходимо, и могут быть составлены .

Методы второго уровня являются высокоуровневыми и не поддаются компоновке: они не могут вызывать друг друга из-за фатальной ошибки «Соединения с базой данных не реентерабельные». Они предоставляют поточно-ориентированные методы базы данных, которые гарантируют согласованность базы данных.

Для получения дополнительной информации см. Руководство по параллелизму .

...