Ensure NameManager caches stay consistent with multiple threads accessing them
This commit is contained in:
parent
d52c329618
commit
013a21159f
|
@ -1,20 +1,21 @@
|
||||||
package com.Acrobot.Breeze.Collection;
|
package com.Acrobot.Breeze.Collection;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
public class SimpleCache<K, V> {
|
public class SimpleCache<K, V> {
|
||||||
private final LinkedHashMap<K, V> map;
|
private final Map<K, V> map;
|
||||||
|
|
||||||
public SimpleCache(int cacheSize) {
|
public SimpleCache(int cacheSize) {
|
||||||
map = new LinkedHashMap<K, V>(cacheSize * 10/9, 0.7f, true) {
|
map = Collections.synchronizedMap(new LinkedHashMap<K, V>(cacheSize * 10/9, 0.7f, true) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||||
return size() > cacheSize;
|
return size() > cacheSize;
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public V put(K key, V value) {
|
public V put(K key, V value) {
|
||||||
|
|
|
@ -36,6 +36,8 @@ import java.util.logging.Level;
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("UnusedAssignment") // I deliberately set the variables to null while initializing
|
@SuppressWarnings("UnusedAssignment") // I deliberately set the variables to null while initializing
|
||||||
public class NameManager implements Listener {
|
public class NameManager implements Listener {
|
||||||
|
private static final Object accountsLock = new Object();
|
||||||
|
|
||||||
private static Dao<Account, String> accounts;
|
private static Dao<Account, String> accounts;
|
||||||
|
|
||||||
private static SimpleCache<String, Account> usernameToAccount = new SimpleCache<>(Properties.CACHE_SIZE);
|
private static SimpleCache<String, Account> usernameToAccount = new SimpleCache<>(Properties.CACHE_SIZE);
|
||||||
|
@ -97,20 +99,22 @@ public class NameManager implements Listener {
|
||||||
*/
|
*/
|
||||||
public static Account getAccount(UUID uuid) {
|
public static Account getAccount(UUID uuid) {
|
||||||
try {
|
try {
|
||||||
return uuidToAccount.get(uuid, () -> {
|
synchronized (accountsLock) {
|
||||||
try {
|
return uuidToAccount.get(uuid, () -> {
|
||||||
Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("uuid", new SelectArg(uuid)).queryForFirst();
|
try {
|
||||||
if (account != null) {
|
Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("uuid", new SelectArg(uuid)).queryForFirst();
|
||||||
account.setUuid(uuid); // HOW IS IT EVEN POSSIBLE THAT UUID IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
|
if (account != null) {
|
||||||
shortToAccount.put(account.getShortName(), account);
|
account.setUuid(uuid); // HOW IS IT EVEN POSSIBLE THAT UUID IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
|
||||||
usernameToAccount.put(account.getName(), account);
|
shortToAccount.put(account.getShortName(), account);
|
||||||
return account;
|
usernameToAccount.put(account.getName(), account);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + uuid + ":", e);
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
throw new Exception("Could not find account for " + uuid);
|
||||||
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + uuid + ":", e);
|
});
|
||||||
}
|
}
|
||||||
throw new Exception("Could not find account for " + uuid);
|
|
||||||
});
|
|
||||||
} catch (ExecutionException ignored) {
|
} catch (ExecutionException ignored) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -127,19 +131,21 @@ public class NameManager implements Listener {
|
||||||
Preconditions.checkNotNull(fullName, "fullName cannot be null!");
|
Preconditions.checkNotNull(fullName, "fullName cannot be null!");
|
||||||
Preconditions.checkArgument(!fullName.isEmpty(), "fullName cannot be empty!");
|
Preconditions.checkArgument(!fullName.isEmpty(), "fullName cannot be empty!");
|
||||||
try {
|
try {
|
||||||
return usernameToAccount.get(fullName, () -> {
|
synchronized (accountsLock) {
|
||||||
try {
|
return usernameToAccount.get(fullName, () -> {
|
||||||
Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("name", new SelectArg(fullName)).queryForFirst();
|
try {
|
||||||
if (account != null) {
|
Account account = accounts.queryBuilder().orderBy("lastSeen", false).where().eq("name", new SelectArg(fullName)).queryForFirst();
|
||||||
account.setName(fullName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
|
if (account != null) {
|
||||||
shortToAccount.put(account.getShortName(), account);
|
account.setName(fullName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
|
||||||
return account;
|
shortToAccount.put(account.getShortName(), account);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + fullName + ":", e);
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
throw new Exception("Could not find account for " + fullName);
|
||||||
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + fullName + ":", e);
|
});
|
||||||
}
|
}
|
||||||
throw new Exception("Could not find account for " + fullName);
|
|
||||||
});
|
|
||||||
} catch (ExecutionException ignored) {
|
} catch (ExecutionException ignored) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -167,18 +173,20 @@ public class NameManager implements Listener {
|
||||||
Account account = null;
|
Account account = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
account = shortToAccount.get(shortName, () -> {
|
synchronized (accountsLock) {
|
||||||
try {
|
account = shortToAccount.get(shortName, () -> {
|
||||||
Account a = accounts.queryBuilder().where().eq("shortName", new SelectArg(shortName)).queryForFirst();
|
try {
|
||||||
if (a != null) {
|
Account a = accounts.queryBuilder().where().eq("shortName", new SelectArg(shortName)).queryForFirst();
|
||||||
a.setShortName(shortName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
|
if (a != null) {
|
||||||
return a;
|
a.setShortName(shortName); // HOW IS IT EVEN POSSIBLE THAT THE NAME IS NOT SET EVEN IF WE HAVE FOUND THE PLAYER?!
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + shortName + ":", e);
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
throw new Exception("Could not find account for " + shortName);
|
||||||
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while getting account for " + shortName + ":", e);
|
});
|
||||||
}
|
}
|
||||||
throw new Exception("Could not find account for " + shortName);
|
|
||||||
});
|
|
||||||
} catch (ExecutionException ignored) {}
|
} catch (ExecutionException ignored) {}
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
@ -222,27 +230,29 @@ public class NameManager implements Listener {
|
||||||
final UUID uuid = player.getUniqueId();
|
final UUID uuid = player.getUniqueId();
|
||||||
|
|
||||||
Account latestAccount = null;
|
Account latestAccount = null;
|
||||||
try {
|
synchronized (accountsLock) {
|
||||||
latestAccount = accounts.queryBuilder().where().eq("uuid", new SelectArg(uuid)).and().eq("name", new SelectArg(player.getName())).queryForFirst();
|
try {
|
||||||
} catch (SQLException e) {
|
latestAccount = accounts.queryBuilder().where().eq("uuid", new SelectArg(uuid)).and().eq("name", new SelectArg(player.getName())).queryForFirst();
|
||||||
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while searching for latest account of " + player.getName() + "/" + uuid + ":", e);
|
} catch (SQLException e) {
|
||||||
}
|
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while searching for latest account of " + player.getName() + "/" + uuid + ":", e);
|
||||||
|
}
|
||||||
|
|
||||||
if (latestAccount == null) {
|
if (latestAccount == null) {
|
||||||
latestAccount = new Account(player.getName(), getNewShortenedName(player), player.getUniqueId());
|
latestAccount = new Account(player.getName(), getNewShortenedName(player), player.getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
latestAccount.setLastSeen(new Date());
|
latestAccount.setLastSeen(new Date());
|
||||||
try {
|
try {
|
||||||
accounts.createOrUpdate(latestAccount);
|
accounts.createOrUpdate(latestAccount);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while updating account " + latestAccount + ":", e);
|
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while updating account " + latestAccount + ":", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
usernameToAccount.put(latestAccount.getName(), latestAccount);
|
usernameToAccount.put(latestAccount.getName(), latestAccount);
|
||||||
uuidToAccount.put(uuid, latestAccount);
|
uuidToAccount.put(uuid, latestAccount);
|
||||||
shortToAccount.put(latestAccount.getShortName(), latestAccount);
|
shortToAccount.put(latestAccount.getShortName(), latestAccount);
|
||||||
|
}
|
||||||
|
|
||||||
return latestAccount;
|
return latestAccount;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue