mirror of
https://github.com/TotalFreedomMC/TF-EssentialsX.git
synced 2025-02-11 11:49:12 +00:00
[Experimental] Rewrite UUIDMap to use one single ScheduledExecutorService
This commit is contained in:
parent
526118e7d3
commit
b8f7918a4a
2 changed files with 68 additions and 88 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<String, UUID> names, final ConcurrentSkipListMap<UUID, ArrayList<String>> 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<String> list = new ArrayList<String>();
|
||||
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<String> list = new ArrayList<>();
|
||||
list.add(name);
|
||||
history.put(uuid, list);
|
||||
} else {
|
||||
final ArrayList<String> list = history.get(uuid);
|
||||
if (!list.contains(name)) {
|
||||
list.add(name);
|
||||
history.put(uuid, list);
|
||||
} else {
|
||||
final ArrayList<String> 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<String, UUID> 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<String, UUID> names;
|
||||
private final AtomicInteger pendingDiskWrites;
|
||||
|
||||
private WriteRunner(final File location, final File endFile, final Map<String, UUID> names, final AtomicInteger pendingDiskWrites) {
|
||||
private WriteRunner(final File location, final File endFile, final Map<String, UUID> 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<String, UUID> 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<String, UUID> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue