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.linkage.Linked;
|
||||||
import de.steamwar.network.packets.PacketHandler;
|
import de.steamwar.network.packets.PacketHandler;
|
||||||
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
|
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
|
||||||
|
import de.steamwar.sql.BauweltMember;
|
||||||
import io.papermc.paper.event.player.PlayerPickBlockEvent;
|
import io.papermc.paper.event.player.PlayerPickBlockEvent;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.boss.BarColor;
|
import org.bukkit.boss.BarColor;
|
||||||
@@ -57,6 +58,8 @@ public class BauMemberUpdate extends PacketHandler implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void baumemberUpdate() {
|
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> newSpectator = new HashSet<>();
|
||||||
Set<Player> newBuilder = new HashSet<>();
|
Set<Player> newBuilder = new HashSet<>();
|
||||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
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.dao.CompositeEntityClass
|
||||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
||||||
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
|
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
|
||||||
@@ -48,10 +49,12 @@ object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
|||||||
|
|
||||||
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||||
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
|
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) =
|
private fun cache(member: BauweltMember) =
|
||||||
cache.put(member.memberID, member)
|
cache.put(member.bauweltID to member.memberID, Optional.of(member))
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun clear() =
|
fun clear() =
|
||||||
@@ -75,20 +78,20 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
|||||||
it[worldEdit] = false
|
it[worldEdit] = false
|
||||||
it[world] = false
|
it[world] = false
|
||||||
}
|
}
|
||||||
}
|
}.also { cache.remove(ownerId.value to newMemberId.value) }
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
|
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
|
||||||
fun getBauMember(bauwelt: UUID, member: UUID) =
|
fun getBauMember(bauwelt: UUID, member: UUID) =
|
||||||
useDb {
|
getBauMember(SteamwarUser.get(bauwelt)!!.id.value, SteamwarUser.get(member)!!.id.value)
|
||||||
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getBauMember(bauwelt: Int, member: Int) =
|
fun getBauMember(bauwelt: Int, member: Int): BauweltMember? =
|
||||||
useDb {
|
cache.computeIfAbsent(bauwelt to member) {
|
||||||
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
|
Optional.ofNullable(useDb {
|
||||||
}
|
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()
|
||||||
|
})
|
||||||
|
}.orElse(null)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated("Use getMembers(bauwelt: Int)")
|
@Deprecated("Use getMembers(bauwelt: Int)")
|
||||||
@@ -140,8 +143,11 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
|||||||
fun isWorld() =
|
fun isWorld() =
|
||||||
world
|
world
|
||||||
|
|
||||||
fun remove() =
|
fun remove() {
|
||||||
|
val key = bauweltID to memberID
|
||||||
useDb {
|
useDb {
|
||||||
delete()
|
delete()
|
||||||
}
|
}
|
||||||
|
cache.remove(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package de.steamwar.velocitycore.commands;
|
package de.steamwar.velocitycore.commands;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import de.steamwar.command.PreviousArguments;
|
import de.steamwar.command.PreviousArguments;
|
||||||
import de.steamwar.command.SWCommand;
|
import de.steamwar.command.SWCommand;
|
||||||
import de.steamwar.command.TypeMapper;
|
import de.steamwar.command.TypeMapper;
|
||||||
@@ -45,7 +46,9 @@ import de.steamwar.velocitycore.network.NetworkSender;
|
|||||||
import de.steamwar.velocitycore.util.BauLock;
|
import de.steamwar.velocitycore.util.BauLock;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Linked
|
@Linked
|
||||||
@@ -72,6 +75,7 @@ public class BauCommand extends SWCommand {
|
|||||||
@Register(value = "addmember", description = "BAU_ADDMEMBER_USAGE")
|
@Register(value = "addmember", description = "BAU_ADDMEMBER_USAGE")
|
||||||
public void addmember(Chatter sender, @Validator("addMemberTarget") SteamwarUser target) {
|
public void addmember(Chatter sender, @Validator("addMemberTarget") SteamwarUser target) {
|
||||||
BauweltMember.addMember(sender.user().getUUID(), target.getUUID());
|
BauweltMember.addMember(sender.user().getUUID(), target.getUUID());
|
||||||
|
sendBaumemberUpdate(sender.user().getUUID());
|
||||||
sender.system("BAU_ADDMEMBER_ADDED");
|
sender.system("BAU_ADDMEMBER_ADDED");
|
||||||
Chatter.of(target.getUUID()).system("BAU_ADDMEMBER_ADDED_TARGET", sender);
|
Chatter.of(target.getUUID()).system("BAU_ADDMEMBER_ADDED_TARGET", sender);
|
||||||
}
|
}
|
||||||
@@ -161,10 +165,7 @@ public class BauCommand extends SWCommand {
|
|||||||
withMember(owner, user, target -> {
|
withMember(owner, user, target -> {
|
||||||
setter.accept(target);
|
setter.accept(target);
|
||||||
|
|
||||||
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
|
sendBaumemberUpdate(owner.user().getUUID());
|
||||||
if (bauserver != null) {
|
|
||||||
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Chatter.of(user.getUUID()).system("BAU_MEMBER_SET_TARGET", owner, new Message(permName));
|
Chatter.of(user.getUUID()).system("BAU_MEMBER_SET_TARGET", owner, new Message(permName));
|
||||||
owner.system("BAU_MEMBER_SET", new Message(permName));
|
owner.system("BAU_MEMBER_SET", new Message(permName));
|
||||||
@@ -184,9 +185,7 @@ public class BauCommand extends SWCommand {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bauserver != null) {
|
sendBaumemberUpdate(owner.user().getUUID());
|
||||||
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
deletePlayerData(owner, user);
|
deletePlayerData(owner, user);
|
||||||
@@ -289,6 +288,23 @@ public class BauCommand extends SWCommand {
|
|||||||
BauLock.setLocked(sender, BauLockState.OPEN);
|
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) {
|
private static void withMember(Chatter owner, SteamwarUser member, Consumer<BauweltMember> function) {
|
||||||
if (member == null) {
|
if (member == null) {
|
||||||
owner.system("UNKNOWN_PLAYER");
|
owner.system("UNKNOWN_PLAYER");
|
||||||
|
|||||||
Reference in New Issue
Block a user