Remove context pre-processing (mostly)

This approach isn't very effective when contexts are frequently changing, and it is hard to guess in advance which contexts are going to be in-use.

The Sponge version has proven that this whole system isn't really necessary.

Contexts for 'allow all' and 'global' are still pre-processed, however this should be significantly less work for the server. (even if it is being done async)
This commit is contained in:
Luck 2017-10-12 20:17:52 +01:00
parent 9c505e4402
commit 8920396360
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
15 changed files with 53 additions and 227 deletions

View File

@ -174,11 +174,6 @@ public class BukkitListener implements Listener {
}
plugin.refreshAutoOp(player);
// We assume all users are not op, but those who are need extra calculation.
if (player.isOp()) {
plugin.doAsync(() -> user.getUserData().preCalculate(plugin.getPreProcessContexts(true)));
}
}
@EventHandler(priority = EventPriority.MONITOR)

View File

@ -31,8 +31,6 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.calculators.BukkitCalculatorFactory;
import me.lucko.luckperms.bukkit.contexts.BukkitContextManager;
import me.lucko.luckperms.bukkit.contexts.WorldCalculator;
@ -82,7 +80,6 @@ import me.lucko.luckperms.common.utils.LoginHelper;
import me.lucko.luckperms.common.utils.UuidCache;
import me.lucko.luckperms.common.verbose.VerboseHandler;
import org.bukkit.World;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
@ -346,7 +343,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
final User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
user.unregisterData();
user.getUserData().invalidateCaches();
getUserManager().unload(user);
}
}
@ -376,31 +373,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
// Bukkit will do this again when #onDisable completes, but we do it early to prevent NPEs elsewhere.
getServer().getScheduler().cancelTasks(this);
HandlerList.unregisterAll(this);
// Null everything
vaultHookManager = null;
configuration = null;
userManager = null;
groupManager = null;
trackManager = null;
storage = null;
fileWatcher = null;
messagingService = null;
uuidCache = null;
listener = null;
apiProvider = null;
log = null;
defaultsProvider = null;
childPermissionProvider = null;
localeManager = null;
cachedStateManager = null;
contextManager = null;
calculatorFactory = null;
updateTaskBuffer = null;
verboseHandler = null;
senderFactory = null;
permissionVault = null;
logDispatcher = null;
getLog().info("Goodbye!");
}
public void tryVaultHook(boolean force) {
@ -538,75 +511,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public Set<Contexts> getPreProcessContexts(boolean op) {
Set<ContextSet> c = new HashSet<>();
c.add(ContextSet.empty());
c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.SERVER)));
// Pre process all worlds
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
MutableContextSet set = MutableContextSet.create();
set.add("server", getConfiguration().get(ConfigKeys.SERVER));
set.add("world", s);
set.addAll(configuration.getContextsFile().getStaticContexts());
return set.makeImmutable();
})
.collect(Collectors.toList())
);
// Pre process the separate Vault server, if any
if (!getConfiguration().get(ConfigKeys.SERVER).equals(getConfiguration().get(ConfigKeys.VAULT_SERVER))) {
c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.VAULT_SERVER)));
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
MutableContextSet set = MutableContextSet.create();
set.add("server", getConfiguration().get(ConfigKeys.VAULT_SERVER));
set.add("world", s);
set.addAll(configuration.getContextsFile().getStaticContexts());
return set.makeImmutable();
})
.collect(Collectors.toList())
);
}
Set<Contexts> contexts = new HashSet<>();
// Convert to full Contexts
contexts.addAll(c.stream()
.map(set -> new Contexts(
set,
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
true,
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
op
))
.collect(Collectors.toSet())
);
// Check for and include varying Vault config options
boolean vaultDiff = getConfiguration().get(ConfigKeys.VAULT_INCLUDING_GLOBAL) != getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS) ||
!getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS) ||
!getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS) ||
!getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS);
if (vaultDiff) {
contexts.addAll(c.stream()
.map(map -> new Contexts(map, getConfiguration().get(ConfigKeys.VAULT_INCLUDING_GLOBAL), true, true, true, true, op))
.collect(Collectors.toSet())
);
}
return contexts;
}
@Override
public LinkedHashMap<String, Object> getExtraInfo() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
public Map<String, Object> getExtraInfo() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Vault Enabled", vaultHookManager != null);
map.put("Bukkit Defaults count", defaultsProvider.size());
map.put("Bukkit Child Permissions count", childPermissionProvider.getPermissions().size());

View File

@ -29,7 +29,6 @@ import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bungee.event.TristateCheckEvent;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
@ -44,12 +43,10 @@ import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PermissionCheckEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
@ -192,24 +189,4 @@ public class BungeeListener implements Listener {
e.setResult(result);
}
// We don't pre-process all servers, so we have to do it here.
@EventHandler(priority = EventPriority.LOWEST)
public void onServerSwitch(ServerConnectEvent e) {
String serverName = e.getTarget().getName();
UUID uuid = e.getPlayer().getUniqueId();
plugin.doAsync(() -> {
MutableContextSet set = MutableContextSet.create();
set.add("server", plugin.getConfiguration().get(ConfigKeys.SERVER));
set.add("world", serverName);
User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(uuid));
if (user == null) {
return;
}
Contexts contexts = plugin.getContextManager().formContexts(e.getPlayer(), set.makeImmutable());
user.getUserData().preCalculate(contexts);
});
}
}

View File

@ -30,8 +30,6 @@ import lombok.Getter;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.bungee.calculators.BungeeCalculatorFactory;
import me.lucko.luckperms.bungee.contexts.BackendServerCalculator;
import me.lucko.luckperms.bungee.contexts.BungeeContextManager;
@ -83,7 +81,6 @@ import net.md_5.bungee.api.plugin.Plugin;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@ -215,6 +212,9 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
@Override
public void onDisable() {
permissionVault.setShutdown(true);
verboseHandler.setShutdown(true);
getLog().info("Closing storage...");
storage.shutdown();
@ -231,8 +231,10 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
getLog().info("Shutting down internal scheduler...");
scheduler.shutdown();
getProxy().getScheduler().cancel(this);
getProxy().getPluginManager().unregisterListeners(this);
getLog().info("Goodbye!");
}
@Override
@ -324,14 +326,4 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
public Sender getConsoleSender() {
return getSenderFactory().wrap(getProxy().getConsole());
}
@Override
public Set<Contexts> getPreProcessContexts(boolean op) {
Set<ImmutableContextSet> c = new HashSet<>();
c.add(ContextSet.empty());
c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.SERVER)));
return c.stream()
.map(set -> contextManager.formContexts(null, set))
.collect(Collectors.toSet());
}
}

View File

@ -108,7 +108,7 @@ public final class UserDelegate extends PermissionHolderDelegate implements User
@Override
public void setupDataCache() {
handle.preCalculateData(false);
handle.preCalculateData();
}
@Override

View File

@ -31,12 +31,10 @@ import lombok.RequiredArgsConstructor;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.ChatMetaType;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.caching.MetaContexts;
import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.contexts.ExtractedContexts;
@ -59,20 +57,22 @@ public class UserCache implements UserData {
private final User user;
private final LoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new PermissionCacheLoader());
private final LoadingCache<MetaContexts, MetaCache> meta = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new MetaCacheLoader());
@Override
public PermissionCache getPermissionData(@NonNull Contexts contexts) {
//noinspection ConstantConditions
return permission.get(contexts);
}
@Override
public MetaCache getMetaData(@NonNull MetaContexts contexts) {
//noinspection ConstantConditions
return meta.get(contexts);
}
@ -131,14 +131,14 @@ public class UserCache implements UserData {
@Override
public void recalculatePermissions() {
Set<Contexts> keys = ImmutableSet.copyOf(permission.asMap().keySet());
keys.forEach(permission::refresh);
Set<Contexts> keys = permission.asMap().keySet();
keys.forEach(this::recalculatePermissions);
}
@Override
public void recalculateMeta() {
Set<MetaContexts> keys = ImmutableSet.copyOf(meta.asMap().keySet());
keys.forEach(meta::refresh);
Set<MetaContexts> keys = meta.asMap().keySet();
keys.forEach(this::recalculateMeta);
}
@Override
@ -148,30 +148,27 @@ public class UserCache implements UserData {
@Override
public void preCalculate(@NonNull Contexts contexts) {
permission.get(contexts);
meta.get(makeFromMetaContextsConfig(contexts, user.getPlugin()));
// pre-calculate just by requesting the data from this cache.
// if the data isn't already loaded, it will be calculated.
getPermissionData(contexts);
getMetaData(contexts);
}
public void invalidateCache() {
public void invalidateCaches() {
permission.invalidateAll();
meta.invalidateAll();
}
@Override
public void invalidatePermissionCalculators() {
permission.asMap().values().forEach(PermissionData::invalidateCache);
permission.asMap().values().forEach(PermissionCache::invalidateCache);
}
public void cleanup() {
public void doCacheCleanup() {
permission.cleanUp();
meta.cleanUp();
}
public void clear() {
permission.invalidateAll();
meta.invalidateAll();
}
private final class PermissionCacheLoader implements CacheLoader<Contexts, PermissionCache> {
@Override
public PermissionCache load(Contexts contexts) {

View File

@ -99,7 +99,7 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
plugin.getScheduler().asyncLater(() -> {
User user = getIfLoaded(plugin.getUuidCache().getUUID(uuid));
if (user != null && !plugin.isPlayerOnline(uuid)) {
user.unregisterData();
user.getUserData().invalidateCaches();
unload(user);
plugin.getUuidCache().clearCache(uuid);
}

View File

@ -29,6 +29,7 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.api.delegates.UserDelegate;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.caching.UserCache;
@ -161,16 +162,14 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
* Sets up the UserData cache
* Blocking call.
*/
public synchronized void preCalculateData(boolean op) {
userData.preCalculate(getPlugin().getPreProcessContexts(op));
getPlugin().onUserRefresh(this);
}
public synchronized void preCalculateData() {
// first try to refresh any existing permissions
refreshPermissions();
/**
* Removes the UserData cache from this user
*/
public void unregisterData() {
userData.clear();
// pre-calc the allowall & global contexts
// since contexts change so frequently, it's not worth trying to calculate any more than this.
userData.preCalculate(Contexts.allowAll());
userData.preCalculate(Contexts.global());
}
/**
@ -205,10 +204,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
}
}
public void cleanup() {
userData.cleanup();
}
private static final class UserRefreshBuffer extends BufferedRequest<Void> {
private final User user;

View File

@ -55,8 +55,8 @@ import me.lucko.luckperms.common.verbose.VerboseHandler;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@ -369,14 +369,6 @@ public interface LuckPermsPlugin {
*/
Set<UUID> getUniqueConnections();
/**
* Gets a set of Contexts that should be pre-processed in advance
*
* @param op if the user being processed is op
* @return a set of contexts
*/
Set<Contexts> getPreProcessContexts(boolean op);
default List<Command> getExtraCommands() {
return Collections.emptyList();
}
@ -386,7 +378,7 @@ public interface LuckPermsPlugin {
*
* @return a map of options, or null
*/
default LinkedHashMap<String, Object> getExtraInfo() {
default Map<String, Object> getExtraInfo() {
return null;
}

View File

@ -37,7 +37,7 @@ public class CacheHousekeepingTask implements Runnable {
@Override
public void run() {
for (User user : plugin.getUserManager().getAll().values()) {
user.cleanup();
user.getUserData().doCacheCleanup();
}
}
}

View File

@ -90,7 +90,8 @@ public class LoginHelper {
plugin.getStorage().noBuffer().saveUser(user).join();
}
user.preCalculateData(false); // Pretty nasty calculation call. Sets up the caching system so data is ready when the user joins.
// Does some minimum pre-calculations to (maybe) speed things up later.
user.preCalculateData();
}
final long time = System.currentTimeMillis() - startTime;

View File

@ -114,6 +114,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@ -295,6 +296,9 @@ public class LPSpongePlugin implements LuckPermsPlugin {
@Listener
public void onDisable(GameStoppingServerEvent event) {
permissionVault.setShutdown(true);
verboseHandler.setShutdown(true);
getLog().info("Closing storage...");
storage.shutdown();
@ -311,6 +315,8 @@ public class LPSpongePlugin implements LuckPermsPlugin {
getLog().info("Shutting down internal scheduler...");
scheduler.shutdown();
getLog().info("Goodbye!");
}
@Listener
@ -470,19 +476,14 @@ public class LPSpongePlugin implements LuckPermsPlugin {
return getSenderFactory().wrap(game.getServer().getConsole());
}
@Override
public Set<Contexts> getPreProcessContexts(boolean op) {
return Collections.emptySet();
}
@Override
public List<Command> getExtraCommands() {
return Collections.singletonList(new SpongeMainCommand(this));
}
@Override
public LinkedHashMap<String, Object> getExtraInfo() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
public Map<String, Object> getExtraInfo() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("SubjectCollection count", service.getLoadedCollections().size());
map.put("Subject count",
service.getLoadedCollections().values().stream()

View File

@ -27,8 +27,6 @@ package me.lucko.luckperms.sponge;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User;
@ -36,7 +34,6 @@ import me.lucko.luckperms.common.utils.LoginHelper;
import me.lucko.luckperms.common.utils.UuidCache;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.command.SendCommandEvent;
@ -45,15 +42,11 @@ import org.spongepowered.api.event.network.ClientConnectionEvent;
import org.spongepowered.api.profile.GameProfile;
import org.spongepowered.api.text.serializer.TextSerializers;
import org.spongepowered.api.util.Tristate;
import org.spongepowered.api.world.World;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@RequiredArgsConstructor
public class SpongeListener {
@ -140,8 +133,8 @@ public class SpongeListener {
@IsCancelled(Tristate.UNDEFINED)
public void onClientLogin(ClientConnectionEvent.Login e) {
/* Called when the player starts logging into the server.
At this point, the users data should be present and loaded.
Listening on LOW priority to allow plugins to further modify data here. (auth plugins, etc.) */
At this point, the users data should be present and loaded.
Listening on LOW priority to allow plugins to further modify data here. (auth plugins, etc.) */
final GameProfile player = e.getProfile();
@ -151,7 +144,7 @@ public class SpongeListener {
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
if (user == null) {
deniedLogin.add(player.getUniqueId());
@ -160,29 +153,6 @@ public class SpongeListener {
e.setMessageCancelled(false);
//noinspection deprecation
e.setMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(Message.LOADING_ERROR.asString(plugin.getLocaleManager())));
return;
}
// Attempt to pre-process some permissions for the user to save time later. Might not work, but it's better than nothing.
Optional<Player> p = e.getCause().first(Player.class);
if (p.isPresent()) {
MutableContextSet context = MutableContextSet.fromSet(plugin.getContextManager().getApplicableContext(p.get()));
List<String> worlds = plugin.getGame().isServerAvailable() ? plugin.getGame().getServer().getWorlds().stream()
.map(World::getName)
.collect(Collectors.toList()) : Collections.emptyList();
plugin.doAsync(() -> {
UserData data = user.getUserData();
data.preCalculate(plugin.getService().calculateContexts(context.makeImmutable()));
for (String world : worlds) {
MutableContextSet modified = MutableContextSet.fromSet(context);
modified.removeAll("world");
modified.add("world", world);
data.preCalculate(plugin.getService().calculateContexts(modified.makeImmutable()));
}
});
}
}

View File

@ -85,7 +85,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
user.getIoLock().unlock();
// ok, data is here, let's do the pre-calculation stuff.
user.preCalculateData(false);
user.preCalculateData();
return user.sponge();
}
@ -97,7 +97,7 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
throw new RuntimeException();
}
user.preCalculateData(false);
user.preCalculateData();
return user.sponge();
});

View File

@ -191,7 +191,7 @@ public class SpongeUser extends User {
@Override
public void invalidateCaches(CacheLevel cacheLevel) {
// invalidate for all changes
parent.getUserData().invalidateCache();
parent.getUserData().invalidateCaches();
}
}