Skip to content
This repository has been archived by the owner on Feb 3, 2024. It is now read-only.

Commit

Permalink
Cache user and group names from SQL database when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
zml committed Sep 14, 2014
1 parent ce5e56f commit 8ab8ccf
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 12 deletions.
90 changes: 78 additions & 12 deletions src/main/java/ru/tehkode/permissions/backends/sql/SQLBackend.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
*/
package ru.tehkode.permissions.backends.sql;

import com.google.common.collect.ImmutableSet;
import org.apache.commons.dbcp.BasicDataSource;
import org.bukkit.configuration.ConfigurationSection;
import ru.tehkode.permissions.PermissionManager;
import ru.tehkode.permissions.PermissionsData;
import ru.tehkode.permissions.PermissionsGroupData;
import ru.tehkode.permissions.PermissionsUserData;
import ru.tehkode.permissions.backends.PermissionBackend;
Expand All @@ -45,12 +47,14 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
* @author code
*/
public class SQLBackend extends PermissionBackend {
protected Map<String, List<String>> worldInheritanceCache = new HashMap<>();
private final AtomicReference<ImmutableSet<String>> userNamesCache = new AtomicReference<>(), groupNamesCache = new AtomicReference<>();
private Map<String, Object> tableNames;
private SQLQueryCache queryCache;
private static final SQLQueryCache DEFAULT_QUERY_CACHE;
Expand Down Expand Up @@ -250,12 +254,78 @@ public String getTableName(String identifier) {

@Override
public PermissionsUserData getUserData(String name) {
return new CachingUserData(new SQLData(name, SQLData.Type.USER, this), getExecutor(), new Object());
CachingUserData data = new CachingUserData(new SQLData(name, SQLData.Type.USER, this), getExecutor(), new Object());
updateNameCache(userNamesCache, data);
return data;
}

@Override
public PermissionsGroupData getGroupData(String name) {
return new CachingGroupData(new SQLData(name, SQLData.Type.GROUP, this), getExecutor(), new Object());
CachingGroupData data = new CachingGroupData(new SQLData(name, SQLData.Type.GROUP, this), getExecutor(), new Object());
updateNameCache(groupNamesCache, data);
return data;
}

/**
* Update the cache of names for a newly created data object, if necessary.
*
* @param list The pointer to current cache state
* @param data The data to check for presence
*/
private void updateNameCache(AtomicReference<ImmutableSet<String>> list, PermissionsData data) {
ImmutableSet<String> cache, newVal;
do {
newVal = cache = list.get();
if (cache == null || (!cache.contains(data.getIdentifier()) && !data.isVirtual())) {
newVal = null;
}

} while (!list.compareAndSet(cache, newVal));
}

/**
* Clear the names cache for the type of the provided data object
*
* @param data The data object that was updated making this necessary.
*/
void updateNameCache(SQLData data) {
final AtomicReference<ImmutableSet<String>> ref;
switch (data.getType()) {
case USER:
ref = userNamesCache;
break;
case GROUP:
ref = groupNamesCache;
break;
default:
return; // No cache for you
}
updateNameCache(ref, data);
}

/**
* Gets the names of known entities of the give type, returning cached values if possible
*
* @param cacheRef The cache reference to check
* @param type The type to get
* @return A set of known entity names
*/
private ImmutableSet<String> getEntityNames(AtomicReference<ImmutableSet<String>> cacheRef, SQLData.Type type) {
while (true) {
ImmutableSet<String> cache = cacheRef.get();
if (cache != null) {
return cache;
} else {
try (SQLConnection conn = getSQL()) {
ImmutableSet<String> newCache = ImmutableSet.copyOf(SQLData.getEntitiesNames(conn, type, false));
if (cacheRef.compareAndSet(null, newCache)) {
return newCache;
}
} catch (SQLException | IOException e) {
throw new RuntimeException(e);
}
}
}
}

@Override
Expand All @@ -280,24 +350,17 @@ public boolean hasGroup(String group) {

@Override
public Collection<String> getGroupNames() {
try (SQLConnection conn = getSQL()) {
return SQLData.getEntitiesNames(conn, SQLData.Type.GROUP, false);
} catch (SQLException | IOException e) {
throw new RuntimeException(e);
}
return getEntityNames(groupNamesCache, SQLData.Type.GROUP);
}

@Override
public Collection<String> getUserIdentifiers() {
try (SQLConnection conn = getSQL()) {
return SQLData.getEntitiesNames(conn, SQLData.Type.USER, false);
} catch (SQLException | IOException e) {
throw new RuntimeException(e);
}
return getEntityNames(userNamesCache, SQLData.Type.USER);
}

@Override
public Collection<String> getUserNames() {
// TODO: Look at implementing caching
Set<String> ret = new HashSet<>();
try (SQLConnection conn = getSQL()) {
ResultSet set = conn.prepAndBind("SELECT `value` FROM `{permissions}` WHERE `type` = ? AND `permission` = 'name' AND `value` IS NOT NULL", SQLData.Type.USER.ordinal()).executeQuery();
Expand Down Expand Up @@ -432,6 +495,8 @@ public void setWorldInheritance(String worldName, List<String> parentWorlds) {

public void reload() {
worldInheritanceCache.clear();
userNamesCache.set(null);
groupNamesCache.set(null);
}

@Override
Expand Down Expand Up @@ -499,4 +564,5 @@ public void close() throws PermissionBackendException {
}
super.close();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ protected void updateInfo() {
throw new RuntimeException(e);
}

backend.updateNameCache(this);
this.virtual.set(false);
}

Expand Down Expand Up @@ -92,6 +93,7 @@ public boolean setIdentifier(String identifier) {
conn.prepAndBind("entity.rename.permissions", identifier, this.identifier, this.type.ordinal()).execute();
conn.prepAndBind("entity.rename.inheritance", identifier, this.identifier, this.type.ordinal()).execute();
this.identifier = identifier;
backend.updateNameCache(this);
return true;
} catch (SQLException | IOException e) {
throw new RuntimeException(e);
Expand Down Expand Up @@ -289,6 +291,7 @@ public void remove() {
conn.prepAndBind("entity.delete.permissions", this.getIdentifier(), this.type.ordinal()).execute();
// clear info
conn.prepAndBind("entity.delete.entity", this.getIdentifier(), this.type.ordinal()).execute();
backend.updateNameCache(this);
} catch (SQLException | IOException e) {
throw new RuntimeException(e);
}
Expand Down Expand Up @@ -368,6 +371,10 @@ public void setParents(List<String> parents, String worldName) {
public void load() { // Nothing to load, we don't handle caching!
}

public Type getType() {
return type;
}

public enum Type {
GROUP, USER, WORLD
}
Expand Down

0 comments on commit 8ab8ccf

Please sign in to comment.