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:
2026-10-19 10:24:41 +02:00
parent 27d260a8d2
commit 0c3e63d521
3 changed files with 43 additions and 18 deletions
@@ -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)
}
}