Skip to content

Commit 264b0ed

Browse files
authored
Improve memory resilience with Configurate (#6266)
Hopefully should cut down on memory usage with configurate. Closes #6118
1 parent 4dabeb5 commit 264b0ed

File tree

3 files changed

+48
-18
lines changed

3 files changed

+48
-18
lines changed

Essentials/src/main/java/com/earth2me/essentials/Essentials.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.earth2me.essentials.utils.VersionUtil;
4646
import io.papermc.lib.PaperLib;
4747
import net.ess3.api.Economy;
48+
import com.earth2me.essentials.config.EssentialsConfiguration;
4849
import net.ess3.api.IEssentials;
4950
import net.ess3.api.IItemDb;
5051
import net.ess3.api.IJails;
@@ -570,6 +571,8 @@ public void onDisable() {
570571
Trade.closeLog();
571572
getUsers().shutdown();
572573

574+
EssentialsConfiguration.shutdownExecutor();
575+
573576
HandlerList.unregisterAll(this);
574577
}
575578

Essentials/src/main/java/com/earth2me/essentials/config/ConfigurationSaveTask.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,32 @@
66
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
77

88
import java.util.concurrent.atomic.AtomicInteger;
9+
import java.util.function.Supplier;
910
import java.util.logging.Level;
1011

1112
public class ConfigurationSaveTask implements Runnable {
1213
private final YamlConfigurationLoader loader;
13-
private final CommentedConfigurationNode node;
14+
private final Supplier<CommentedConfigurationNode> nodeSupplier;
1415
private final AtomicInteger pendingWrites;
1516

16-
public ConfigurationSaveTask(final YamlConfigurationLoader loader, final CommentedConfigurationNode node, final AtomicInteger pendingWrites) {
17+
public ConfigurationSaveTask(final YamlConfigurationLoader loader, final Supplier<CommentedConfigurationNode> nodeSupplier, final AtomicInteger pendingWrites) {
1718
this.loader = loader;
18-
this.node = node;
19+
this.nodeSupplier = nodeSupplier;
1920
this.pendingWrites = pendingWrites;
2021
}
2122

2223
@Override
2324
public void run() {
24-
synchronized (loader) {
25-
// Check if there are more writes in queue.
26-
// If that's the case, we shouldn't bother writing data which is already out-of-date.
27-
if (pendingWrites.get() > 1) {
28-
pendingWrites.decrementAndGet();
29-
}
25+
if (pendingWrites.decrementAndGet() > 0) {
26+
return;
27+
}
3028

29+
synchronized (loader) {
3130
try {
31+
final CommentedConfigurationNode node = nodeSupplier.get();
3232
loader.save(node);
3333
} catch (ConfigurateException e) {
3434
Essentials.getWrappedLogger().log(Level.SEVERE, e.getMessage(), e);
35-
} finally {
36-
pendingWrites.decrementAndGet();
3735
}
3836
}
3937
}

Essentials/src/main/java/com/earth2me/essentials/config/EssentialsConfiguration.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@
4343
import java.util.concurrent.ExecutionException;
4444
import java.util.concurrent.ExecutorService;
4545
import java.util.concurrent.Executors;
46-
import java.util.concurrent.Future;
46+
import java.util.concurrent.RejectedExecutionException;
4747
import java.util.concurrent.atomic.AtomicBoolean;
4848
import java.util.concurrent.atomic.AtomicInteger;
4949
import java.util.logging.Level;
5050

5151
import static com.earth2me.essentials.I18n.tlLiteral;
5252

5353
public class EssentialsConfiguration {
54-
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
54+
private static volatile ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
5555
private static final ObjectMapper.Factory MAPPER_FACTORY = ObjectMapper.factoryBuilder()
5656
.addProcessor(DeleteOnEmpty.class, (data, value) -> new DeleteOnEmptyProcessor())
5757
.addProcessor(DeleteIfIncomplete.class, (data, value) -> new DeleteIfIncompleteProcessor())
@@ -462,21 +462,50 @@ public synchronized void save() {
462462

463463
public synchronized void blockingSave() {
464464
try {
465-
delaySave().get();
465+
delaySave();
466+
getExecutor().submit(() -> {}).get();
466467
} catch (final InterruptedException | ExecutionException e) {
467468
Essentials.getWrappedLogger().log(Level.SEVERE, e.getMessage(), e);
468469
}
469470
}
470471

471-
private Future<?> delaySave() {
472+
private void delaySave() {
472473
if (saveHook != null) {
473474
saveHook.run();
474475
}
475476

476-
final CommentedConfigurationNode node = configurationNode.copy();
477-
478477
pendingWrites.incrementAndGet();
478+
try {
479+
getExecutor().submit(new ConfigurationSaveTask(loader, () -> configurationNode.copy(), pendingWrites));
480+
} catch (RejectedExecutionException rex) {
481+
try {
482+
synchronized (loader) {
483+
loader.save(configurationNode.copy());
484+
}
485+
} catch (ConfigurateException e) {
486+
Essentials.getWrappedLogger().log(Level.SEVERE, e.getMessage(), e);
487+
} finally {
488+
pendingWrites.decrementAndGet();
489+
}
490+
}
491+
}
479492

480-
return EXECUTOR_SERVICE.submit(new ConfigurationSaveTask(loader, node, pendingWrites));
493+
public static void shutdownExecutor() {
494+
final ExecutorService exec = EXECUTOR_SERVICE;
495+
exec.shutdown();
496+
try {
497+
if (!exec.awaitTermination(5, java.util.concurrent.TimeUnit.SECONDS)) {
498+
exec.shutdownNow();
499+
}
500+
} catch (InterruptedException ignored) {
501+
exec.shutdownNow();
502+
}
503+
}
504+
505+
private static synchronized ExecutorService getExecutor() {
506+
if (EXECUTOR_SERVICE == null || EXECUTOR_SERVICE.isShutdown() || EXECUTOR_SERVICE.isTerminated()) {
507+
EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
508+
}
509+
return EXECUTOR_SERVICE;
481510
}
482511
}

0 commit comments

Comments
 (0)