From 85d104269f8df2cb44707b21ab69aecd5e5c5f86 Mon Sep 17 00:00:00 2001 From: Sven Date: Thu, 17 Sep 2026 23:25:12 +0200 Subject: [PATCH] Prevent deadlock in internalAddToPlayerShards by introducing internalGetShardTypeShards --- .../diamondbankog/api/DiamondBankAPIKotlin.kt | 15 ++++++++++----- .../diamondbankog/persistence/PostgreSQL.kt | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/net/trueog/diamondbankog/api/DiamondBankAPIKotlin.kt b/src/main/kotlin/net/trueog/diamondbankog/api/DiamondBankAPIKotlin.kt index 0734508..3d77675 100644 --- a/src/main/kotlin/net/trueog/diamondbankog/api/DiamondBankAPIKotlin.kt +++ b/src/main/kotlin/net/trueog/diamondbankog/api/DiamondBankAPIKotlin.kt @@ -35,7 +35,8 @@ class DiamondBankAPIKotlin { return transactionLock.withLockSuspend(uuid) { val player = Bukkit.getPlayer(uuid) ?: Bukkit.getOfflinePlayer(uuid) - if (player !is Player && !player.hasPlayedBefore()) return@withLockSuspend Result.failure(InvalidPlayerException()) + if (player !is Player && !player.hasPlayedBefore()) + return@withLockSuspend Result.failure(InvalidPlayerException()) balanceManager.addToBankShards(uuid, shards.toLong()).getOrElse { handleError(it) @@ -68,7 +69,8 @@ class DiamondBankAPIKotlin { return transactionLock.withLockSuspend(uuid) { val player = Bukkit.getPlayer(uuid) ?: Bukkit.getOfflinePlayer(uuid) - if (player !is Player && !player.hasPlayedBefore()) return@withLockSuspend Result.failure(InvalidPlayerException()) + if (player !is Player && !player.hasPlayedBefore()) + return@withLockSuspend Result.failure(InvalidPlayerException()) balanceManager.subtractFromBankShards(uuid, shards.toLong()).getOrElse { if (it is InsufficientBalanceException) return@withLockSuspend Result.failure(it) @@ -161,7 +163,8 @@ class DiamondBankAPIKotlin { return transactionLock.withLockSuspend(uuid) { val player = Bukkit.getOfflinePlayer(uuid) - if (player !is Player && !player.hasPlayedBefore()) return@withLockSuspend Result.failure(InvalidPlayerException()) + if (player !is Player && !player.hasPlayedBefore()) + return@withLockSuspend Result.failure(InvalidPlayerException()) player.uniqueId .withInventoryLockSuspend { @@ -220,10 +223,12 @@ class DiamondBankAPIKotlin { return transactionLock.withLockSuspend(senderUuid) { val sender = Bukkit.getPlayer(senderUuid) ?: Bukkit.getOfflinePlayer(senderUuid) - if (sender !is Player && !sender.hasPlayedBefore()) return@withLockSuspend Result.failure(InvalidPlayerException()) + if (sender !is Player && !sender.hasPlayedBefore()) + return@withLockSuspend Result.failure(InvalidPlayerException()) val receiver = Bukkit.getPlayer(receiverUuid) ?: Bukkit.getOfflinePlayer(receiverUuid) - if (receiver !is Player && !receiver.hasPlayedBefore()) return@withLockSuspend Result.failure(InvalidPlayerException()) + if (receiver !is Player && !receiver.hasPlayedBefore()) + return@withLockSuspend Result.failure(InvalidPlayerException()) sender.uniqueId .withInventoryLockSuspend { diff --git a/src/main/kotlin/net/trueog/diamondbankog/persistence/PostgreSQL.kt b/src/main/kotlin/net/trueog/diamondbankog/persistence/PostgreSQL.kt index 13aa1d4..9c7c654 100644 --- a/src/main/kotlin/net/trueog/diamondbankog/persistence/PostgreSQL.kt +++ b/src/main/kotlin/net/trueog/diamondbankog/persistence/PostgreSQL.kt @@ -139,7 +139,7 @@ internal class PostgreSQL private constructor() { .await() if (result.rows.isEmpty()) { - val shards = getShardTypeShards(uuid, type).getOrElse { throw it } + val shards = internalGetShardTypeShards(uuid, type, conn).getOrElse { throw it } throw InsufficientBalanceException(shards) } val row = result.rows[0] @@ -271,12 +271,21 @@ internal class PostgreSQL private constructor() { } suspend fun getShardTypeShards(uuid: UUID, type: ShardType): Result { - var shards: Long? try { val connection = pool.asSuspending.connect() + return internalGetShardTypeShards(uuid, type, connection) + } catch (e: Exception) { + plugin.logger.severe(e.toString()) + return Result.failure(DatabaseException(e.message ?: "Database exception")) + } + } + + private suspend fun internalGetShardTypeShards(uuid: UUID, type: ShardType, conn: Connection): Result { + var shards: Long? + try { val result = - connection + conn .inTransaction { conn -> conn.sendPreparedStatement( "SELECT ${type.string} FROM diamond WHERE uuid = ? LIMIT 1",