Refactor SQL storage implementation

This commit is contained in:
Luck 2020-01-25 16:02:37 +00:00
parent 8fa629a243
commit a779b31ca5
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
14 changed files with 533 additions and 583 deletions

View File

@ -65,6 +65,7 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* A map of nodes held by a {@link PermissionHolder}.
@ -272,7 +273,7 @@ public final class NodeMap {
this.inheritanceMap.remove(context);
}
void setContent(Collection<? extends Node> set) {
void setContent(Iterable<? extends Node> set) {
this.map.clear();
this.inheritanceMap.clear();
for (Node n : set) {
@ -280,6 +281,12 @@ public final class NodeMap {
}
}
void setContent(Stream<? extends Node> stream) {
this.map.clear();
this.inheritanceMap.clear();
stream.forEach(this::add);
}
void setContent(Multimap<ImmutableContextSet, ? extends Node> multimap) {
setContent(multimap.values());
}

View File

@ -71,6 +71,7 @@ import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Represents an object that can hold permissions, (a user or group)
@ -233,11 +234,16 @@ public abstract class PermissionHolder {
getPlugin().getEventDispatcher().dispatchDataRecalculate(this);
}
public void setNodes(DataType type, Collection<? extends Node> set) {
public void setNodes(DataType type, Iterable<? extends Node> set) {
getData(type).setContent(set);
invalidateCache();
}
public void setNodes(DataType type, Stream<? extends Node> stream) {
getData(type).setContent(stream);
invalidateCache();
}
public void replaceNodes(DataType type, Multimap<ImmutableContextSet, ? extends Node> multimap) {
getData(type).setContent(multimap);
invalidateCache();

View File

@ -46,6 +46,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
@ -96,6 +97,7 @@ public final class Track {
}
public void setGroups(List<String> groups) {
Objects.requireNonNull(groups, "groups");
this.groups.clear();
this.groups.addAll(groups);
}

View File

@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.common.cache.LoadingMap;
import java.util.Collection;
import java.util.Map;
/**
@ -69,6 +70,13 @@ public abstract class AbstractManager<I, C, T extends C> implements Manager<I, C
}
}
@Override
public void retainAll(Collection<I> ids) {
this.objects.keySet().stream()
.filter(g -> !ids.contains(g))
.forEach(this::unload);
}
protected I sanitizeIdentifier(I i) {
return i;
}

View File

@ -25,6 +25,7 @@
package me.lucko.luckperms.common.model.manager;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
@ -77,4 +78,12 @@ public interface Manager<I, C, T extends C> extends Function<I, T> {
*/
void unload(I id);
/**
* Calls {@link #unload(Object)} for all objects currently
* loaded not in the given collection of ids.
*
* @param ids the ids to retain
*/
void retainAll(Collection<I> ids);
}

View File

@ -36,7 +36,7 @@ import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
import me.lucko.luckperms.common.storage.implementation.split.SplitStorage;
import me.lucko.luckperms.common.util.ThrowingRunnable;
import me.lucko.luckperms.common.util.Throwing;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.event.cause.CreationCause;
@ -92,7 +92,7 @@ public class Storage {
}, this.plugin.getBootstrap().getScheduler().async());
}
private CompletableFuture<Void> makeFuture(ThrowingRunnable runnable) {
private CompletableFuture<Void> makeFuture(Throwing.Runnable runnable) {
return CompletableFuture.runAsync(() -> {
try {
runnable.run();

View File

@ -27,13 +27,10 @@ package me.lucko.luckperms.common.storage.implementation.file;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.manager.group.GroupManager;
import me.lucko.luckperms.common.model.manager.track.TrackManager;
import me.lucko.luckperms.common.node.model.HeldNodeImpl;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.implementation.file.loader.ConfigurateLoader;
import me.lucko.luckperms.common.util.Iterators;
import net.luckperms.api.node.HeldNode;
import net.luckperms.api.node.Node;
@ -290,32 +287,17 @@ public class CombinedConfigurateStorage extends AbstractConfigurateStorage {
@Override
public void loadAllGroups() throws IOException {
List<String> groups = new ArrayList<>();
this.groupsLoader.apply(false, true, root -> {
groups.addAll(root.getChildrenMap().keySet().stream()
.map(Object::toString)
.collect(Collectors.toList()));
});
boolean success = true;
for (String g : groups) {
try {
loadGroup(g);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
if (!success) {
if (!Iterators.tryIterate(groups, this::loadGroup)) {
throw new RuntimeException("Exception occurred whilst loading a group");
}
GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream()
.map(Group::getName)
.filter(g -> !groups.contains(g))
.forEach(gm::unload);
this.plugin.getGroupManager().retainAll(groups);
}
@Override
@ -345,32 +327,17 @@ public class CombinedConfigurateStorage extends AbstractConfigurateStorage {
@Override
public void loadAllTracks() throws IOException {
List<String> tracks = new ArrayList<>();
this.tracksLoader.apply(false, true, root -> {
tracks.addAll(root.getChildrenMap().keySet().stream()
.map(Object::toString)
.collect(Collectors.toList()));
});
boolean success = true;
for (String t : tracks) {
try {
loadTrack(t);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
if (!success) {
if (!Iterators.tryIterate(tracks, this::loadTrack)) {
throw new RuntimeException("Exception occurred whilst loading a track");
}
TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream()
.map(Track::getName)
.filter(t -> !tracks.contains(t))
.forEach(tm::unload);
this.plugin.getTrackManager().retainAll(tracks);
}
}

View File

@ -27,14 +27,11 @@ package me.lucko.luckperms.common.storage.implementation.file;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.model.manager.group.GroupManager;
import me.lucko.luckperms.common.model.manager.track.TrackManager;
import me.lucko.luckperms.common.node.model.HeldNodeImpl;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.implementation.file.loader.ConfigurateLoader;
import me.lucko.luckperms.common.util.Iterators;
import me.lucko.luckperms.common.util.MoreFiles;
import me.lucko.luckperms.common.util.Uuids;
@ -292,25 +289,11 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
.collect(Collectors.toList());
}
boolean success = true;
for (String g : groups) {
try {
loadGroup(g);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
if (!success) {
if (!Iterators.tryIterate(groups, this::loadGroup)) {
throw new RuntimeException("Exception occurred whilst loading a group");
}
GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream()
.map(Group::getName)
.filter(g -> !groups.contains(g))
.forEach(gm::unload);
this.plugin.getGroupManager().retainAll(groups);
}
@Override
@ -349,25 +332,11 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
.collect(Collectors.toList());
}
boolean success = true;
for (String t : tracks) {
try {
loadTrack(t);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
if (!success) {
if (!Iterators.tryIterate(tracks, this::loadTrack)) {
throw new RuntimeException("Exception occurred whilst loading a track");
}
TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream()
.map(Track::getName)
.filter(t -> !tracks.contains(t))
.forEach(tm::unload);
this.plugin.getTrackManager().retainAll(tracks);
}
}

View File

@ -46,13 +46,13 @@ import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.model.manager.group.GroupManager;
import me.lucko.luckperms.common.model.manager.track.TrackManager;
import me.lucko.luckperms.common.node.factory.NodeBuilders;
import me.lucko.luckperms.common.node.model.HeldNodeImpl;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
import me.lucko.luckperms.common.storage.misc.PlayerSaveResultImpl;
import me.lucko.luckperms.common.storage.misc.StorageCredentials;
import me.lucko.luckperms.common.util.Iterators;
import net.luckperms.api.actionlog.Action;
import net.luckperms.api.context.Context;
@ -442,25 +442,11 @@ public class MongoStorage implements StorageImplementation {
}
}
boolean success = true;
for (String g : groups) {
try {
loadGroup(g);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
if (!success) {
if (!Iterators.tryIterate(groups, this::loadGroup)) {
throw new RuntimeException("Exception occurred whilst loading a group");
}
GroupManager<?> gm = this.plugin.getGroupManager();
gm.getAll().values().stream()
.map(Group::getName)
.filter(g -> !groups.contains(g))
.forEach(gm::unload);
this.plugin.getGroupManager().retainAll(groups);
}
@Override
@ -569,25 +555,11 @@ public class MongoStorage implements StorageImplementation {
}
}
boolean success = true;
for (String t : tracks) {
try {
loadTrack(t);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
if (!success) {
if (!Iterators.tryIterate(tracks, this::loadTrack)) {
throw new RuntimeException("Exception occurred whilst loading a track");
}
TrackManager<?> tm = this.plugin.getTrackManager();
tm.getAll().values().stream()
.map(Track::getName)
.filter(t -> !tracks.contains(t))
.forEach(tm::unload);
this.plugin.getTrackManager().retainAll(tracks);
}
@Override

View File

@ -0,0 +1,69 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.implementation.sql;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
public final class SchemaReader {
private SchemaReader() {}
public static List<String> getStatements(InputStream is) throws IOException {
List<String> queries = new LinkedList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("--") || line.startsWith("#")) {
continue;
}
sb.append(line);
// check for end of declaration
if (line.endsWith(";")) {
sb.deleteCharAt(sb.length() - 1);
String result = sb.toString().trim();
if (!result.isEmpty()) {
queries.add(result);
}
// reset
sb = new StringBuilder();
}
}
}
return queries;
}
}

View File

@ -47,6 +47,8 @@ import java.util.Set;
*/
public final class SqlNode {
public static final int NULL_ID = -1;
public static SqlNode fromNode(Node node) {
ContextSet contexts = node.getContexts();
@ -78,11 +80,7 @@ public final class SqlNode {
long expiry = node.hasExpiry() ? node.getExpiry().getEpochSecond() : 0L;
return new SqlNode(node.getKey(), node.getValue(), server, world, expiry, contexts.immutableCopy(), -1);
}
public static SqlNode fromSqlFields(String permission, boolean value, String server, String world, long expiry, String contexts) {
return new SqlNode(permission, value, server, world, expiry, ContextSetJsonSerializer.deserializeContextSet(GsonProvider.normal(), contexts).immutableCopy(), -1);
return new SqlNode(node.getKey(), node.getValue(), server, world, expiry, contexts.immutableCopy(), NULL_ID);
}
public static SqlNode fromSqlFields(long sqlId, String permission, boolean value, String server, String world, long expiry, String contexts) {
@ -142,7 +140,7 @@ public final class SqlNode {
}
public long getSqlId() {
if (this.sqlId == -1) {
if (this.sqlId == NULL_ID) {
throw new IllegalStateException("sql id not set");
}
return this.sqlId;

View File

@ -34,44 +34,56 @@ import java.util.function.Function;
public final class Iterators {
private Iterators() {}
public static <E> void tryIterate(Iterable<E> iterable, Consumer<E> action) {
public static <E> boolean tryIterate(Iterable<E> iterable, Throwing.Consumer<E> action) {
boolean success = true;
for (E element : iterable) {
try {
action.accept(element);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
return success;
}
public static <I, O> void tryIterate(Iterable<I> iterable, Function<I, O> mapping, Consumer<O> action) {
public static <I, O> boolean tryIterate(Iterable<I> iterable, Function<I, O> mapping, Consumer<O> action) {
boolean success = true;
for (I element : iterable) {
try {
action.accept(mapping.apply(element));
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
return success;
}
public static <E> void tryIterate(E[] array, Consumer<E> action) {
public static <E> boolean tryIterate(E[] array, Consumer<E> action) {
boolean success = true;
for (E element : array) {
try {
action.accept(element);
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
return success;
}
public static <I, O> void tryIterate(I[] array, Function<I, O> mapping, Consumer<O> action) {
public static <I, O> boolean tryIterate(I[] array, Function<I, O> mapping, Consumer<O> action) {
boolean success = true;
for (I element : array) {
try {
action.accept(mapping.apply(element));
} catch (Exception e) {
e.printStackTrace();
success = false;
}
}
return success;
}
public static <E> List<List<E>> divideIterable(Iterable<E> source, int size) {

View File

@ -25,8 +25,15 @@
package me.lucko.luckperms.common.util;
public interface ThrowingRunnable {
public interface Throwing {
void run() throws Exception;
@FunctionalInterface
interface Runnable {
void run() throws Exception;
}
@FunctionalInterface
interface Consumer<T> {
void accept(T t) throws Exception;
}
}