Refactor the way holder caches are invalidated in order to improve consistency (#734)

This commit is contained in:
Luck 2018-05-03 19:35:44 +01:00
parent b8a1871cf1
commit a6facf7492
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
22 changed files with 231 additions and 260 deletions

View File

@ -55,11 +55,6 @@ public class BukkitConfigAdapter extends AbstractConfigurationAdapter implements
this.configuration = YamlConfiguration.loadConfiguration(this.file); this.configuration = YamlConfiguration.loadConfiguration(this.file);
} }
@Override
public boolean contains(String path) {
return this.configuration.contains(path);
}
@Override @Override
public String getString(String path, String def) { public String getString(String path, String def) {
return this.configuration.getString(path, def); return this.configuration.getString(path, def);

View File

@ -403,7 +403,7 @@ public class VaultPermissionHook extends AbstractVaultPermission {
void holderSave(PermissionHolder holder) { void holderSave(PermissionHolder holder) {
if (holder.getType().isUser()) { if (holder.getType().isUser()) {
User u = (User) holder; User u = (User) holder;
this.plugin.getStorage().saveUser(u).thenRunAsync(() -> u.getRefreshBuffer().request(), this.plugin.getBootstrap().getScheduler().async()); this.plugin.getStorage().saveUser(u).thenRunAsync(() -> u.reloadCachedData(), this.plugin.getBootstrap().getScheduler().async());
} }
if (holder.getType().isGroup()) { if (holder.getType().isGroup()) {
Group g = (Group) holder; Group g = (Group) holder;

View File

@ -61,11 +61,6 @@ public class BungeeConfigAdapter extends AbstractConfigurationAdapter implements
} }
} }
@Override
public boolean contains(String path) {
return this.configuration.contains(path);
}
@Override @Override
public String getString(String path, String def) { public String getString(String path, String def) {
return this.configuration.getString(path, def); return this.configuration.getString(path, def);

View File

@ -98,7 +98,7 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
@Nonnull @Nonnull
@Override @Override
public CompletableFuture<Void> refreshCachedData() { public CompletableFuture<Void> refreshCachedData() {
return this.handle.getRefreshBuffer().request(); return this.handle.reloadCachedData();
} }
@Nonnull @Nonnull

View File

@ -99,7 +99,7 @@ public final class ApiUser extends ApiPermissionHolder implements me.lucko.luckp
@Override @Override
@Deprecated @Deprecated
public void refreshPermissions() { public void refreshPermissions() {
this.handle.getRefreshBuffer().requestDirectly(); this.handle.reloadCachedData().join();
} }
@Override @Override

View File

@ -25,9 +25,9 @@
package me.lucko.luckperms.common.caching; package me.lucko.luckperms.common.caching;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.api.ChatMetaType; import me.lucko.luckperms.api.ChatMetaType;
import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Contexts;
@ -43,7 +43,6 @@ import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.metastacking.SimpleMetaStack; import me.lucko.luckperms.common.metastacking.SimpleMetaStack;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -51,6 +50,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
* Abstract implementation of {@link CachedData}. * Abstract implementation of {@link CachedData}.
@ -65,16 +65,16 @@ public abstract class AbstractCachedData implements CachedData {
/** /**
* The cache used for {@link PermissionCache} instances. * The cache used for {@link PermissionCache} instances.
*/ */
private final LoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder() private final AsyncLoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder()
.expireAfterAccess(2, TimeUnit.MINUTES) .expireAfterAccess(2, TimeUnit.MINUTES)
.build(new PermissionCacheLoader()); .buildAsync(new PermissionCacheLoader());
/** /**
* The cache used for {@link MetaCache} instances. * The cache used for {@link MetaCache} instances.
*/ */
private final LoadingCache<MetaContexts, MetaCache> meta = Caffeine.newBuilder() private final AsyncLoadingCache<MetaContexts, MetaCache> meta = Caffeine.newBuilder()
.expireAfterAccess(2, TimeUnit.MINUTES) .expireAfterAccess(2, TimeUnit.MINUTES)
.build(new MetaCacheLoader()); .buildAsync(new MetaCacheLoader());
public AbstractCachedData(LuckPermsPlugin plugin) { public AbstractCachedData(LuckPermsPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -190,7 +190,7 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
//noinspection ConstantConditions //noinspection ConstantConditions
return this.permission.get(contexts); return this.permission.get(contexts).join();
} }
@Nonnull @Nonnull
@ -199,7 +199,7 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
//noinspection ConstantConditions //noinspection ConstantConditions
return this.meta.get(contexts); return this.meta.get(contexts).join();
} }
@Nonnull @Nonnull
@ -233,13 +233,13 @@ public abstract class AbstractCachedData implements CachedData {
@Override @Override
public void recalculatePermissions(@Nonnull Contexts contexts) { public void recalculatePermissions(@Nonnull Contexts contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
this.permission.refresh(contexts); this.permission.synchronous().refresh(contexts);
} }
@Override @Override
public void recalculateMeta(@Nonnull MetaContexts contexts) { public void recalculateMeta(@Nonnull MetaContexts contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
this.meta.refresh(contexts); this.meta.synchronous().refresh(contexts);
} }
@Override @Override
@ -254,13 +254,19 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
// get the previous value - to use when recalculating // get the previous value - to use when recalculating
PermissionCache previous = this.permission.getIfPresent(contexts); CompletableFuture<PermissionCache> previous = this.permission.getIfPresent(contexts);
// invalidate the entry // invalidate any previous setting
this.permission.invalidate(contexts); this.permission.synchronous().invalidate(contexts);
// repopulate the cache // if the previous value is already calculated, use it when recalculating.
return CompletableFuture.supplyAsync(() -> this.permission.get(contexts, c -> calculatePermissions(c, previous))); PermissionCache value = getIfReady(previous);
if (value != null) {
return this.permission.get(contexts, c -> calculatePermissions(c, value));
}
// otherwise, just calculate a new value
return this.permission.get(contexts);
} }
@Nonnull @Nonnull
@ -269,13 +275,19 @@ public abstract class AbstractCachedData implements CachedData {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
// get the previous value - to use when recalculating // get the previous value - to use when recalculating
MetaCache previous = this.meta.getIfPresent(contexts); CompletableFuture<MetaCache> previous = this.meta.getIfPresent(contexts);
// invalidate the entry // invalidate any previous setting
this.meta.invalidate(contexts); this.meta.synchronous().invalidate(contexts);
// repopulate the cache // if the previous value is already calculated, use it when recalculating.
return CompletableFuture.supplyAsync(() -> this.meta.get(contexts, c -> calculateMeta(c, previous))); MetaCache value = getIfReady(previous);
if (value != null) {
return this.meta.get(contexts, c -> calculateMeta(c, value));
}
// otherwise, just calculate a new value
return this.meta.get(contexts);
} }
@Nonnull @Nonnull
@ -287,30 +299,34 @@ public abstract class AbstractCachedData implements CachedData {
@Override @Override
public void recalculatePermissions() { public void recalculatePermissions() {
Set<Contexts> keys = this.permission.asMap().keySet(); Set<Contexts> keys = this.permission.synchronous().asMap().keySet();
keys.forEach(this::recalculatePermissions); keys.forEach(this::recalculatePermissions);
} }
@Override @Override
public void recalculateMeta() { public void recalculateMeta() {
Set<MetaContexts> keys = this.meta.asMap().keySet(); Set<MetaContexts> keys = this.meta.synchronous().asMap().keySet();
keys.forEach(this::recalculateMeta); keys.forEach(this::recalculateMeta);
} }
@Nonnull @Nonnull
@Override @Override
public CompletableFuture<Void> reloadPermissions() { public CompletableFuture<Void> reloadPermissions() {
Set<Contexts> keys = new HashSet<>(this.permission.asMap().keySet()); Set<Contexts> keys = this.permission.synchronous().asMap().keySet();
return CompletableFuture.allOf(keys.stream().map(this::reloadPermissions).toArray(CompletableFuture[]::new)); return CompletableFuture.allOf(keys.stream().map(this::reloadPermissions).toArray(CompletableFuture[]::new));
} }
@Nonnull @Nonnull
@Override @Override
public CompletableFuture<Void> reloadMeta() { public CompletableFuture<Void> reloadMeta() {
Set<MetaContexts> keys = new HashSet<>(this.meta.asMap().keySet()); Set<MetaContexts> keys = this.meta.synchronous().asMap().keySet();
return CompletableFuture.allOf(keys.stream().map(this::reloadMeta).toArray(CompletableFuture[]::new)); return CompletableFuture.allOf(keys.stream().map(this::reloadMeta).toArray(CompletableFuture[]::new));
} }
public CompletableFuture<Void> reloadAll() {
return CompletableFuture.allOf(reloadPermissions(), reloadMeta());
}
@Override @Override
public void preCalculate(@Nonnull Contexts contexts) { public void preCalculate(@Nonnull Contexts contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
@ -324,34 +340,45 @@ public abstract class AbstractCachedData implements CachedData {
@Override @Override
public void invalidatePermissions(@Nonnull Contexts contexts) { public void invalidatePermissions(@Nonnull Contexts contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
this.permission.invalidate(contexts); this.permission.synchronous().invalidate(contexts);
} }
@Override @Override
public void invalidateMeta(@Nonnull MetaContexts contexts) { public void invalidateMeta(@Nonnull MetaContexts contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
this.meta.invalidate(contexts); this.meta.synchronous().invalidate(contexts);
} }
@Override @Override
public void invalidateMeta(@Nonnull Contexts contexts) { public void invalidateMeta(@Nonnull Contexts contexts) {
Objects.requireNonNull(contexts, "contexts"); Objects.requireNonNull(contexts, "contexts");
this.meta.invalidate(getDefaultMetaContexts(contexts)); this.meta.synchronous().invalidate(getDefaultMetaContexts(contexts));
} }
@Override @Override
public void invalidatePermissionCalculators() { public void invalidatePermissionCalculators() {
this.permission.asMap().values().forEach(PermissionCache::invalidateCache); this.permission.synchronous().asMap().values().forEach(PermissionCache::invalidateCache);
} }
public void invalidateCaches() { public void invalidateCaches() {
this.permission.invalidateAll(); this.permission.synchronous().invalidateAll();
this.meta.invalidateAll(); this.meta.synchronous().invalidateAll();
} }
public void doCacheCleanup() { public void doCacheCleanup() {
this.permission.cleanUp(); this.permission.synchronous().cleanUp();
this.meta.cleanUp(); this.meta.synchronous().cleanUp();
}
private static boolean isReady(@Nullable CompletableFuture<?> future) {
return (future != null) && future.isDone()
&& !future.isCompletedExceptionally()
&& (future.join() != null);
}
/** Returns the current value or null if either not done or failed. */
private static <V> V getIfReady(@Nullable CompletableFuture<V> future) {
return isReady(future) ? future.join() : null;
} }
private final class PermissionCacheLoader implements CacheLoader<Contexts, PermissionCache> { private final class PermissionCacheLoader implements CacheLoader<Contexts, PermissionCache> {

View File

@ -108,7 +108,7 @@ public final class StorageAssistant {
return; return;
} }
user.getRefreshBuffer().requestDirectly(); user.reloadCachedData().join();
Optional<InternalMessagingService> messagingService = plugin.getMessagingService(); Optional<InternalMessagingService> messagingService = plugin.getMessagingService();
if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) { if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) {

View File

@ -36,8 +36,6 @@ public interface ConfigurationAdapter {
void reload(); void reload();
boolean contains(String path);
String getString(String path, String def); String getString(String path, String def);
int getInt(String path, int def); int getInt(String path, int def);

View File

@ -29,7 +29,6 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.nodetype.types.DisplayNameType; import me.lucko.luckperms.api.nodetype.types.DisplayNameType;
import me.lucko.luckperms.common.api.delegates.model.ApiGroup; import me.lucko.luckperms.common.api.delegates.model.ApiGroup;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.buffers.Cache; import me.lucko.luckperms.common.buffers.Cache;
import me.lucko.luckperms.common.caching.GroupCachedData; import me.lucko.luckperms.common.caching.GroupCachedData;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -61,34 +60,66 @@ public class Group extends PermissionHolder implements Identifiable<String> {
*/ */
private final GroupCachedData cachedData; private final GroupCachedData cachedData;
/**
* The group's cached data refresh buffer
*/
private final GroupRefreshBuffer refreshBuffer;
public Group(String name, LuckPermsPlugin plugin) { public Group(String name, LuckPermsPlugin plugin) {
super(name, plugin); super(plugin);
this.name = name.toLowerCase(); this.name = name.toLowerCase();
this.refreshBuffer = new GroupRefreshBuffer(plugin, this);
this.cachedData = new GroupCachedData(this); this.cachedData = new GroupCachedData(this);
getPlugin().getEventFactory().handleGroupCacheLoad(this, this.cachedData); getPlugin().getEventFactory().handleGroupCacheLoad(this, this.cachedData);
// invalidate our caches when data is updated
getStateListeners().add(this.refreshBuffer::request);
} }
@Override @Override
protected void invalidateCache() { protected void invalidateCache() {
super.invalidateCache();
// invalidate our caches
this.weightCache.invalidate(); this.weightCache.invalidate();
this.displayNameCache.invalidate(); this.displayNameCache.invalidate();
super.invalidateCache();
} }
// name getters
public String getName() { public String getName() {
return this.name; return this.name;
} }
@Override
public String getObjectName() {
return this.name;
}
@Override
public String getId() {
return this.name;
}
@Override
public String getFriendlyName() {
Optional<String> dn = getDisplayName();
return dn.map(s -> this.name + " (" + s + ")").orElse(this.name);
}
public Optional<String> getDisplayName() {
return this.displayNameCache.get();
}
/**
* Gets a display name value exactly matching a specific context.
*
* <p>Note that the behaviour of {@link #getDisplayName()} is not the same as this.</p>
*
* @param contextSet the contexts to lookup in
* @return the display name
*/
public Optional<String> getDisplayName(ContextSet contextSet) {
for (Node n : getData(NodeMapType.ENDURING).immutable().get(contextSet.makeImmutable())) {
Optional<DisplayNameType> displayName = n.getTypeData(DisplayNameType.KEY);
if (displayName.isPresent()) {
return Optional.of(displayName.get().getDisplayName());
}
}
return Optional.empty();
}
public ApiGroup getApiDelegate() { public ApiGroup getApiDelegate() {
return this.apiDelegate; return this.apiDelegate;
} }
@ -98,36 +129,6 @@ public class Group extends PermissionHolder implements Identifiable<String> {
return this.cachedData; return this.cachedData;
} }
@Override
public BufferedRequest<Void> getRefreshBuffer() {
return this.refreshBuffer;
}
@Override
public String getId() {
return this.name;
}
public Optional<String> getDisplayName() {
return this.displayNameCache.get();
}
public Optional<String> getDisplayName(ContextSet contextSet) {
for (Node n : getData(NodeMapType.ENDURING).immutable().get(contextSet.makeImmutable())) {
Optional<DisplayNameType> displayName = n.getTypeData(DisplayNameType.KEY);
if (displayName.isPresent()) {
return Optional.of(displayName.get().getDisplayName());
}
}
return Optional.empty();
}
@Override
public String getFriendlyName() {
Optional<String> dn = getDisplayName();
return dn.map(s -> this.name + " (" + s + ")").orElse(this.name);
}
@Override @Override
public OptionalInt getWeight() { public OptionalInt getWeight() {
return this.weightCache.get(); return this.weightCache.get();
@ -138,11 +139,10 @@ public class Group extends PermissionHolder implements Identifiable<String> {
return HolderType.GROUP; return HolderType.GROUP;
} }
private CompletableFuture<Void> reloadCachedData() { @Override
return CompletableFuture.allOf( public CompletableFuture<Void> reloadCachedData() {
this.cachedData.reloadPermissions(), return this.cachedData.reloadAll()
this.cachedData.reloadMeta() .thenAccept(n -> getPlugin().getEventFactory().handleGroupDataRecalculate(this, this.cachedData));
).thenAccept(n -> getPlugin().getEventFactory().handleGroupDataRecalculate(this, this.cachedData));
} }
@Override @Override
@ -163,18 +163,4 @@ public class Group extends PermissionHolder implements Identifiable<String> {
return "Group(name=" + this.name + ")"; return "Group(name=" + this.name + ")";
} }
private static final class GroupRefreshBuffer extends BufferedRequest<Void> {
private final Group group;
private GroupRefreshBuffer(LuckPermsPlugin plugin, Group group) {
super(50L, 5L, plugin.getBootstrap().getScheduler().async());
this.group = group;
}
@Override
protected Void perform() {
return this.group.reloadCachedData().join();
}
}
} }

View File

@ -40,9 +40,7 @@ import me.lucko.luckperms.api.StandardNodeEquality;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.caching.HolderCachedData; import me.lucko.luckperms.common.caching.HolderCachedData;
import me.lucko.luckperms.common.caching.handlers.StateListener;
import me.lucko.luckperms.common.caching.type.MetaAccumulator; import me.lucko.luckperms.common.caching.type.MetaAccumulator;
import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.inheritance.InheritanceComparator; import me.lucko.luckperms.common.inheritance.InheritanceComparator;
@ -67,7 +65,7 @@ import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -95,18 +93,6 @@ import java.util.function.Predicate;
*/ */
public abstract class PermissionHolder { public abstract class PermissionHolder {
/**
* The name of this object.
*
* <p>Used as a base for identifying permission holding objects. Also acts
* as a method for preventing circular inheritance issues.</p>
*
* @see User#getUuid()
* @see Group#getName()
* @see #getObjectName()
*/
private final String objectName;
/** /**
* Reference to the main plugin instance * Reference to the main plugin instance
* @see #getPlugin() * @see #getPlugin()
@ -146,18 +132,15 @@ public abstract class PermissionHolder {
private final Comparator<Group> inheritanceComparator = InheritanceComparator.getFor(this); private final Comparator<Group> inheritanceComparator = InheritanceComparator.getFor(this);
/** /**
* A set of runnables which are called when this objects state changes. * Creates a new instance
*
* @param plugin the plugin instance
*/ */
private final Set<StateListener> stateListeners = ConcurrentHashMap.newKeySet(); protected PermissionHolder(LuckPermsPlugin plugin) {
protected PermissionHolder(String objectName, LuckPermsPlugin plugin) {
this.objectName = objectName;
this.plugin = plugin; this.plugin = plugin;
} }
public String getObjectName() { // getters
return this.objectName;
}
public LuckPermsPlugin getPlugin() { public LuckPermsPlugin getPlugin() {
return this.plugin; return this.plugin;
@ -167,52 +150,6 @@ public abstract class PermissionHolder {
return this.ioLock; return this.ioLock;
} }
public Set<StateListener> getStateListeners() {
return this.stateListeners;
}
protected void invalidateCache() {
this.enduringNodes.invalidate();
this.transientNodes.invalidate();
// Invalidate listeners
for (StateListener listener : this.stateListeners) {
try {
listener.onStateChange();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Gets the friendly name of this permission holder (for use in commands, etc)
*
* @return the holders "friendly" name
*/
public abstract String getFriendlyName();
/**
* Gets the holders cached data
*
* @return the holders cached data
*/
public abstract HolderCachedData<?> getCachedData();
/**
* Gets the holders refresh buffer
*
* @return the holders refresh buffer
*/
public abstract BufferedRequest<Void> getRefreshBuffer();
/**
* Returns the type of this PermissionHolder.
*
* @return this holders type
*/
public abstract HolderType getType();
public Comparator<Group> getInheritanceComparator() { public Comparator<Group> getInheritanceComparator() {
return this.inheritanceComparator; return this.inheritanceComparator;
} }
@ -236,6 +173,51 @@ public abstract class PermissionHolder {
return this.transientNodes; return this.transientNodes;
} }
/**
* Gets the unique name of this holder object.
*
* <p>Used as a base for identifying permission holding objects. Also acts
* as a method for preventing circular inheritance issues.</p>
*
* @return the object name
*/
public abstract String getObjectName();
/**
* Gets the friendly name of this permission holder (for use in commands, etc)
*
* @return the holders "friendly" name
*/
public abstract String getFriendlyName();
/**
* Gets the holders cached data
*
* @return the holders cached data
*/
public abstract HolderCachedData<?> getCachedData();
/**
* Returns the type of this PermissionHolder.
*
* @return this holders type
*/
public abstract HolderType getType();
/**
* Reloads the holder's cached data.
*
* @return a future encapsulating the result
*/
public abstract CompletableFuture<Void> reloadCachedData();
protected void invalidateCache() {
this.enduringNodes.invalidate();
this.transientNodes.invalidate();
reloadCachedData();
}
public void setNodes(NodeMapType type, Set<Node> set) { public void setNodes(NodeMapType type, Set<Node> set) {
getData(type).setContent(set); getData(type).setContent(set);
invalidateCache(); invalidateCache();

View File

@ -26,16 +26,18 @@
package me.lucko.luckperms.common.model; package me.lucko.luckperms.common.model;
import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.caching.UserCachedData; import me.lucko.luckperms.common.caching.UserCachedData;
import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.primarygroup.CachedPrimaryGroupHolder;
import me.lucko.luckperms.common.primarygroup.PrimaryGroupHolder; import me.lucko.luckperms.common.primarygroup.PrimaryGroupHolder;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
public class User extends PermissionHolder implements Identifiable<UserIdentifier> { public class User extends PermissionHolder implements Identifiable<UserIdentifier> {
/** /**
@ -46,6 +48,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
/** /**
* The last known username of a player * The last known username of a player
*/ */
@Nullable
private String name = null; private String name = null;
/** /**
@ -58,14 +61,11 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
*/ */
private final UserCachedData cachedData; private final UserCachedData cachedData;
private final UserRefreshBuffer refreshBuffer;
public User(UUID uuid, String name, LuckPermsPlugin plugin) { public User(UUID uuid, String name, LuckPermsPlugin plugin) {
super(uuid.toString(), plugin); super(plugin);
this.uuid = uuid; this.uuid = uuid;
setName(name, false); setName(name, false);
this.refreshBuffer = new UserRefreshBuffer(plugin, this);
this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this); this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this);
this.cachedData = new UserCachedData(this); this.cachedData = new UserCachedData(this);
@ -76,22 +76,27 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
this(uuid, null, plugin); this(uuid, null, plugin);
} }
@Override
protected void invalidateCache() {
super.invalidateCache();
// invalidate our caches
if (this.primaryGroup instanceof CachedPrimaryGroupHolder) {
((CachedPrimaryGroupHolder) this.primaryGroup).invalidate();
}
}
public UUID getUuid() { public UUID getUuid() {
return this.uuid; return this.uuid;
} }
public PrimaryGroupHolder getPrimaryGroup() { public Optional<String> getName() {
return this.primaryGroup; return Optional.ofNullable(this.name);
} }
@Override @Override
public UserCachedData getCachedData() { public String getObjectName() {
return this.cachedData; return this.uuid.toString();
}
@Override
public BufferedRequest<Void> getRefreshBuffer() {
return this.refreshBuffer;
} }
@Override @Override
@ -99,8 +104,18 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
return UserIdentifier.of(this.uuid, this.name); return UserIdentifier.of(this.uuid, this.name);
} }
public Optional<String> getName() { @Override
return Optional.ofNullable(this.name); public String getFriendlyName() {
return this.name != null ? this.name : this.uuid.toString();
}
@Override
public UserCachedData getCachedData() {
return this.cachedData;
}
public PrimaryGroupHolder getPrimaryGroup() {
return this.primaryGroup;
} }
/** /**
@ -152,11 +167,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
} }
} }
@Override
public String getFriendlyName() {
return this.name != null ? this.name : this.uuid.toString();
}
@Override @Override
public HolderType getType() { public HolderType getType() {
return HolderType.USER; return HolderType.USER;
@ -168,7 +178,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
*/ */
public void preCalculateData() { public void preCalculateData() {
// first try to refresh any existing permissions // first try to refresh any existing permissions
this.refreshBuffer.requestDirectly(); reloadCachedData().join();
// pre-calc the allowall & global contexts // pre-calc the allowall & global contexts
// since contexts change so frequently, it's not worth trying to calculate any more than this. // since contexts change so frequently, it's not worth trying to calculate any more than this.
@ -176,11 +186,10 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
this.cachedData.preCalculate(Contexts.global()); this.cachedData.preCalculate(Contexts.global());
} }
@Override
public CompletableFuture<Void> reloadCachedData() { public CompletableFuture<Void> reloadCachedData() {
return CompletableFuture.allOf( return this.cachedData.reloadAll()
this.cachedData.reloadPermissions(), .thenAccept(n -> getPlugin().getEventFactory().handleUserDataRecalculate(this, this.cachedData));
this.cachedData.reloadMeta()
).thenAccept(n -> getPlugin().getEventFactory().handleUserDataRecalculate(this, this.cachedData));
} }
/** /**
@ -197,13 +206,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
return true; return true;
} }
public void clearNodes(boolean giveDefault) {
super.clearNodes();
if (giveDefault) {
getPlugin().getUserManager().giveDefaultIfNeeded(this, false);
}
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == this) return true; if (o == this) return true;
@ -222,18 +224,4 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
return "User(uuid=" + this.uuid + ")"; return "User(uuid=" + this.uuid + ")";
} }
private static final class UserRefreshBuffer extends BufferedRequest<Void> {
private final User user;
private UserRefreshBuffer(LuckPermsPlugin plugin, User user) {
super(50L, 5L, plugin.getBootstrap().getScheduler().async());
this.user = user;
}
@Override
protected Void perform() {
return this.user.reloadCachedData().join();
}
}
} }

View File

@ -26,16 +26,15 @@
package me.lucko.luckperms.common.primarygroup; package me.lucko.luckperms.common.primarygroup;
import me.lucko.luckperms.common.buffers.Cache; import me.lucko.luckperms.common.buffers.Cache;
import me.lucko.luckperms.common.caching.handlers.StateListener;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.factory.NodeFactory; import me.lucko.luckperms.common.node.factory.NodeFactory;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* Abstract implementation of {@link StateListener} which caches all lookups. * Abstract implementation of {@link PrimaryGroupHolder} which caches all lookups.
*/ */
public abstract class CachedPrimaryGroupHolder extends StoredHolder implements StateListener { public abstract class CachedPrimaryGroupHolder extends StoredHolder {
// cache lookups // cache lookups
private final Cache<String> cache = new Cache<String>() { private final Cache<String> cache = new Cache<String>() {
@ -48,20 +47,18 @@ public abstract class CachedPrimaryGroupHolder extends StoredHolder implements S
public CachedPrimaryGroupHolder(User user) { public CachedPrimaryGroupHolder(User user) {
super(user); super(user);
user.getStateListeners().add(this);
} }
protected abstract String calculateValue(); protected abstract String calculateValue();
public void invalidate() {
this.cache.invalidate();;
}
@Override @Override
public final String getValue() { public final String getValue() {
String s = this.cache.get(); String s = this.cache.get();
return s != null ? s : getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME); return s != null ? s : getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME);
} }
@Override
public void onStateChange() {
this.cache.invalidate();
}
} }

View File

@ -205,7 +205,7 @@ public abstract class AbstractConfigurateDao extends AbstractDao {
} finally { } finally {
user.getIoLock().unlock(); user.getIoLock().unlock();
} }
user.getRefreshBuffer().requestDirectly(); user.reloadCachedData().join();
return user; return user;
} }
@ -261,7 +261,7 @@ public abstract class AbstractConfigurateDao extends AbstractDao {
} finally { } finally {
group.getIoLock().unlock(); group.getIoLock().unlock();
} }
group.getRefreshBuffer().requestDirectly(); group.reloadCachedData().join();
return group; return group;
} }
@ -295,7 +295,7 @@ public abstract class AbstractConfigurateDao extends AbstractDao {
group.getIoLock().unlock(); group.getIoLock().unlock();
} }
} }
group.getRefreshBuffer().requestDirectly(); group.reloadCachedData().join();
return Optional.of(group); return Optional.of(group);
} }

View File

@ -286,7 +286,7 @@ public class MongoDao extends AbstractDao {
} finally { } finally {
user.getIoLock().unlock(); user.getIoLock().unlock();
} }
user.getRefreshBuffer().requestDirectly(); user.reloadCachedData().join();
return user; return user;
} }
@ -357,7 +357,7 @@ public class MongoDao extends AbstractDao {
} finally { } finally {
group.getIoLock().unlock(); group.getIoLock().unlock();
} }
group.getRefreshBuffer().requestDirectly(); group.reloadCachedData().join();
return group; return group;
} }
@ -388,7 +388,7 @@ public class MongoDao extends AbstractDao {
group.getIoLock().unlock(); group.getIoLock().unlock();
} }
} }
group.getRefreshBuffer().requestDirectly(); group.reloadCachedData().join();
return Optional.of(group); return Optional.of(group);
} }

View File

@ -356,7 +356,7 @@ public class SqlDao extends AbstractDao {
} finally { } finally {
user.getIoLock().unlock(); user.getIoLock().unlock();
} }
user.getRefreshBuffer().requestDirectly(); user.reloadCachedData().join();
return user; return user;
} }
@ -595,7 +595,7 @@ public class SqlDao extends AbstractDao {
} finally { } finally {
group.getIoLock().unlock(); group.getIoLock().unlock();
} }
group.getRefreshBuffer().requestDirectly(); group.reloadCachedData().join();
return Optional.of(group); return Optional.of(group);
} }

View File

@ -57,7 +57,7 @@ public class ExpireTemporaryTask implements Runnable {
if (user.auditTemporaryPermissions()) { if (user.auditTemporaryPermissions()) {
this.plugin.getStorage().saveUser(user); this.plugin.getStorage().saveUser(user);
if (!groupChanges) { if (!groupChanges) {
user.getRefreshBuffer().request(); user.reloadCachedData();
} }
} }
} }

View File

@ -55,11 +55,6 @@ public class NukkitConfigAdapter extends AbstractConfigurationAdapter implements
this.configuration = new Config(this.file, Config.YAML); this.configuration = new Config(this.file, Config.YAML);
} }
@Override
public boolean contains(String path) {
return this.configuration.exists(path);
}
@Override @Override
public String getString(String path, String def) { public String getString(String path, String def) {
return this.configuration.getString(path, def); return this.configuration.getString(path, def);

View File

@ -85,11 +85,6 @@ public class SpongeConfigAdapter extends AbstractConfigurationAdapter implements
return node; return node;
} }
@Override
public boolean contains(String path) {
return !resolvePath(path).isVirtual();
}
@Override @Override
public String getString(String path, String def) { public String getString(String path, String def) {
return resolvePath(path).getString(def); return resolvePath(path).getString(def);

View File

@ -37,6 +37,13 @@ public class SpongeGroup extends Group implements SpongePermissionHolder {
this.spongeData = new GroupSubject(plugin, this); this.spongeData = new GroupSubject(plugin, this);
} }
@Override
protected void invalidateCache() {
super.invalidateCache();
this.spongeData.fireUpdateEvent();
}
@Override @Override
public GroupSubject sponge() { public GroupSubject sponge() {
return this.spongeData; return this.spongeData;

View File

@ -48,6 +48,13 @@ public class SpongeUser extends User implements SpongePermissionHolder {
this.spongeData = new UserSubject(plugin, this); this.spongeData = new UserSubject(plugin, this);
} }
@Override
protected void invalidateCache() {
super.invalidateCache();
this.spongeData.fireUpdateEvent();
}
@Override @Override
public UserSubject sponge() { public UserSubject sponge() {
return this.spongeData; return this.spongeData;

View File

@ -66,12 +66,11 @@ public abstract class HolderSubject<T extends PermissionHolder> implements LPSub
this.plugin = plugin; this.plugin = plugin;
this.subjectData = new HolderSubjectData(plugin.getService(), NodeMapType.ENDURING, parent, this); this.subjectData = new HolderSubjectData(plugin.getService(), NodeMapType.ENDURING, parent, this);
this.transientSubjectData = new HolderSubjectData(plugin.getService(), NodeMapType.TRANSIENT, parent, this); this.transientSubjectData = new HolderSubjectData(plugin.getService(), NodeMapType.TRANSIENT, parent, this);
}
// fire update event public void fireUpdateEvent() {
parent.getStateListeners().add(() -> { this.plugin.getUpdateEventHandler().fireUpdateEvent(this.subjectData);
plugin.getUpdateEventHandler().fireUpdateEvent(this.subjectData); this.plugin.getUpdateEventHandler().fireUpdateEvent(this.transientSubjectData);
plugin.getUpdateEventHandler().fireUpdateEvent(this.transientSubjectData);
});
} }
public T getParent() { public T getParent() {

View File

@ -450,7 +450,7 @@ public class HolderSubjectData implements LPSubjectData {
// don't bother saving to primary storage. just refresh // don't bother saving to primary storage. just refresh
if (t.getType().isUser()) { if (t.getType().isUser()) {
User user = ((User) t); User user = ((User) t);
return user.getRefreshBuffer().request(); return user.reloadCachedData();
} else { } else {
return this.service.getPlugin().getUpdateTaskBuffer().request(); return this.service.getPlugin().getUpdateTaskBuffer().request();
} }
@ -465,7 +465,7 @@ public class HolderSubjectData implements LPSubjectData {
fut.complete(null); fut.complete(null);
} }
user.getRefreshBuffer().request().thenAccept(fut::complete); user.reloadCachedData().thenAccept(fut::complete);
}, this.service.getPlugin().getBootstrap().getScheduler().async()); }, this.service.getPlugin().getBootstrap().getScheduler().async());
return fut; return fut;
} else { } else {