JetBrains / Exposed

Kotlin SQL Framework
http://jetbrains.github.io/Exposed/
Apache License 2.0
8.34k stars 696 forks source link

How do I perform async transactions on a sqlite database without locking issues? #982

Open ChimpGamer opened 4 years ago

ChimpGamer commented 4 years ago

At the moment I keep getting: org.sqlite.SQLiteException: [SQLITE_BUSY] The database file is locked (database is locked) while trying to perform transactions async to the sqlite database. How can I prevent this from happening? I've tried a couple of things but without success. If someone could push me in the right direction I would really appreciate that.

Tapac commented 4 years ago

I guess it's worth to try something from SO or make a global lock and work with a single connection.

utybo commented 4 years ago

FWIW, even when enabling the options suggested in SO, there are still locking issues. Here's an example using coroutines:

Database.connect(                                                          
    "jdbc:sqlite:$db",                                                                       
    driver = "org.sqlite.JDBC",                                                              
    setupConnection = {                                                                      
        SQLiteConfig().apply {
            // Some options that could help with this but don't                                                               
            setSharedCache(true)                                                             
            setJournalMode(SQLiteConfig.JournalMode.WAL)                                     
            setLockingMode(SQLiteConfig.LockingMode.EXCLUSIVE)                               
            apply(it)                                                                        
        }                                                                                    
    }                                                                                        
).apply {                                                                                    
    // Required for SQLite                                                                   
    transactionManager.defaultIsolationLevel =                                               
        Connection.TRANSACTION_SERIALIZABLE                                                  

    transaction { SchemaUtils.create(TestTable) }                                            

    runBlocking(Dispatchers.Default) {                                                       

        List(100) { i ->                                                           
            async {                                                                          
                newSuspendedTransaction {                                                    
                    TestTable.insert {                                                       
                        it[string] = "Le Test $i.1"                                          
                    }                                                                        
                }                                                                            
                delay(20)                                                                    
                newSuspendedTransaction {                                                    
                    TestTable.insert {                                                       
                        it[string] = "Le Test $i.2"                                         
                    }                                                                        
                }                                                                            
            }                                                                                
        }.awaitAll()                                                                         
    }                                                                                        
}                                                                                            

object TestTable : IntIdTable("Test") {
    val string = varchar("test_string", 250)
}

Is there anything that can help with this inside Exposed?