From d55dbb85d8a7917487c54f118752e58a8fff452d Mon Sep 17 00:00:00 2001 From: Netcrafts Date: Sat, 23 May 2026 12:57:42 +0200 Subject: [PATCH] fix: skip action rows with extra_data exceeding MySQL TEXT column limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When deeply-nested item NBT (e.g. a shulker box containing 27 beehives each with 3 named/potioned bees and long lore) is serialised, the resulting string can exceed 65,535 bytes — the MySQL TEXT column limit for extra_data. This caused the entire batchInsert to fail and the execute() retry wrapper to spin up to MAX_QUERY_RETRIES times on the same unresolvable batch, halting all logging on the server. Partition the action list before batchInsert: any row whose extraData exceeds MAX_EXTRA_DATA_BYTES (65,535) is dropped and a WARN is emitted with the action type, coordinates, world, and player name so operators can identify the trigger. The remaining rows are inserted normally. --- .../ledger/database/DatabaseManager.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt b/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt index b3346ffb..44654d77 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt @@ -73,6 +73,7 @@ import kotlin.math.ceil const val MAX_QUERY_RETRIES = 10 const val MIN_RETRY_DELAY = 1000L const val MAX_RETRY_DELAY = 300_000L +private const val MAX_EXTRA_DATA_BYTES = 65_535 object DatabaseManager { @@ -456,7 +457,18 @@ object DatabaseManager { } private fun Transaction.insertActions(actions: List) { - Tables.Actions.batchInsert(actions, shouldReturnGeneratedValues = false) { action -> + val (safe, oversized) = actions.partition { + it.extraData == null || it.extraData!!.length <= MAX_EXTRA_DATA_BYTES + } + oversized.forEach { action -> + logWarn( + "Skipping action log: extra_data too large (${action.extraData!!.length} chars) " + + "for action ${action.identifier} at " + + "[${action.world} ${action.pos.x} ${action.pos.y} ${action.pos.z}] " + + "by ${action.sourceProfile?.name ?: action.sourceName}", + ) + } + Tables.Actions.batchInsert(safe, shouldReturnGeneratedValues = false) { action -> this[Tables.Actions.actionIdentifier] = getOrCreateActionId(action.identifier) this[Tables.Actions.timestamp] = action.timestamp this[Tables.Actions.x] = action.pos.x