aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/1036-Write-SavedData-IO-async.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/1036-Write-SavedData-IO-async.patch')
-rw-r--r--patches/server/1036-Write-SavedData-IO-async.patch193
1 files changed, 193 insertions, 0 deletions
diff --git a/patches/server/1036-Write-SavedData-IO-async.patch b/patches/server/1036-Write-SavedData-IO-async.patch
new file mode 100644
index 0000000000..932cd936fb
--- /dev/null
+++ b/patches/server/1036-Write-SavedData-IO-async.patch
@@ -0,0 +1,193 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Cryptite <[email protected]>
+Date: Tue, 27 Jun 2023 11:35:52 -0500
+Subject: [PATCH] Write SavedData IO async
+
+Co-Authored-By: Shane Freeder <[email protected]>
+
+diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
+index e049fbe4038aaea896f45b11ce9ce8f05922c898..7f6d1ccd147e5593412567bb2934ce5662da7ef0 100644
+--- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
++++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
+@@ -110,6 +110,15 @@ public class ThreadedWorldUpgrader {
+ }
+
+ this.threadPool.execute(new ConvertTask(info, regionPos.x >> 5, regionPos.z >> 5));
++ // Paper start - Write SavedData IO async
++ this.threadPool.execute(() -> {
++ try {
++ worldPersistentData.close();
++ } catch (IOException exception) {
++ LOGGER.error("Failed to close persistent world data", exception);
++ }
++ });
++ // Paper end - Write SavedData IO async
+ }
+ this.threadPool.shutdown();
+
+diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+index 36caf354634d6675a3f1ec6829f4778e1d0623bc..b99f50604bafecbc68835974c9ed0caa91911a40 100644
+--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+@@ -447,6 +447,13 @@ public class ServerChunkCache extends ChunkSource {
+
+ public void close(boolean save) { // Paper - rewrite chunk system
+ this.level.chunkTaskScheduler.chunkHolderManager.close(save, true); // Paper - rewrite chunk system
++ // Paper start - Write SavedData IO async
++ try {
++ this.dataStorage.close();
++ } catch (IOException exception) {
++ LOGGER.error("Failed to close persistent world data", exception);
++ }
++ // Paper end - Write SavedData IO async
+ }
+
+ // CraftBukkit start - modelled on below
+diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
+index 0b0fd9e32d2772098d1aade83083b38cac01b76e..0981d440d0dbfe4df668d1f3f1b5706a93bc4434 100644
+--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
+@@ -1492,7 +1492,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+
+ try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) {
+ if (doFull) {
+- this.saveLevelData();
++ this.saveLevelData(true); // Paper - Write SavedData IO async
+ }
+
+ this.timings.worldSaveChunks.startTiming(); // Paper
+@@ -1528,7 +1528,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel"));
+ }
+
+- this.saveLevelData();
++ this.saveLevelData(!close); // Paper - Write SavedData IO async
+ if (progressListener != null) {
+ progressListener.progressStage(Component.translatable("menu.savingChunks"));
+ }
+@@ -1551,12 +1551,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ // CraftBukkit end
+ }
+
+- private void saveLevelData() {
++ private void saveLevelData(boolean async) { // Paper - Write SavedData IO async
+ if (this.dragonFight != null) {
+ this.serverLevelData.setEndDragonFightData(this.dragonFight.saveData()); // CraftBukkit
+ }
+
+- this.getChunkSource().getDataStorage().save();
++ this.getChunkSource().getDataStorage().save(async); // Paper - Write SavedData IO async
+ }
+
+ public <T extends Entity> List<? extends T> getEntities(EntityTypeTest<Entity, T> filter, Predicate<? super T> predicate) {
+diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+index 639f72618a7c22fa94effa9d0406b97fffc64cb5..3e582c49069f2a820ba3baac03917493877d9875 100644
+--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
++++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+@@ -116,7 +116,13 @@ public class WorldUpgrader {
+ (new WorldUpgrader.PoiUpgrader(this)).upgrade();
+ WorldUpgrader.LOGGER.info("Upgrading blocks");
+ (new WorldUpgrader.ChunkUpgrader()).upgrade();
+- this.overworldDataStorage.save();
++ // Paper start - Write SavedData IO async
++ try {
++ this.overworldDataStorage.close();
++ } catch (final IOException e) {
++ LOGGER.error("Failed to close persistent world data", e);
++ }
++ // Paper end - Write SavedData IO async
+ i = Util.getMillis() - i;
+ WorldUpgrader.LOGGER.info("World optimizaton finished after {} seconds", i / 1000L);
+ this.finished = true;
+diff --git a/src/main/java/net/minecraft/world/level/saveddata/SavedData.java b/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
+index 9cc3850bb70dfbcf342651360314a19fd9ea3ecc..4cbb943b702baaeb8bfd2b558cc848e719cf095d 100644
+--- a/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
++++ b/src/main/java/net/minecraft/world/level/saveddata/SavedData.java
+@@ -30,20 +30,36 @@ public abstract class SavedData {
+ return this.dirty;
+ }
+
++ // Paper start - Write SavedData IO async - joining is evil, but we assume the old blocking behavior here just for safety
++ @io.papermc.paper.annotation.DoNotUse
+ public void save(File file, HolderLookup.Provider registryLookup) {
++ save(file, registryLookup, null).join();
++ }
++
++ public java.util.concurrent.CompletableFuture<Void> save(File file, HolderLookup.Provider registryLookup, @org.jetbrains.annotations.Nullable java.util.concurrent.ExecutorService ioExecutor) {
++ // Paper end - Write SavedData IO async
+ if (this.isDirty()) {
+ CompoundTag compoundTag = new CompoundTag();
+ compoundTag.put("data", this.save(new CompoundTag(), registryLookup));
+ NbtUtils.addCurrentDataVersion(compoundTag);
+
++ Runnable writeRunnable = () -> { // Paper - Write SavedData IO async
+ try {
+ NbtIo.writeCompressed(compoundTag, file.toPath());
+ } catch (IOException var5) {
+ LOGGER.error("Could not save data {}", this, var5);
+ }
++ }; // Paper - Write SavedData IO async
+
+ this.setDirty(false);
++ // Paper start - Write SavedData IO async
++ if (ioExecutor == null) {
++ return java.util.concurrent.CompletableFuture.runAsync(writeRunnable); // No executor, just use common pool
++ }
++ return java.util.concurrent.CompletableFuture.runAsync(writeRunnable, ioExecutor);
+ }
++ return java.util.concurrent.CompletableFuture.completedFuture(null);
++ // Paper end - Write SavedData IO async
+ }
+
+ public static record Factory<T extends SavedData>(
+diff --git a/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java b/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
+index 7d98d2911e9d6ec429ce7df1f1f2650c7ea32325..6e23e69abd56eeda3b52a22019e1b74ae10682e7 100644
+--- a/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
++++ b/src/main/java/net/minecraft/world/level/storage/DimensionDataStorage.java
+@@ -23,17 +23,19 @@ import net.minecraft.util.datafix.DataFixTypes;
+ import net.minecraft.world.level.saveddata.SavedData;
+ import org.slf4j.Logger;
+
+-public class DimensionDataStorage {
++public class DimensionDataStorage implements java.io.Closeable { // Paper - Write SavedData IO async
+ private static final Logger LOGGER = LogUtils.getLogger();
+ public final Map<String, SavedData> cache = Maps.newHashMap();
+ private final DataFixer fixerUpper;
+ private final HolderLookup.Provider registries;
+ private final File dataFolder;
++ protected final java.util.concurrent.ExecutorService ioExecutor; // Paper - Write SavedData IO async
+
+ public DimensionDataStorage(File directory, DataFixer dataFixer, HolderLookup.Provider registryLookup) {
+ this.fixerUpper = dataFixer;
+ this.dataFolder = directory;
+ this.registries = registryLookup;
++ this.ioExecutor = java.util.concurrent.Executors.newSingleThreadExecutor(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("DimensionDataIO - " + dataFolder.getParent() + " - %d").setDaemon(true).build()); // Paper - Write SavedData IO async
+ }
+
+ private File getDataFile(String id) {
+@@ -123,10 +125,23 @@ public class DimensionDataStorage {
+ return bl;
+ }
+
+- public void save() {
++ // Paper start - Write SavedData IO async
++ @Override
++ public void close() throws IOException {
++ save(false);
++ this.ioExecutor.shutdown();
++ }
++ // Paper end - Write SavedData IO async
++
++ public void save(boolean async) { // Paper - Write SavedData IO async
+ this.cache.forEach((id, state) -> {
+ if (state != null) {
+- state.save(this.getDataFile(id), this.registries);
++ // Paper start - Write SavedData IO async
++ final java.util.concurrent.CompletableFuture<Void> save = state.save(this.getDataFile(id), this.registries, this.ioExecutor);
++ if (!async) {
++ save.join();
++ }
++ // Paper end - Write SavedData IO async
+ }
+ });
+ }