forked from SteamWar/SteamWar
Restore BauweltMember cache and notify all dev servers on member changes
getBauMember never read its cache after the Exposed migration, so every scoreboard render hit the DB (Too many connections, #479). Cache by (bauwelt, member) incl. negative results, evict on addMember/remove, and send BaumemberUpdatePacket to the Bauserver and all dev servers (also on addmember). BauMemberUpdate clears the cache itself to not depend on handler order.
This commit is contained in:
@@ -28,6 +28,7 @@ import de.steamwar.bausystem.utils.bossbar.BossBarService;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.network.packets.PacketHandler;
|
||||
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
|
||||
import de.steamwar.sql.BauweltMember;
|
||||
import io.papermc.paper.event.player.PlayerPickBlockEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
@@ -57,6 +58,8 @@ public class BauMemberUpdate extends PacketHandler implements Listener {
|
||||
}
|
||||
|
||||
public static void baumemberUpdate() {
|
||||
// Packet handlers run in no guaranteed order, so don't rely on CoreNetworkHandler having cleared the cache yet.
|
||||
BauweltMember.clear();
|
||||
Set<Player> newSpectator = new HashSet<>();
|
||||
Set<Player> newBuilder = new HashSet<>();
|
||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.exposed.v1.dao.CompositeEntity
|
||||
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
|
||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
||||
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
|
||||
@@ -48,10 +49,12 @@ object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
||||
|
||||
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
|
||||
private val cache = mutableMapOf<Int, BauweltMember>()
|
||||
// Keyed by (bauweltId, memberId). Optional.empty() caches "is not a member", which is the
|
||||
// common case for spectators and would otherwise hit the DB on every permission check.
|
||||
private val cache = ConcurrentHashMap<Pair<Int, Int>, Optional<BauweltMember>>()
|
||||
|
||||
private fun cache(member: BauweltMember) =
|
||||
cache.put(member.memberID, member)
|
||||
cache.put(member.bauweltID to member.memberID, Optional.of(member))
|
||||
|
||||
@JvmStatic
|
||||
fun clear() =
|
||||
@@ -75,20 +78,20 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||
it[worldEdit] = false
|
||||
it[world] = false
|
||||
}
|
||||
}
|
||||
}.also { cache.remove(ownerId.value to newMemberId.value) }
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
|
||||
fun getBauMember(bauwelt: UUID, member: UUID) =
|
||||
useDb {
|
||||
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) }
|
||||
}
|
||||
getBauMember(SteamwarUser.get(bauwelt)!!.id.value, SteamwarUser.get(member)!!.id.value)
|
||||
|
||||
@JvmStatic
|
||||
fun getBauMember(bauwelt: Int, member: Int) =
|
||||
useDb {
|
||||
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
|
||||
}
|
||||
fun getBauMember(bauwelt: Int, member: Int): BauweltMember? =
|
||||
cache.computeIfAbsent(bauwelt to member) {
|
||||
Optional.ofNullable(useDb {
|
||||
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()
|
||||
})
|
||||
}.orElse(null)
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use getMembers(bauwelt: Int)")
|
||||
@@ -140,8 +143,11 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||
fun isWorld() =
|
||||
world
|
||||
|
||||
fun remove() =
|
||||
fun remove() {
|
||||
val key = bauweltID to memberID
|
||||
useDb {
|
||||
delete()
|
||||
}
|
||||
cache.remove(key)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
package de.steamwar.velocitycore.commands;
|
||||
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import de.steamwar.command.PreviousArguments;
|
||||
import de.steamwar.command.SWCommand;
|
||||
import de.steamwar.command.TypeMapper;
|
||||
@@ -45,7 +46,9 @@ import de.steamwar.velocitycore.network.NetworkSender;
|
||||
import de.steamwar.velocitycore.util.BauLock;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Linked
|
||||
@@ -72,6 +75,7 @@ public class BauCommand extends SWCommand {
|
||||
@Register(value = "addmember", description = "BAU_ADDMEMBER_USAGE")
|
||||
public void addmember(Chatter sender, @Validator("addMemberTarget") SteamwarUser target) {
|
||||
BauweltMember.addMember(sender.user().getUUID(), target.getUUID());
|
||||
sendBaumemberUpdate(sender.user().getUUID());
|
||||
sender.system("BAU_ADDMEMBER_ADDED");
|
||||
Chatter.of(target.getUUID()).system("BAU_ADDMEMBER_ADDED_TARGET", sender);
|
||||
}
|
||||
@@ -161,10 +165,7 @@ public class BauCommand extends SWCommand {
|
||||
withMember(owner, user, target -> {
|
||||
setter.accept(target);
|
||||
|
||||
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
|
||||
if (bauserver != null) {
|
||||
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
|
||||
}
|
||||
sendBaumemberUpdate(owner.user().getUUID());
|
||||
|
||||
Chatter.of(user.getUUID()).system("BAU_MEMBER_SET_TARGET", owner, new Message(permName));
|
||||
owner.system("BAU_MEMBER_SET", new Message(permName));
|
||||
@@ -184,9 +185,7 @@ public class BauCommand extends SWCommand {
|
||||
}
|
||||
});
|
||||
|
||||
if (bauserver != null) {
|
||||
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
|
||||
}
|
||||
sendBaumemberUpdate(owner.user().getUUID());
|
||||
|
||||
try {
|
||||
deletePlayerData(owner, user);
|
||||
@@ -289,6 +288,23 @@ public class BauCommand extends SWCommand {
|
||||
BauLock.setLocked(sender, BauLockState.OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the owner's Bauserver and every dev server to drop their cached {@link BauweltMember}s,
|
||||
* since those run in separate processes and would otherwise keep serving stale permissions.
|
||||
* Plugin messages need a connected player, so servers without players are skipped.
|
||||
*/
|
||||
private static void sendBaumemberUpdate(UUID owner) {
|
||||
Bauserver bauserver = Bauserver.get(owner);
|
||||
if (bauserver != null) {
|
||||
sendBaumemberUpdate(bauserver.getRegisteredServer());
|
||||
}
|
||||
new ArrayList<>(DevCommand.getDevServers().values()).forEach(BauCommand::sendBaumemberUpdate);
|
||||
}
|
||||
|
||||
private static void sendBaumemberUpdate(RegisteredServer server) {
|
||||
server.getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
|
||||
}
|
||||
|
||||
private static void withMember(Chatter owner, SteamwarUser member, Consumer<BauweltMember> function) {
|
||||
if (member == null) {
|
||||
owner.system("UNKNOWN_PLAYER");
|
||||
|
||||
Reference in New Issue
Block a user