Fix bad caching logic resulting in super high CPU usage

This commit is contained in:
Luck 2018-05-04 23:12:46 +01:00
parent d8a7d8de4a
commit 10c0efaa5d
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
20 changed files with 40 additions and 122 deletions

View File

@ -178,9 +178,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
// set the transient node
User user = this.permissible.getUser();
if (user.setTransientPermission(transientNode).asBoolean()) {
user.reloadCachedData();
}
user.setTransientPermission(transientNode);
}
private void unsetPermissionInternal(String name) {
@ -190,17 +188,13 @@ public class LPPermissionAttachment extends PermissionAttachment {
// remove transient permissions from the holder which were added by this attachment & equal the permission
User user = this.permissible.getUser();
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name))) {
user.reloadCachedData();
}
user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name));
}
private void clearInternal() {
// remove all transient permissions added by this attachment
User user = this.permissible.getUser();
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this)) {
user.reloadCachedData();
}
user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this);
}
@Override

View File

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

View File

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

View File

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

View File

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

View File

@ -117,11 +117,6 @@ public final class EventFactory {
fireEventAsync(event);
}
public void handleGroupDataRecalculate(Group group, GroupData data) {
EventGroupDataRecalculate event = new EventGroupDataRecalculate(group.getApiDelegate(), data);
fireEventAsync(event);
}
public void handleGroupDelete(Group group, DeletionCause cause) {
EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.enduringData().immutable().values()), cause);
fireEventAsync(event);
@ -249,9 +244,16 @@ public final class EventFactory {
fireEventAsync(event);
}
public void handleUserDataRecalculate(User user, UserData data) {
EventUserDataRecalculate event = new EventUserDataRecalculate(new ApiUser(user), data);
fireEventAsync(event);
public void handleDataRecalculate(PermissionHolder holder) {
if (holder.getType().isUser()) {
User user = (User) holder;
EventUserDataRecalculate event = new EventUserDataRecalculate(user.getApiDelegate(), user.getCachedData());
fireEventAsync(event);
} else {
Group group = (Group) holder;
EventGroupDataRecalculate event = new EventGroupDataRecalculate(group.getApiDelegate(), group.getCachedData());
fireEventAsync(event);
}
}
public void handleUserFirstLogin(UUID uuid, String username) {

View File

@ -89,9 +89,6 @@ public abstract class AbstractConnectionListener implements ConnectionListener {
if (save) {
this.plugin.getStorage().saveUser(user).join();
}
// Does some minimum pre-calculations to (maybe) speed things up later.
user.preCalculateData();
}
final long time = System.currentTimeMillis() - startTime;

View File

@ -35,7 +35,6 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CompletableFuture;
public class Group extends PermissionHolder implements Identifiable<String> {
private final ApiGroup apiDelegate = new ApiGroup(this);
@ -139,12 +138,6 @@ public class Group extends PermissionHolder implements Identifiable<String> {
return HolderType.GROUP;
}
@Override
public CompletableFuture<Void> reloadCachedData() {
return this.cachedData.reloadAll()
.thenAccept(n -> getPlugin().getEventFactory().handleGroupDataRecalculate(this, this.cachedData));
}
@Override
public boolean equals(Object o) {
if (o == this) return true;

View File

@ -63,7 +63,6 @@ import java.util.OptionalInt;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
@ -203,17 +202,18 @@ public abstract class PermissionHolder {
public abstract HolderType getType();
/**
* Reloads the holder's cached data.
*
* @return a future encapsulating the result
* Invalidates the holder's cached data.
*/
public abstract CompletableFuture<Void> reloadCachedData();
public void invalidateCachedData() {
getCachedData().invalidateCaches();
}
protected void invalidateCache() {
this.enduringNodes.invalidate();
this.transientNodes.invalidate();
reloadCachedData();
invalidateCachedData();
getPlugin().getEventFactory().handleDataRecalculate(this);
}
public void setNodes(NodeMapType type, Set<? extends Node> set) {

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.common.model;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.api.delegates.model.ApiUser;
import me.lucko.luckperms.common.caching.UserCachedData;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
@ -34,11 +34,11 @@ import me.lucko.luckperms.common.primarygroup.PrimaryGroupHolder;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nullable;
public class User extends PermissionHolder implements Identifiable<UserIdentifier> {
private final ApiUser apiDelegate = new ApiUser(this);
/**
* The users Mojang UUID
@ -109,6 +109,10 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
return this.name != null ? this.name : this.uuid.toString();
}
public ApiUser getApiDelegate() {
return this.apiDelegate;
}
@Override
public UserCachedData getCachedData() {
return this.cachedData;
@ -172,26 +176,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
return HolderType.USER;
}
/**
* Sets up the UserData cache
* Blocking call.
*/
public void preCalculateData() {
// first try to refresh any existing permissions
reloadCachedData().join();
// pre-calc the allowall & global contexts
// since contexts change so frequently, it's not worth trying to calculate any more than this.
this.cachedData.preCalculate(Contexts.allowAll());
this.cachedData.preCalculate(Contexts.global());
}
@Override
public CompletableFuture<Void> reloadCachedData() {
return this.cachedData.reloadAll()
.thenAccept(n -> getPlugin().getEventFactory().handleUserDataRecalculate(this, this.cachedData));
}
/**
* Clear all of the users permission nodes
*/

View File

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

View File

@ -287,7 +287,6 @@ public class MongoDao extends AbstractDao {
} finally {
user.getIoLock().unlock();
}
user.reloadCachedData().join();
return user;
}
@ -358,7 +357,6 @@ public class MongoDao extends AbstractDao {
} finally {
group.getIoLock().unlock();
}
group.reloadCachedData().join();
return group;
}
@ -389,7 +387,6 @@ public class MongoDao extends AbstractDao {
group.getIoLock().unlock();
}
}
group.reloadCachedData().join();
return Optional.of(group);
}

View File

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

View File

@ -56,9 +56,6 @@ public class ExpireTemporaryTask implements Runnable {
}
if (user.auditTemporaryPermissions()) {
this.plugin.getStorage().saveUser(user);
if (!groupChanges) {
user.reloadCachedData();
}
}
}

View File

@ -179,9 +179,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
// set the transient node
User user = this.permissible.getUser();
if (user.setTransientPermission(transientNode).asBoolean()) {
user.reloadCachedData();
}
user.setTransientPermission(transientNode).asBoolean();
}
private void unsetPermissionInternal(String name) {
@ -191,17 +189,13 @@ public class LPPermissionAttachment extends PermissionAttachment {
// remove transient permissions from the holder which were added by this attachment & equal the permission
User user = this.permissible.getUser();
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name))) {
user.reloadCachedData();
}
user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name));
}
private void clearInternal() {
// remove all transient permissions added by this attachment
User user = this.permissible.getUser();
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this)) {
user.reloadCachedData();
}
user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this);
}
@Override

View File

@ -32,7 +32,6 @@ import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.SimpleConfigurationNode;
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
import ninja.leaping.configurate.loader.ConfigurationLoader;
@ -67,22 +66,11 @@ public class SpongeConfigAdapter extends AbstractConfigurationAdapter implements
}
private ConfigurationNode resolvePath(String path) {
Iterable<String> paths = Splitter.on('.').split(path);
ConfigurationNode node = this.root;
if (node == null) {
if (this.root == null) {
throw new RuntimeException("Config is not loaded.");
}
for (String s : paths) {
node = node.getNode(s);
if (node == null) {
return SimpleConfigurationNode.root();
}
}
return node;
return this.root.getNode(Splitter.on('.').splitToList(path).toArray());
}
@Override

View File

@ -81,8 +81,6 @@ public class SpongeUserManager extends AbstractUserManager<SpongeUser> implement
user.getIoLock().lock();
user.getIoLock().unlock();
// ok, data is here, let's do the pre-calculation stuff.
user.preCalculateData();
return user.sponge();
}
@ -94,7 +92,6 @@ public class SpongeUserManager extends AbstractUserManager<SpongeUser> implement
throw new RuntimeException();
}
user.preCalculateData();
return user.sponge();
});

View File

@ -448,10 +448,7 @@ public class HolderSubjectData implements LPSubjectData {
// handle transient first
if (this.type == NodeMapType.TRANSIENT) {
// don't bother saving to primary storage. just refresh
if (t.getType().isUser()) {
User user = ((User) t);
return user.reloadCachedData();
} else {
if (t.getType().isGroup()) {
return this.service.getPlugin().getUpdateTaskBuffer().request();
}
}
@ -459,26 +456,11 @@ public class HolderSubjectData implements LPSubjectData {
// handle enduring
if (t.getType().isUser()) {
User user = ((User) t);
CompletableFuture<Void> fut = new CompletableFuture<>();
this.service.getPlugin().getStorage().saveUser(user).whenCompleteAsync((v, ex) -> {
if (ex != null) {
fut.complete(null);
}
user.reloadCachedData().thenAccept(fut::complete);
}, this.service.getPlugin().getBootstrap().getScheduler().async());
return fut;
return this.service.getPlugin().getStorage().saveUser(user);
} else {
Group group = ((Group) t);
CompletableFuture<Void> fut = new CompletableFuture<>();
this.service.getPlugin().getStorage().saveGroup(group).whenCompleteAsync((v, ex) -> {
if (ex != null) {
fut.complete(null);
}
this.service.getPlugin().getUpdateTaskBuffer().request().thenAccept(fut::complete);
}, this.service.getPlugin().getBootstrap().getScheduler().async());
return fut;
return this.service.getPlugin().getStorage().saveGroup(group)
.thenCompose(v -> this.service.getPlugin().getUpdateTaskBuffer().request());
}
}
}

View File

@ -67,7 +67,7 @@ public class SubjectDataContainer {
* @param root the root json object
* @return a container representing the json data
*/
public static SubjectDataContainer derserialize(LPPermissionService service, JsonObject root) {
public static SubjectDataContainer deserialize(LPPermissionService service, JsonObject root) {
return new SubjectDataContainer(service, root);
}

View File

@ -202,7 +202,7 @@ public class SubjectStorage {
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
JsonObject data = this.gson.fromJson(reader, JsonObject.class);
SubjectDataContainer model = SubjectDataContainer.derserialize(this.service, data);
SubjectDataContainer model = SubjectDataContainer.deserialize(this.service, data);
return new LoadedSubject(subjectName, model);
}
}