Added ability to manually categorize keys to improve performance

This commit is contained in:
Auxilor
2022-02-17 12:21:26 +00:00
parent 00da717f6d
commit c88dac56b7
10 changed files with 118 additions and 29 deletions

View File

@@ -1,6 +1,8 @@
package com.willfp.eco.core.data.keys;
import com.willfp.eco.core.Eco;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -9,6 +11,8 @@ import java.util.Set;
/**
* API to register persistent data keys.
*/
@ApiStatus.Internal
@Eco.HandlerComponent
public interface KeyRegistry {
/**
* Register a persistent data key to be stored.
@@ -24,6 +28,15 @@ public interface KeyRegistry {
*/
Set<PersistentDataKey<?>> getRegisteredKeys();
/**
* Mark key as category.
*
* @param key The key.
* @param category The category.
*/
void markKeyAs(@NotNull PersistentDataKey<?> key,
@NotNull KeyRegistry.KeyCategory category);
/**
* Get persistent data key from namespaced key.
*
@@ -32,4 +45,19 @@ public interface KeyRegistry {
*/
@Nullable
PersistentDataKey<?> getKeyFrom(@NotNull NamespacedKey namespacedKey);
/**
* Locations for key categorization.
*/
enum KeyCategory {
/**
* Player keys.
*/
PLAYER,
/**
* Server keys.
*/
SERVER
}
}

View File

@@ -1,7 +1,6 @@
package com.willfp.eco.core.data.keys;
import com.willfp.eco.core.Eco;
import com.willfp.eco.util.NamespacedKeyUtils;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -14,7 +13,7 @@ import java.util.Set;
*
* @param <T> The type of the data.
*/
public class PersistentDataKey<T> {
public final class PersistentDataKey<T> {
/**
* The key of the persistent data value.
*/
@@ -83,6 +82,32 @@ public class PersistentDataKey<T> {
return this.type;
}
/**
* Categorize key as a server key, will register new column to MySQL
* database immediately rather than waiting for auto-categorization.
* <p>
* This will improve performance.
*
* @return The key.
*/
public PersistentDataKey<T> server() {
Eco.getHandler().getKeyRegistry().markKeyAs(this, KeyRegistry.KeyCategory.SERVER);
return this;
}
/**
* Categorize key as a player key, will register new column to MySQL
* database immediately rather than waiting for auto-categorization.
* <p>
* This will improve performance.
*
* @return The key.
*/
public PersistentDataKey<T> player() {
Eco.getHandler().getKeyRegistry().markKeyAs(this, KeyRegistry.KeyCategory.PLAYER);
return this;
}
/**
* Get all persistent data keys.
*

View File

@@ -187,10 +187,11 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
(Eco.getHandler() as EcoHandler).setAdventure(BukkitAudiences.create(this))
}
this.logger.info("Ignore messages about deprecated events!")
// Init FIS
this.getProxy(FastItemStackFactoryProxy::class.java).create(ItemStack(Material.AIR)).unwrap()
// Preload categorized persistent data keys
(Eco.getHandler().profileHandler as EcoProfileHandler).initialize()
}
override fun handleDisable() {
@@ -216,7 +217,6 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
CustomItemsManager.registerAllItems()
CustomEntitiesManager.registerAllEntities()
ShopManager.registerEcoProvider()
(Eco.getHandler().profileHandler as EcoProfileHandler).runPostInit()
}
override fun loadIntegrationLoaders(): List<IntegrationLoader> {

View File

@@ -1,12 +1,14 @@
package com.willfp.eco.internal.spigot.data
import com.willfp.eco.core.Eco
import com.willfp.eco.core.data.keys.KeyRegistry
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import org.bukkit.NamespacedKey
class EcoKeyRegistry: KeyRegistry {
class EcoKeyRegistry : KeyRegistry {
private val registry = mutableMapOf<NamespacedKey, PersistentDataKey<*>>()
private val categories = mutableMapOf<NamespacedKey, KeyRegistry.KeyCategory>()
override fun registerKey(key: PersistentDataKey<*>) {
if (this.registry.containsKey(key.key)) {
@@ -41,6 +43,11 @@ class EcoKeyRegistry: KeyRegistry {
}
}
override fun markKeyAs(key: PersistentDataKey<*>, category: KeyRegistry.KeyCategory) {
categories[key.key] = category
(Eco.getHandler().profileHandler as EcoProfileHandler).handler.categorize(key, category) // ew
}
override fun getKeyFrom(namespacedKey: NamespacedKey): PersistentDataKey<*>? {
return registry[namespacedKey]
}

View File

@@ -65,4 +65,4 @@ class EcoServerProfile(
override fun toString(): String {
return "EcoServerProfile"
}
}
}

View File

@@ -18,7 +18,7 @@ class EcoProfileHandler(
plugin: EcoSpigotPlugin
) : ProfileHandler {
private val loaded = mutableMapOf<UUID, Profile>()
private val handler: DataHandler = if (useSql) MySQLDataHandler(plugin, this) else
val handler: DataHandler = if (useSql) MySQLDataHandler(plugin, this) else
YamlDataHandler(plugin, this)
fun loadGenericProfile(uuid: UUID): Profile {
@@ -64,7 +64,7 @@ class EcoProfileHandler(
handler.save()
}
fun runPostInit() {
handler.runPostInit()
fun initialize() {
handler.initialize()
}
}

View File

@@ -15,23 +15,23 @@ object KeyHelpers {
val key = NamespacedKeyUtils.fromStringOrNull(split[0]) ?: return null
val type = PersistentDataKeyType.valueOf(split[1]) ?: return null
return when (type.name()) {
"STRING" -> PersistentDataKey(
return when (type) {
PersistentDataKeyType.STRING -> PersistentDataKey(
key,
type as PersistentDataKeyType<String>,
if(split.size >= 3) split.toList().subList(2, split.size).joinToString("") else ""
if (split.size >= 3) split.toList().subList(2, split.size).joinToString("") else ""
)
"INT" -> PersistentDataKey(
PersistentDataKeyType.INT -> PersistentDataKey(
key,
type as PersistentDataKeyType<Int>,
split[2].toInt()
)
"DOUBLE" -> PersistentDataKey(
PersistentDataKeyType.DOUBLE -> PersistentDataKey(
key,
type as PersistentDataKeyType<Double>,
split[2].toDouble()
)
"BOOLEAN" -> PersistentDataKey(
PersistentDataKeyType.BOOLEAN -> PersistentDataKey(
key,
type as PersistentDataKeyType<Boolean>,
java.lang.Boolean.parseBoolean(split[2])
@@ -43,4 +43,4 @@ object KeyHelpers {
fun serializeToString(key: PersistentDataKey<*>): String {
return "${key.key};${key.type.name()};${key.defaultValue}"
}
}
}

View File

@@ -1,5 +1,6 @@
package com.willfp.eco.internal.spigot.data.storage
import com.willfp.eco.core.data.keys.KeyRegistry
import com.willfp.eco.core.data.keys.PersistentDataKey
import org.bukkit.NamespacedKey
import java.util.UUID
@@ -8,7 +9,11 @@ interface DataHandler {
fun save()
fun saveAll(uuids: Iterable<UUID>)
fun runPostInit() {
fun categorize(key: PersistentDataKey<*>, category: KeyRegistry.KeyCategory) {
}
fun initialize() {
}

View File

@@ -3,6 +3,7 @@ package com.willfp.eco.internal.spigot.data.storage
import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.willfp.eco.core.Eco
import com.willfp.eco.core.EcoPlugin
import com.willfp.eco.core.data.keys.KeyRegistry
import com.willfp.eco.core.data.keys.PersistentDataKey
import com.willfp.eco.core.data.keys.PersistentDataKeyType
import com.willfp.eco.internal.spigot.EcoSpigotPlugin
@@ -31,6 +32,7 @@ import java.util.UUID
import java.util.concurrent.Callable
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.Future
@Suppress("UNCHECKED_CAST")
class MySQLDataHandler(
@@ -42,7 +44,8 @@ class MySQLDataHandler(
UUIDTable("eco_players"),
plugin,
plugin.dataYml.getStrings("categorized-keys.player")
.mapNotNull { KeyHelpers.deserializeFromString(it) }
.mapNotNull { KeyHelpers.deserializeFromString(it) },
KeyRegistry.KeyCategory.PLAYER
)
private val serverHandler = ImplementedMySQLHandler(
@@ -50,7 +53,8 @@ class MySQLDataHandler(
UUIDTable("eco_server"),
plugin,
plugin.dataYml.getStrings("categorized-keys.server")
.mapNotNull { KeyHelpers.deserializeFromString(it) }
.mapNotNull { KeyHelpers.deserializeFromString(it) },
KeyRegistry.KeyCategory.SERVER
)
override fun saveAll(uuids: Iterable<UUID>) {
@@ -84,6 +88,14 @@ class MySQLDataHandler(
}
}
override fun categorize(key: PersistentDataKey<*>, category: KeyRegistry.KeyCategory) {
if (category == KeyRegistry.KeyCategory.SERVER) {
serverHandler.ensureKeyRegistration(key.key)
} else {
playerHandler.ensureKeyRegistration(key.key)
}
}
override fun save() {
plugin.dataYml.set(
"categorized-keys.player",
@@ -98,9 +110,9 @@ class MySQLDataHandler(
plugin.dataYml.save()
}
override fun runPostInit() {
playerHandler.runPostInit()
serverHandler.runPostInit()
override fun initialize() {
playerHandler.initialize()
serverHandler.initialize()
}
}
@@ -109,12 +121,14 @@ private class ImplementedMySQLHandler(
private val handler: EcoProfileHandler,
private val table: UUIDTable,
private val plugin: EcoPlugin,
private val knownKeys: Collection<PersistentDataKey<*>>
private val knownKeys: Collection<PersistentDataKey<*>>,
private val category: KeyRegistry.KeyCategory
) {
private val columns = mutableMapOf<String, Column<*>>()
private val threadFactory = ThreadFactoryBuilder().setNameFormat("eco-mysql-thread-%d").build()
private val executor = Executors.newFixedThreadPool(plugin.configYml.getInt("mysql.threads"), threadFactory)
val registeredKeys = ConcurrentHashMap<NamespacedKey, PersistentDataKey<*>>()
private val currentlyProcessingRegistration = ConcurrentHashMap<NamespacedKey, Future<*>>()
init {
val config = HikariConfig()
@@ -145,7 +159,7 @@ private class ImplementedMySQLHandler(
}
}
fun runPostInit() {
fun initialize() {
transaction {
for (key in knownKeys) {
registerColumn(key, table)
@@ -170,12 +184,21 @@ private class ImplementedMySQLHandler(
return
}
transaction {
registerColumn(persistentKey, table)
SchemaUtils.createMissingTablesAndColumns(table, withLogs = false)
val future = currentlyProcessingRegistration[key]
if (future != null) {
future.get()
return
}
registeredKeys[key] = persistentKey
currentlyProcessingRegistration[key] = executor.submit {
transaction {
registerColumn(persistentKey, table)
SchemaUtils.createMissingTablesAndColumns(table, withLogs = false)
}
registeredKeys[key] = persistentKey
currentlyProcessingRegistration.remove(key)
}
}
fun <T> write(uuid: UUID, key: NamespacedKey, value: T) {

View File

@@ -2,6 +2,7 @@
categorized-keys:
# Preloading known keys (as of the release of 6.25.0) for optimal performance.
# This is only used when MySQL is enabled as the columns must be added each time a new key is registered.
player:
- ecoskills:crit_damage;INT;0
- ecoskills:strong_impact;INT;0