Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database interruption #659

Merged
merged 15 commits into from
Dec 1, 2019
Merged

Database interruption #659

merged 15 commits into from
Dec 1, 2019

Conversation

groue
Copy link
Owner

@groue groue commented Nov 30, 2019

This pull request introduces a new interrupt() method:

dbQueue.interrupt()
dbPool.interrupt()

This method causes any pending database operation to abort and return at its earliest opportunity.

It can be called from any thread.

A call to interrupt() that occurs when there are no running SQL statements is a no-op and has no effect on SQL statements that are started after interrupt() returns.

A database operation that is interrupted will throw a DatabaseError with code SQLITE_INTERRUPT. If the interrupted SQL operation is an INSERT, UPDATE, or DELETE that is inside an explicit transaction, then the entire transaction will be rolled back automatically. If the rolled back transaction was started by a transaction-wrapping method such as DatabaseWriter.write or Database.inTransaction, then all database accesses will throw a DatabaseError with code SQLITE_ABORT until the wrapping method returns.

For example:

try dbQueue.write { db in
    // interrupted:
    try Player(...).insert(db)     // throws SQLITE_INTERRUPT
    // not executed:
    try Player(...).insert(db)
}                                  // throws SQLITE_INTERRUPT

try dbQueue.write { db in
    do {
        // interrupted:
        try Player(...).insert(db) // throws SQLITE_INTERRUPT
    } catch { }
    try Player(...).insert(db)     // throws SQLITE_ABORT
}                                  // throws SQLITE_ABORT

try dbQueue.write { db in
    do {
        // interrupted:
        try Player(...).insert(db) // throws SQLITE_INTERRUPT
    } catch { }
}                                  // throws SQLITE_ABORT

A DatabaseError of code SQLITE_ABORT is generally thrown when the application accesses the database, inside a transaction-wrapping method, if the transaction was ended prematurely. This may happen due to interrupt(), but also, for example, from an ON CONFLICT ROLLBACK clause.

When an application creates transaction without a transaction-wrapping method, no SQLITE_ABORT error warns of aborted transactions:

try dbQueue.inDatabase { db in // or dbPool.writeWithoutTransaction
    try db.beginTransaction()
    do {
        // interrupted:
        try Player(...).insert(db) // throws SQLITE_INTERRUPT
    } catch { }
    try Player(...).insert(db)     // success
    try db.commit()                // throws SQLITE_ERROR "cannot commit - no transaction is active"
}

@groue groue merged commit 1fb12a2 into development Dec 1, 2019
@groue groue deleted the dev/interrupt branch December 1, 2019 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant