From b8f7918a4a608455e79b851bdf1ed0b52c253e30 Mon Sep 17 00:00:00 2001 From: vemacs Date: Tue, 1 Mar 2016 17:45:48 -0700 Subject: [PATCH] [Experimental] Rewrite UUIDMap to use one single ScheduledExecutorService --- .../com/earth2me/essentials/Essentials.java | 2 +- .../src/com/earth2me/essentials/UUIDMap.java | 154 ++++++++---------- 2 files changed, 68 insertions(+), 88 deletions(-) diff --git a/Essentials/src/com/earth2me/essentials/Essentials.java b/Essentials/src/com/earth2me/essentials/Essentials.java index 54eb48981..d9e8f0717 100644 --- a/Essentials/src/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/com/earth2me/essentials/Essentials.java @@ -304,7 +304,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials { } Economy.setEss(null); Trade.closeLog(); - getUserMap().getUUIDMap().forceWriteUUIDMap(); + getUserMap().getUUIDMap().shutdown(); HandlerList.unregisterAll(this); } diff --git a/Essentials/src/com/earth2me/essentials/UUIDMap.java b/Essentials/src/com/earth2me/essentials/UUIDMap.java index 6cb735d25..dc2a468d3 100644 --- a/Essentials/src/com/earth2me/essentials/UUIDMap.java +++ b/Essentials/src/com/earth2me/essentials/UUIDMap.java @@ -9,7 +9,6 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.regex.Pattern; @@ -18,13 +17,28 @@ public class UUIDMap { private final transient net.ess3.api.IEssentials ess; private File userList; private final transient Pattern splitPattern = Pattern.compile(","); - private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor(); - private final AtomicInteger pendingDiskWrites = new AtomicInteger(0); + + private static boolean pendingWrite; + private static final ScheduledExecutorService writeScheduler = Executors.newScheduledThreadPool(1); + private final Runnable writeTaskRunnable; public UUIDMap(final net.ess3.api.IEssentials ess) { this.ess = ess; userList = new File(ess.getDataFolder(), "usermap.csv"); - + pendingWrite = false; + writeTaskRunnable = new Runnable() { + @Override + public void run() { + if (pendingWrite) { + try { + new WriteRunner(ess.getDataFolder(), userList, ess.getUserMap().getNames()).run(); + } catch (Throwable t) { // bad code to prevent task from being suppressed + t.printStackTrace(); + } + } + } + }; + writeScheduler.scheduleWithFixedDelay(writeTaskRunnable, 5, 5, TimeUnit.SECONDS); } public void loadAllUsers(final ConcurrentSkipListMap names, final ConcurrentSkipListMap> history) { @@ -33,41 +47,36 @@ public class UUIDMap { userList.createNewFile(); } - synchronized (pendingDiskWrites) { - if (ess.getSettings().isDebug()) { - ess.getLogger().log(Level.INFO, "Reading usermap from disk"); - } + if (ess.getSettings().isDebug()) { + ess.getLogger().log(Level.INFO, "Reading usermap from disk"); + } - names.clear(); - history.clear(); + names.clear(); + history.clear(); - final BufferedReader reader = new BufferedReader(new FileReader(userList)); - try { - while (true) { - final String line = reader.readLine(); - if (line == null) { - break; - } else { - final String[] values = splitPattern.split(line); - if (values.length == 2) { - final String name = values[0]; - final UUID uuid = UUID.fromString(values[1]); - names.put(name, uuid); - if (!history.containsKey(uuid)) { - final ArrayList list = new ArrayList(); + try (BufferedReader reader = new BufferedReader(new FileReader(userList))) { + while (true) { + final String line = reader.readLine(); + if (line == null) { + break; + } else { + final String[] values = splitPattern.split(line); + if (values.length == 2) { + final String name = values[0]; + final UUID uuid = UUID.fromString(values[1]); + names.put(name, uuid); + if (!history.containsKey(uuid)) { + final ArrayList list = new ArrayList<>(); + list.add(name); + history.put(uuid, list); + } else { + final ArrayList list = history.get(uuid); + if (!list.contains(name)) { list.add(name); - history.put(uuid, list); - } else { - final ArrayList list = history.get(uuid); - if (!list.contains(name)) { - list.add(name); - } } } } } - } finally { - reader.close(); } } } catch (IOException ex) { @@ -76,87 +85,58 @@ public class UUIDMap { } public void writeUUIDMap() { - _writeUUIDMap(); + pendingWrite = true; } public void forceWriteUUIDMap() { if (ess.getSettings().isDebug()) { ess.getLogger().log(Level.INFO, "Forcing usermap write to disk"); } - try { - Future future = _writeUUIDMap(); - if (future != null) { - future.get(); - } - } catch (InterruptedException ex) { - ess.getLogger().log(Level.SEVERE, ex.getMessage(), ex); - } catch (ExecutionException ex) { - ess.getLogger().log(Level.SEVERE, ex.getMessage(), ex); - } + pendingWrite = true; + writeTaskRunnable.run(); } - public Future _writeUUIDMap() { - Map names = ess.getUserMap().getNames(); - if (names.size() < 1) { - return null; - } - // The _names_ Map is being shallowly cloned because of an issue that occurs during restarts/reloads that - // causes UserMap database to be cleared. The assumed culprit is names.clear() when loading in UserMap. - // - // For more information, please refer to #213. - names = new HashMap<>(names); - pendingDiskWrites.incrementAndGet(); - Future future = EXECUTOR_SERVICE.submit(new WriteRunner(ess.getDataFolder(), userList, names, pendingDiskWrites)); - return future; + public void shutdown() { + writeScheduler.submit(writeTaskRunnable); + writeScheduler.shutdown(); } - private static class WriteRunner implements Runnable { private final File location; private final File endFile; private final Map names; - private final AtomicInteger pendingDiskWrites; - private WriteRunner(final File location, final File endFile, final Map names, final AtomicInteger pendingDiskWrites) { + private WriteRunner(final File location, final File endFile, final Map names) { this.location = location; this.endFile = endFile; - this.names = names; - this.pendingDiskWrites = pendingDiskWrites; + this.names = new HashMap<>(names); } @Override public void run() { - synchronized (pendingDiskWrites) { - if (pendingDiskWrites.get() > 1) { - pendingDiskWrites.decrementAndGet(); - return; + pendingWrite = false; + File configFile = null; + + try { + configFile = File.createTempFile("usermap", ".tmp.csv", location); + + final BufferedWriter bWriter = new BufferedWriter(new FileWriter(configFile)); + for (Map.Entry entry : names.entrySet()) { + bWriter.write(entry.getKey() + "," + entry.getValue().toString()); + bWriter.newLine(); } - File configFile = null; - + bWriter.close(); + Files.move(configFile, endFile); + } catch (IOException ex) { try { - configFile = File.createTempFile("usermap", ".tmp.csv", location); - - final BufferedWriter bWriter = new BufferedWriter(new FileWriter(configFile)); - for (Map.Entry entry : names.entrySet()) { - bWriter.write(entry.getKey() + "," + entry.getValue().toString()); - bWriter.newLine(); + if (configFile != null && configFile.exists()) { + Files.move(configFile, new File(endFile.getParentFile(), "usermap.bak.csv")); } - - bWriter.close(); - Files.move(configFile, endFile); - } catch (IOException ex) { - try { - if (configFile != null && configFile.exists()) { - Files.move(configFile, new File(endFile.getParentFile(), "usermap.bak.csv")); - } - } catch (Exception ex2) { - Bukkit.getLogger().log(Level.SEVERE, ex2.getMessage(), ex2); - } - Bukkit.getLogger().log(Level.WARNING, ex.getMessage(), ex); - } finally { - pendingDiskWrites.decrementAndGet(); + } catch (Exception ex2) { + Bukkit.getLogger().log(Level.SEVERE, ex2.getMessage(), ex2); } + Bukkit.getLogger().log(Level.WARNING, ex.getMessage(), ex); } } }