Skip to content

Commit

Permalink
Fix deleteDatabase blocking due to open connections (#144)
Browse files Browse the repository at this point in the history
- Automatically close database connections on delete
- Handle "blocked" event in deleteDatabase
  • Loading branch information
janne-koschinski committed Jan 31, 2024
1 parent 2d62d59 commit 9102f14
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 10 deletions.
23 changes: 17 additions & 6 deletions core/src/jsMain/kotlin/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,25 @@ public suspend fun openDatabase(
public suspend fun deleteDatabase(name: String) {
val factory = checkNotNull(window.indexedDB) { "Your browser doesn't support IndexedDB." }
val request = factory.deleteDatabase(name)
request.onNextEvent("success", "error") { event ->
request.onNextEvent("success", "error", "blocked") { event ->
when (event.type) {
"error" -> throw ErrorEventException(event)
"error", "blocked" -> throw ErrorEventException(event)
else -> null
}
}
}

public class Database internal constructor(internal val database: IDBDatabase) {
public class Database internal constructor(database: IDBDatabase) {
private var database: IDBDatabase? = database

init {
// listen for database structure changes (e.g., upgradeneeded while DB is open or deleteDatabase)
database.addEventListener("versionchange", { close() })
// listen for force close, e.g., browser profile on a USB drive that's ejected or db deleted through dev tools
database.addEventListener("close", { close() })
}

internal fun ensureDatabase(): IDBDatabase = checkNotNull(database) { "database is closed" }

/**
* Inside the [action] block, you must not call any `suspend` functions except for:
Expand All @@ -68,7 +78,7 @@ public class Database internal constructor(internal val database: IDBDatabase) {
action: suspend Transaction.() -> T,
): T = withContext(Dispatchers.Unconfined) {
val transaction = Transaction(
database.transaction(arrayOf(*store), "readonly", transactionOptions(durability)),
ensureDatabase().transaction(arrayOf(*store), "readonly", transactionOptions(durability)),
)
val result = transaction.action()
transaction.awaitCompletion()
Expand All @@ -87,7 +97,7 @@ public class Database internal constructor(internal val database: IDBDatabase) {
action: suspend WriteTransaction.() -> T,
): T = withContext(Dispatchers.Unconfined) {
val transaction = WriteTransaction(
database.transaction(arrayOf(*store), "readwrite", transactionOptions(durability)),
ensureDatabase().transaction(arrayOf(*store), "readwrite", transactionOptions(durability)),
)
with(transaction) {
// Force overlapping transactions to not call `action` until prior transactions complete.
Expand All @@ -101,7 +111,8 @@ public class Database internal constructor(internal val database: IDBDatabase) {
}

public fun close() {
database.close()
database?.close()
database = null
}
}

Expand Down
8 changes: 4 additions & 4 deletions core/src/jsMain/kotlin/Transaction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -300,18 +300,18 @@ public class VersionChangeTransaction internal constructor(

/** Creates an object-store that uses explicit out-of-line keys. */
public fun Database.createObjectStore(name: String): ObjectStore =
ObjectStore(database.createObjectStore(name))
ObjectStore(ensureDatabase().createObjectStore(name))

/** Creates an object-store that uses in-line keys. */
public fun Database.createObjectStore(name: String, keyPath: KeyPath): ObjectStore =
ObjectStore(database.createObjectStore(name, keyPath.toWrappedJs()))
ObjectStore(ensureDatabase().createObjectStore(name, keyPath.toWrappedJs()))

/** Creates an object-store that uses out-of-line keys with a key-generator. */
public fun Database.createObjectStore(name: String, autoIncrement: AutoIncrement): ObjectStore =
ObjectStore(database.createObjectStore(name, autoIncrement.toJs()))
ObjectStore(ensureDatabase().createObjectStore(name, autoIncrement.toJs()))

public fun Database.deleteObjectStore(name: String) {
database.deleteObjectStore(name)
ensureDatabase().deleteObjectStore(name)
}

public fun ObjectStore.createIndex(name: String, keyPath: KeyPath, unique: Boolean): Index =
Expand Down

0 comments on commit 9102f14

Please sign in to comment.