aboutsummaryrefslogtreecommitdiffhomepage
path: root/Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch
diff options
context:
space:
mode:
Diffstat (limited to 'Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch')
-rw-r--r--Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch181
1 files changed, 181 insertions, 0 deletions
diff --git a/Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch b/Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch
new file mode 100644
index 0000000000..eb4d40fdd7
--- /dev/null
+++ b/Spigot-Server-Patches/0067-Chunk-save-queue-improvements.patch
@@ -0,0 +1,181 @@
+From 30f567605c740413aa96168ce4eb60ab22ee1180 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Fri, 4 Mar 2016 18:18:37 -0600
+Subject: [PATCH] Chunk save queue improvements
+
+For some unknown reason, Minecraft is sleeping 10ms between every single chunk being saved to disk.
+Under high chunk load/unload activity (lots of movement / teleporting), this causes the chunk unload queue
+to build up in size.
+
+This has multiple impacts:
+1) Performance of the unload queue itself - The save thread is pretty ineffecient for how it accesses it
+ By letting the queue get larger, checking and popping work off the queue can get less performant.
+2) Performance of chunk loading - As with #1, chunk loads also have to check this queue when loading
+ chunk data so that it doesn't load stale data if new data is pending write to disk.
+3) Memory Usage - The entire chunk has been serialized to NBT, and now sits in this queue. This leads to
+ elevated memory usage, and then the objects used in the serialization sit around longer than needed,
+ resulting in promotion to Old Generation instead of dying young.
+
+To optimize this, we change the entire unload queue to be a proper queue. This improves the behavior of popping
+the first queued chunk off, instead of abusing iterators like Mojang was doing.
+
+This also improves reliability of chunk saving, as the previous hack job had a race condition that could
+fail to save some chunks.
+
+Then finally, Sleeping will by default be removed, but due to known issues with 1.9, a config option was added.
+But if sleeps are to remain enabled, we at least lower the sleep interval so it doesn't have as much negative impact.
+
+diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
+index 9a6f87e59..873ffa77d 100644
+--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
+@@ -204,4 +204,10 @@ public class PaperConfig {
+ private static void chunkLoadThreads() {
+ minChunkLoadThreads = Math.min(6, getInt("settings.min-chunk-load-threads", 2)); // Keep people from doing stupid things with max of 6
+ }
++
++ public static boolean enableFileIOThreadSleep;
++ private static void enableFileIOThreadSleep() {
++ enableFileIOThreadSleep = getBoolean("settings.sleep-between-chunk-saves", false);
++ if (enableFileIOThreadSleep) Bukkit.getLogger().info("Enabled sleeping between chunk saves, beware of memory issues");
++ }
+ }
+diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
+index 79cb3953b..7f3e874ba 100644
+--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
+@@ -12,14 +12,17 @@ import java.util.Map;
+ import java.util.Set;
+ import java.util.concurrent.ConcurrentHashMap;
+ import javax.annotation.Nullable;
++import java.util.concurrent.ConcurrentLinkedQueue; // Paper
+ import org.apache.logging.log4j.LogManager;
+ import org.apache.logging.log4j.Logger;
+
+ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
+
++ private ConcurrentLinkedQueue<QueuedChunk> queue = new ConcurrentLinkedQueue<>(); // Paper - Chunk queue improvements
++ private final Object lock = new Object(); // Paper - Chunk queue improvements
+ private static final Logger a = LogManager.getLogger();
+ private final Map<ChunkCoordIntPair, NBTTagCompound> b = new ConcurrentHashMap();
+- private final Set<ChunkCoordIntPair> c = Collections.newSetFromMap(new ConcurrentHashMap());
++ //private final Set<ChunkCoordIntPair> c = Collections.newSetFromMap(new ConcurrentHashMap()); // Paper - Chunk queue improvements
+ private final File d;
+ private final DataConverterManager e;
+ private boolean f;
+@@ -33,11 +36,11 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
+ public boolean chunkExists(World world, int i, int j) {
+ ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
+
+- if (this.c.contains(chunkcoordintpair)) {
++ //if (this.c.contains(chunkcoordintpair)) { // Paper - Chunk queue improvements
+ if (this.b.containsKey(chunkcoordintpair)) {
+ return true;
+ }
+- }
++ //} // Paper - Chunk queue improvements
+
+ // Paper start - Don't create region files when checking that they exist
+ final RegionFile region = RegionFileCache.a(this.d, i, j, false);
+@@ -151,35 +154,32 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
+ }
+
+ protected void a(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) {
+- if (!this.c.contains(chunkcoordintpair)) {
++ synchronized (lock) { // Paper - Chunk queue improvements
+ this.b.put(chunkcoordintpair, nbttagcompound);
+ }
++ queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements
+
+ FileIOThread.a().a(this);
+ }
+
+ public boolean c() {
+- // CraftBukkit start
+- Iterator<Map.Entry<ChunkCoordIntPair, NBTTagCompound>> iter = this.b.entrySet().iterator();
+- if (!iter.hasNext()) {
+- // CraftBukkit end
++ // Paper start - Chunk queue improvements
++ QueuedChunk chunk = queue.poll();
++ if (chunk == null) {
++ // Paper - end
+ if (this.f) {
+ ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", new Object[] { this.d.getName()});
+ }
+
+ return false;
+ } else {
+- // CraftBukkit start
+- Map.Entry<ChunkCoordIntPair, NBTTagCompound> entry = iter.next();
+- iter.remove(); // Pop single entry
+- ChunkCoordIntPair chunkcoordintpair = entry.getKey();
+- // CraftBukkit end
++ ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements
+
+ boolean flag;
+
+ try {
+- this.c.add(chunkcoordintpair);
+- NBTTagCompound nbttagcompound = (NBTTagCompound) entry.getValue(); // CraftBukkit
++ //this.c.add(chunkcoordintpair);
++ NBTTagCompound nbttagcompound = chunk.compound; // Paper - Chunk queue improvements
+
+ if (nbttagcompound != null) {
+ try {
+@@ -188,10 +188,11 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
+ ChunkRegionLoader.a.error("Failed to save chunk", exception);
+ }
+ }
++ synchronized (lock) { if (this.b.get(chunkcoordintpair) == nbttagcompound) { this.b.remove(chunkcoordintpair); } }// Paper - This will not equal if a newer version is still pending
+
+ flag = true;
+ } finally {
+- this.c.remove(chunkcoordintpair);
++ //this.c.remove(chunkcoordintpair); // Paper
+ }
+
+ return flag;
+@@ -556,4 +557,16 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
+ return entity;
+ }
+ }
++
++ // Paper start - Chunk queue improvements
++ private static class QueuedChunk {
++ public ChunkCoordIntPair coords;
++ public NBTTagCompound compound;
++
++ public QueuedChunk(ChunkCoordIntPair coords, NBTTagCompound compound) {
++ this.coords = coords;
++ this.compound = compound;
++ }
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java
+index acfdd52dc..fdbaf5fbd 100644
+--- a/src/main/java/net/minecraft/server/FileIOThread.java
++++ b/src/main/java/net/minecraft/server/FileIOThread.java
+@@ -39,11 +39,15 @@ public class FileIOThread implements Runnable {
+ ++this.d;
+ }
+
+- try {
+- Thread.sleep(this.e ? 0L : 10L);
+- } catch (InterruptedException interruptedexception) {
+- interruptedexception.printStackTrace();
++ // Paper start - Add toggle
++ if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) {
++ try {
++ Thread.sleep(this.e ? 0L : 2L);
++ } catch (InterruptedException interruptedexception) {
++ interruptedexception.printStackTrace();
++ }
+ }
++ // Paper end
+ }
+
+ if (this.b.isEmpty()) {
+--
+2.12.2
+