Better flatfile purge performance; sync flatdb access ...

... to avoid data loss
This commit is contained in:
riking 2013-06-23 11:45:30 -07:00
parent c967a327c6
commit c588fe7420

View File

@ -13,6 +13,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
@ -32,6 +33,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
private final File usersFile; private final File usersFile;
private static final Object fileWritingLock = new Object();
protected FlatfileDatabaseManager() { protected FlatfileDatabaseManager() {
usersFile = new File(mcMMO.getUsersFilePath()); usersFile = new File(mcMMO.getUsersFilePath());
@ -48,65 +50,65 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null; FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath(); String usersFilePath = mcMMO.getUsersFilePath();
// Rationale for doing a file read instead of using the cached values: // This code is O(n) instead of O(n²)
// If mulitple users need be removed, this only opens the file once as synchronized (fileWritingLock) {
// opposed to calling removeUser which opens the file once each time. try {
try { in = new BufferedReader(new FileReader(usersFilePath));
in = new BufferedReader(new FileReader(usersFilePath)); StringBuilder writer = new StringBuilder();
StringBuilder writer = new StringBuilder(); String line = "";
String line = "";
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
String[] character = line.split(":"); String[] character = line.split(":");
PlayerProfile profile = null; PlayerProfile profile = null;
try { try {
profile = loadFromLine(character); profile = loadFromLine(character);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
if (profile == null) continue; // skip malformed lines if (profile == null) continue; // skip malformed lines
boolean powerless = true; boolean powerless = true;
for (SkillType skill : SkillType.nonChildSkills()) { for (SkillType skill : SkillType.nonChildSkills()) {
if (profile.getSkillLevel(skill) != 0) { if (profile.getSkillLevel(skill) != 0) {
powerless = false; powerless = false;
break; break;
}
}
// If they're still around, rewrite them to the file.
if (!powerless) {
writer.append(line).append("\r\n");
}
else {
purgedUsers++;
Misc.profileCleanup(character[0]);
} }
} }
// If they're still around, rewrite them to the file. // Write the new file
if (!powerless) { out = new FileWriter(usersFilePath);
writer.append(line).append("\r\n"); out.write(writer.toString());
}
else {
purgedUsers++;
Misc.profileCleanup(character[0]);
}
} }
catch (IOException e) {
// Write the new file mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
catch (IOException e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
finally {
if (out != null) { if (in != null) {
try { try {
out.close(); in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
catch (IOException ex) {
ex.printStackTrace(); if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
} }
} }
@ -120,9 +122,61 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
mcMMO.p.getLogger().info("Purging old users..."); mcMMO.p.getLogger().info("Purging old users...");
for (OfflinePlayer player : mcMMO.p.getServer().getOfflinePlayers()) {
if (!player.isOnline() && (currentTime - player.getLastPlayed() > PURGE_TIME && removeUser(player.getName()))) { BufferedReader in = null;
removedPlayers++; FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath();
// This code is O(n) instead of O(n²)
synchronized (fileWritingLock) {
try {
in = new BufferedReader(new FileReader(usersFilePath));
StringBuilder writer = new StringBuilder();
String line = "";
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
String name = character[0];
OfflinePlayer player = Bukkit.getOfflinePlayer(name);
boolean old = true;
if (player != null) {
old = (currentTime - player.getLastPlayed()) > PURGE_TIME;
}
if (!old) {
writer.append(line);
}
else {
removedPlayers++;
Misc.profileCleanup(name);
}
}
// Write the new file
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
catch (IOException e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
} }
} }
@ -136,44 +190,46 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null; FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath(); String usersFilePath = mcMMO.getUsersFilePath();
try { synchronized (fileWritingLock) {
in = new BufferedReader(new FileReader(usersFilePath)); try {
StringBuilder writer = new StringBuilder(); in = new BufferedReader(new FileReader(usersFilePath));
String line = ""; StringBuilder writer = new StringBuilder();
String line = "";
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
// Write out the same file but when we get to the player we want to remove, we skip his line. // Write out the same file but when we get to the player we want to remove, we skip his line.
if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) { if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) {
mcMMO.p.getLogger().info("User found, removing..."); mcMMO.p.getLogger().info("User found, removing...");
worked = true; worked = true;
continue; // Skip the player continue; // Skip the player
}
writer.append(line).append("\r\n");
} }
writer.append(line).append("\r\n"); out = new FileWriter(usersFilePath); // Write out the new file
out.write(writer.toString());
} }
catch (Exception e) {
out = new FileWriter(usersFilePath); // Write out the new file mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
out.write(writer.toString());
}
catch (Exception e) {
mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
finally {
if (out != null) { if (in != null) {
try { try {
out.close(); in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
catch (IOException ex) {
ex.printStackTrace(); if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
} }
} }
@ -190,88 +246,90 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileWriter out = null; FileWriter out = null;
String usersFilePath = mcMMO.getUsersFilePath(); String usersFilePath = mcMMO.getUsersFilePath();
try { synchronized (fileWritingLock) {
// Open the file try {
in = new BufferedReader(new FileReader(usersFilePath)); // Open the file
StringBuilder writer = new StringBuilder(); in = new BufferedReader(new FileReader(usersFilePath));
String line; StringBuilder writer = new StringBuilder();
String line;
// While not at the end of the file // While not at the end of the file
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
// Read the line in and copy it to the output it's not the player we want to edit // Read the line in and copy it to the output it's not the player we want to edit
if (!line.split(":")[0].equalsIgnoreCase(playerName)) { if (!line.split(":")[0].equalsIgnoreCase(playerName)) {
writer.append(line).append("\r\n"); writer.append(line).append("\r\n");
} }
else { else {
// Otherwise write the new player information // Otherwise write the new player information
writer.append(playerName).append(":"); writer.append(playerName).append(":");
writer.append(profile.getSkillLevel(SkillType.MINING)).append(":"); writer.append(profile.getSkillLevel(SkillType.MINING)).append(":");
writer.append(":"); writer.append(":");
writer.append(":"); writer.append(":");
writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":");
writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":"); writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":");
writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":"); writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":"); writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":"); writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":"); writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":"); writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":"); writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillLevel(SkillType.AXES)).append(":"); writer.append(profile.getSkillLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":"); writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":");
writer.append(":"); writer.append(":");
writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":"); writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.TAMING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.TAMING)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.BERSERK)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.BERSERK)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.GIGA_DRILL_BREAKER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.GIGA_DRILL_BREAKER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.TREE_FELLER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.TREE_FELLER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.GREEN_TERRA)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.GREEN_TERRA)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SERRATED_STRIKES)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.SERRATED_STRIKES)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SKULL_SPLITTER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.SKULL_SPLITTER)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":");
HudType hudType = profile.getHudType(); HudType hudType = profile.getHudType();
writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":"); writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":");
writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":"); writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":");
writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":"); writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":");
writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":"); writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":");
writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":"); writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
MobHealthbarType mobHealthbarType = profile.getMobHealthbarType(); MobHealthbarType mobHealthbarType = profile.getMobHealthbarType();
writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":"); writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":");
writer.append("\r\n"); writer.append("\r\n");
}
} }
// Write the new file
out = new FileWriter(usersFilePath);
out.write(writer.toString());
} }
catch (Exception e) {
// Write the new file e.printStackTrace();
out = new FileWriter(usersFilePath);
out.write(writer.toString());
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (in != null) {
try {
in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
finally {
if (out != null) { if (in != null) {
try { try {
out.close(); in.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
catch (IOException ex) {
ex.printStackTrace(); if (out != null) {
try {
out.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
} }
} }
} }
@ -300,95 +358,99 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
} }
public void newUser(String playerName) { public void newUser(String playerName) {
try { synchronized (fileWritingLock) {
// Open the file to write the player try {
BufferedWriter out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true)); // Open the file to write the player
BufferedWriter out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true));
// Add the player to the end // Add the player to the end
out.append(playerName).append(":"); out.append(playerName).append(":");
out.append("0:"); // Mining out.append("0:"); // Mining
out.append(":"); out.append(":");
out.append(":"); out.append(":");
out.append("0:"); // Xp out.append("0:"); // Xp
out.append("0:"); // Woodcutting out.append("0:"); // Woodcutting
out.append("0:"); // WoodCuttingXp out.append("0:"); // WoodCuttingXp
out.append("0:"); // Repair out.append("0:"); // Repair
out.append("0:"); // Unarmed out.append("0:"); // Unarmed
out.append("0:"); // Herbalism out.append("0:"); // Herbalism
out.append("0:"); // Excavation out.append("0:"); // Excavation
out.append("0:"); // Archery out.append("0:"); // Archery
out.append("0:"); // Swords out.append("0:"); // Swords
out.append("0:"); // Axes out.append("0:"); // Axes
out.append("0:"); // Acrobatics out.append("0:"); // Acrobatics
out.append("0:"); // RepairXp out.append("0:"); // RepairXp
out.append("0:"); // UnarmedXp out.append("0:"); // UnarmedXp
out.append("0:"); // HerbalismXp out.append("0:"); // HerbalismXp
out.append("0:"); // ExcavationXp out.append("0:"); // ExcavationXp
out.append("0:"); // ArcheryXp out.append("0:"); // ArcheryXp
out.append("0:"); // SwordsXp out.append("0:"); // SwordsXp
out.append("0:"); // AxesXp out.append("0:"); // AxesXp
out.append("0:"); // AcrobaticsXp out.append("0:"); // AcrobaticsXp
out.append(":"); out.append(":");
out.append("0:"); // Taming out.append("0:"); // Taming
out.append("0:"); // TamingXp out.append("0:"); // TamingXp
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("0:"); // DATS out.append("0:"); // DATS
out.append("STANDARD").append(":"); // HUD out.append("STANDARD").append(":"); // HUD
out.append("0:"); // Fishing out.append("0:"); // Fishing
out.append("0:"); // FishingXp out.append("0:"); // FishingXp
out.append("0:"); // Blast Mining out.append("0:"); // Blast Mining
out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
out.append(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD out.append(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD
// Add more in the same format as the line above // Add more in the same format as the line above
out.newLine(); out.newLine();
out.close(); out.close();
} }
catch (Exception e) { catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
}
} }
} }
public PlayerProfile loadPlayerProfile(String playerName, boolean create) { public PlayerProfile loadPlayerProfile(String playerName, boolean create) {
FileReader file = null; FileReader file = null;
BufferedReader in = null; BufferedReader in = null;
try { synchronized (fileWritingLock) {
// Open the user file
file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) {
// Find if the line contains the player we want.
String[] character = line.split(":");
if (!character[0].equalsIgnoreCase(playerName)) {
continue;
}
return loadFromLine(character);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try { try {
if (in != null) { // Open the user file
in.close(); file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) {
// Find if the line contains the player we want.
String[] character = line.split(":");
if (!character[0].equalsIgnoreCase(playerName)) {
continue;
}
return loadFromLine(character);
} }
if (file != null) { }
file.close(); catch (Exception e) {
}
} catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
finally {
try {
if (in != null) {
in.close();
}
if (file != null) {
file.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} }
if (create) { if (create) {
@ -402,37 +464,39 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
FileReader file = null; FileReader file = null;
BufferedReader in = null; BufferedReader in = null;
try { synchronized (fileWritingLock) {
// Open the user file try {
file = new FileReader(mcMMO.getUsersFilePath()); // Open the user file
in = new BufferedReader(file); file = new FileReader(mcMMO.getUsersFilePath());
String line; in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
String[] character = line.split(":"); String[] character = line.split(":");
try { try {
destination.saveUser(loadFromLine(character)); destination.saveUser(loadFromLine(character));
} }
catch (Exception e) { catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
}
} }
} }
} catch (Exception e) {
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
if (in != null) {
in.close();
}
if (file != null) {
file.close();
}
} catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
finally {
try {
if (in != null) {
in.close();
}
if (file != null) {
file.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} }
} }
@ -444,26 +508,29 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
public List<String> getStoredUsers() { public List<String> getStoredUsers() {
ArrayList<String> users = new ArrayList<String>(); ArrayList<String> users = new ArrayList<String>();
BufferedReader in = null; BufferedReader in = null;
try {
// Open the user file
FileReader file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) { synchronized (fileWritingLock) {
String[] character = line.split(":");
users.add(character[0]);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try { try {
in.close(); // Open the user file
} catch (IOException e) { FileReader file = new FileReader(mcMMO.getUsersFilePath());
in = new BufferedReader(file);
String line;
while ((line = in.readLine()) != null) {
String[] character = line.split(":");
users.add(character[0]);
}
}
catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
return users; return users;
} }