3.8.8.4
Changes
- fix: use
plugin.runAsync()inrunTrackedAsync()instead of bareCompletableFuture.runAsync() - fix: log
ExecutionException(failed save) at WARNING level inawaitPendingSaves(); restore interrupt flag onInterruptedException - fix: remove redundant
pendingSaves.isEmpty()early return
Fix Details
ForkJoinPool / Folia compatibility
runTrackedAsync() previously submitted work to the bare JVM ForkJoinPool via CompletableFuture.runAsync(task). On shutdown, if awaitPendingSaves() timed out, those tasks could continue running after redisManager.terminate() closed the Jedis pool, causing JedisConnectionException that was silently swallowed — re-introducing the WiIIiam278#654 data-loss scenario. On Folia, the bare pool also lacked region context. The fix pre-creates a CompletableFuture and completes it inside the plugin.runAsync() lambda (MorePaperLib AsynchronousScheduler on Bukkit, ASYNC_EXEC on Fabric), ensuring tasks are bounded to the plugin lifecycle and Folia-safe.
Exception handling in awaitPendingSaves()
The previous catch (Exception ignored) silently discarded both ExecutionException (a player save failed mid-shutdown) and InterruptedException (interrupt flag lost). The fix logs a WARNING for failed saves and correctly restores Thread.currentThread().interrupt() on interruption.
Based On
Upstream HuskSync — includes all fixes through 3.8.8 with additional Paper 26.1.2 support.