Skip to content

Optimisation des requêtes pour l'économie#1333

Open
gtolontop wants to merge 13 commits into
ServerOpenMC:masterfrom
gtolontop:fix/economy-balance-save
Open

Optimisation des requêtes pour l'économie#1333
gtolontop wants to merge 13 commits into
ServerOpenMC:masterfrom
gtolontop:fix/economy-balance-save

Conversation

@gtolontop

@gtolontop gtolontop commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Petit résumé de la PR:

Optimise la persistance des soldes économie en regroupant les écritures DB des balances modifiées.

Étape nécessaire afin que la PR soit fini (si PR en draft)

  • Suivre le Code de Conduite
  • Enlever tous les imports non utilisés
  • Bien documenter la feature
  • Fournir un profileur (non demandé pour cette PR)
  • Avoir une milestone associée à la PR (2.5.0 à assigner)
  • Valider tout les checks
  • Tester et valider la feature/changement

Decrivez vos changements

EconomyManager écrivait en DB à chaque changement de solde via playersDao.createOrUpdate, notamment depuis setBalance, addBalance et withdrawBalance.

Cette PR garde les soldes à jour en mémoire immédiatement, marque uniquement les comptes modifiés comme à sauvegarder, puis persiste ces comptes en batch avec saveAllBalances() :

  • à l'arrêt de la feature, via le cycle de sauvegarde existant ;
  • périodiquement via un autosave, pour limiter la perte possible en cas de crash.

Le cache utilise aussi computeIfAbsent pour conserver les nouveaux comptes en mémoire avant leur première sauvegarde. En cas d'erreur pendant la sauvegarde batch, les comptes concernés sont remis dans la liste à sauvegarder.

@gtolontop gtolontop changed the title [codex] Batch economy balance persistence Optimise economy balance persistence Jun 22, 2026
@gtolontop gtolontop force-pushed the fix/economy-balance-save branch from 6c8e2c3 to 70dde0d Compare June 22, 2026 16:30
@gtolontop gtolontop force-pushed the fix/economy-balance-save branch from 70dde0d to a2a7db9 Compare June 22, 2026 16:32
@AxenoDev

Copy link
Copy Markdown
Member

au lieu de sauvegarder tout les x temps, pourquoi tu save pas juste a la db au moment ou le plugin se desactive

@iambibi

iambibi commented Jun 22, 2026

Copy link
Copy Markdown
Member

C'était marqué dans l'issue

@gtolontop

Copy link
Copy Markdown
Contributor Author

au lieu de sauvegarder tout les x temps, pourquoi tu save pas juste a la db au moment ou le plugin se desactive

C’est déjà fait xd au save() de la feature, donc à la désactivation du plugin, saveAllBalances() flush les balances modifiées

L’autosave est volontaire en plus du shutdown save ça évite de perdre toutes les modifications depuis le démarrage si le serveur crash/kill ou si l’arrêt ne va pas jusqu’au bout. Et il ne sauvegarde pas tous les balances à chaque fois, seulement les UUID marqués dirty depuis la dernière sauvegarde :)

@AxenoDev

AxenoDev commented Jun 22, 2026

Copy link
Copy Markdown
Member

Le serveur n'est pas cense etre kill ou crash, meme il ne sera jamais kill...

@iambibi

iambibi commented Jun 22, 2026

Copy link
Copy Markdown
Member

Ben après il peut crash oui. Mais il est tjr éteint à 2h donc cv

@gtolontop

Copy link
Copy Markdown
Contributor Author

Même si le serveur n’est pas censé crash il peut crash mdrrrrr l’autosave limite la perte si ça arrive et dans tous les cas le coût en perf est faible on ne save pas tous les comptes seulement les dirty en batch

@iambibi

iambibi commented Jun 22, 2026

Copy link
Copy Markdown
Member

Tu appelles quoi un compte dirty?

@gtolontop

Copy link
Copy Markdown
Contributor Author

Dirty = modifié depuis le dernier save DB
On garde l’UUID dans dirtyBalances et au prochain save on persiste seulement ces comptes

@AxenoDev

Copy link
Copy Markdown
Member

Le probleme, est que la le lag a lieu lors de la premiere connexion des joeurs, c'est un moment que l'on avait fait avec 50bots de souvenir, et le lag venait du save a la db, donc la si je comprends bien, a un moment, le serveur va lag parcequ'il save toutes les donne dans la db a un moment x

@iambibi

iambibi commented Jun 22, 2026

Copy link
Copy Markdown
Member

Fin et déjà pour qu'on valide le problème, il faudra que tu donnes un jar de ton plugin, on fait un teste avec 50-100 joueurs, et on attends jusqu'au moment où le scheduler s'exécute pour save la db, et on tentera

@AxenoDev

Copy link
Copy Markdown
Member

Bon @gtolontop, si c'est pour faire du vibecoding sur le projet ce n'est pas la peine de venir faire une PR.

@Shoccapik

Copy link
Copy Markdown

Le satan qui se réveille avec le full vibecoding qui fait mal

@gtolontop

Copy link
Copy Markdown
Contributor Author

Bon @gtolontop, si c'est pour faire du vibecoding sur le projet ce n'est pas la peine de venir faire une PR.

MDRRRRR, elle est vraiment niquel ma PR je vois pas le soucis + c'est pas du vibecoding mais j'avoue que mon code a une vibe très propre :)

Ta mal pris le faite que tu es tort ici ?
image
ou ici ?
image

@gtolontop

Copy link
Copy Markdown
Contributor Author

Le satan qui se réveille avec le full vibecoding qui fait mal

Très propre se vibecoding en tous cas

@gtolontop

Copy link
Copy Markdown
Contributor Author

Bon @gtolontop, si c'est pour faire du vibecoding sur le projet ce n'est pas la peine de venir faire une PR.

Dis-moi, qu'aurais-tu fait différemment ?

@AxenoDev

AxenoDev commented Jun 23, 2026

Copy link
Copy Markdown
Member

Donc pourquoi il y a ecris CODEX dans ta PR ....
Ne me prends pas pour un con non plus

@AxenoDev

Copy link
Copy Markdown
Member

Bon @gtolontop, si c'est pour faire du vibecoding sur le projet ce n'est pas la peine de venir faire une PR.

Dis-moi, qu'aurais-tu fait différemment ?

Honnetement, bcp de chose, mais si tu "ne vibecode pas" chaque developpeur fait differemment donc je n'aurais pas fait pareil que toi

@iambibi

iambibi commented Jun 23, 2026

Copy link
Copy Markdown
Member

Bon @gtolontop, si c'est pour faire du vibecoding sur le projet ce n'est pas la peine de venir faire une PR.

MDRRRRR, elle est vraiment niquel ma PR je vois pas le soucis + c'est pas du vibecoding mais j'avoue que mon code a une vibe très propre :)

Ta mal pris le faite que tu es tort ici ?
image
ou ici ?
image

Tu mets tout sur le principe de la raison? Pourquoi ?
Tu as des choses à montrer?
Avoir tort n'est pas négatif le temps que la personne explique pourquoi elle a tort.

@ltuffery

Copy link
Copy Markdown
Contributor

Bonjour, elle est encore en draft justement parce que je préfère traiter les retours avant de la passer ready.

Pour la réflexion dans les tests je comprends se que tu dis mais j’ai vérifié, Mockito n'est pas dans les dépendances du projet. Je peux soit ajouter Mockito si vous êtes ok avec ça, soit refactorer le test / la logique pour éviter la réflexion.

Pour les tests EconomyManagerTest, oui je peux regarder pour les stabiliser. Actuellement, sur mon run local, les nouveaux tests isolés EconomyManagerDirtySaveTest passent, mais EconomyManagerTest a encore 5 fails liés au chargement global du plugin / scheduler lancé pendant MockBukkit.

Pour les copies mémoire elles sont là pour éviter qu’un objet mutable récupéré depuis l’API soit modifié sans passer par le dirty tracking. par exemple si getPlayerBank() retourne l’objet live, un appelant peut faire deposit() dessus sans marquer l’UUID dirty. Les copies évitent ça. Après, si vous préférez une autre approche côté API, je peux adapter.

Bonjour,

Il est préférable de passer la PR en ready si vous jugez qu’elle est prête pour la production. Étant donné que nous ne sommes pas nombreux à maintenir le projet, nous nous concentrons principalement sur celles qui sont en ready afin de fournir des retours.

Concernant la réflexion, il serait préférable de la retirer, mais sans utiliser Mockito. Cela fera l’objet d’une autre PR.
Pour les tests oubliés, j’avais peur que les échecs actuels soient causés par cette PR, mais après vérification, cela ne semble pas être le cas.

Concernant le clone, je laisse @PuppyTransGirl vérifier et nous donner son avis afin d’en discuter. De mon côté, je ne suis pas particulièrement favorable.

@gtolontop

Copy link
Copy Markdown
Contributor Author

Généralement, tu dois ouvrir ta PR pour qu'on sache qu'on peut review. Fait comme cela, sinon il risque d'avoir des oublis

ne t'étonne pas si ta PR ne se fait pas review.

Elle est déjà ouverte la PR ? J'ai pas compris

@iambibi

iambibi commented Jun 26, 2026

Copy link
Copy Markdown
Member

Elle est déjà ouverte la PR ? J'ai pas compris

Elle n'est pas en ready for review, elle est en draft

@gtolontop gtolontop marked this pull request as ready for review June 26, 2026 13:48
@gtolontop

Copy link
Copy Markdown
Contributor Author

Il est préférable de passer la PR en ready si vous jugez qu’elle est prête pour la production. Étant donné que nous ne sommes pas nombreux à maintenir le projet, nous nous concentrons principalement sur celles qui sont en ready afin de fournir des retours.

C'est bon :)

@gtolontop

Copy link
Copy Markdown
Contributor Author

Concernant la réflexion, il serait préférable de la retirer, mais sans utiliser Mockito. Cela fera l’objet d’une autre PR. Pour les tests oubliés, j’avais peur que les échecs actuels soient causés par cette PR, mais après vérification, cela ne semble pas être le cas.

Pourquoi faudrait-il la retirer si c'est correct et que ça fonctionne bien ? Et ça n'utilise déjà pas Mockito.

Comment thread src/main/java/fr/openmc/core/features/economy/EconomyManager.java Outdated
Comment thread src/main/java/fr/openmc/core/features/economy/EconomyManager.java Outdated
Comment thread src/main/java/fr/openmc/core/features/economy/EconomyManager.java Outdated
Comment on lines +88 to +143
@SuppressWarnings("unchecked")
private static Dao<EconomyPlayer, String> createPlayersDao(Map<UUID, Double> persistedBalances, Runnable onCreateOrUpdate) {
return (Dao<EconomyPlayer, String>) Proxy.newProxyInstance(
Dao.class.getClassLoader(),
new Class<?>[]{Dao.class},
(proxy, method, args) -> switch (method.getName()) {
case "callBatchTasks" -> ((Callable<?>) args[0]).call();
case "createOrUpdate" -> {
onCreateOrUpdate.run();
EconomyPlayer player = (EconomyPlayer) args[0];
persistedBalances.put(player.getPlayerUUID(), player.getBalance());
yield null;
}
case "toString" -> "InMemoryEconomyPlayerDao";
case "hashCode" -> System.identityHashCode(proxy);
case "equals" -> proxy == args[0];
default -> null;
}
);
}

@SuppressWarnings("unchecked")
private static Dao<EconomyPlayer, String> replacePlayersDao(Dao<EconomyPlayer, String> playersDao) throws Exception {
Field playersDaoField = EconomyManager.class.getDeclaredField("playersDao");
playersDaoField.setAccessible(true);

Dao<EconomyPlayer, String> previousPlayersDao = (Dao<EconomyPlayer, String>) playersDaoField.get(null);
playersDaoField.set(null, playersDao);

return previousPlayersDao;
}

@SuppressWarnings("unchecked")
private static Map<UUID, EconomyPlayer> replaceBalances(Map<UUID, EconomyPlayer> balances) throws Exception {
Field balancesField = EconomyManager.class.getDeclaredField("balances");
balancesField.setAccessible(true);

Map<UUID, EconomyPlayer> previousBalances = (Map<UUID, EconomyPlayer>) balancesField.get(null);
balancesField.set(null, balances);

return previousBalances;
}

@SuppressWarnings("unchecked")
private static Set<UUID> getDirtyBalances() throws Exception {
Field dirtyBalancesField = EconomyManager.class.getDeclaredField("dirtyBalances");
dirtyBalancesField.setAccessible(true);

return (Set<UUID>) dirtyBalancesField.get(null);
}

private static void saveAllBalances(boolean finalSave) throws Exception {
Method saveAllBalancesMethod = EconomyManager.class.getDeclaredMethod("saveAllBalances", boolean.class);
saveAllBalancesMethod.setAccessible(true);
saveAllBalancesMethod.invoke(null, finalSave);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fait gaffe a la rélfexion (reporté par Nirbose)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tu veux que je fasse quoi exactement dessus ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attends @ltuffery, je ne sais pas ce qu'il voulait que tu fasses

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ltuffery on t'attends

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pour moi, il faut retirer la réflexion. Ce n’est vraiment pas une bonne pratique de l’utiliser “à la dure” de cette manière, sans s’appuyer sur une librairie spécialement conçue pour ça.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

j'ai capté pour la reflexion

Je veux juste clarifier j'ai pas tous capté dans ton message précédent, tu disais qu’il fallait la retirer sans Mockito, mais que ça ferait l’objet d’une autre PR.

Du coup tu préfères que je le fasse directement dans cette PR, ou que je garde ça pour une autre PR ?

Si tu veux que je le fasse ici, je peux soit retirer le test qui dépend de l’état privé, soit essayer de le réécrire via l’API publique / H2.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

je ne pense pas qu'il va te répondre, ping le sur discord avec un lien de ton message

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quel channel pls ?

@iambibi

iambibi commented Jun 30, 2026

Copy link
Copy Markdown
Member

plop a review les gens

@iambibi iambibi requested a review from PuppyTransGirl June 30, 2026 17:20
@AxenoDev

Copy link
Copy Markdown
Member

@gtolontop possible d'avoir un jar stp

@gtolontop

Copy link
Copy Markdown
Contributor Author

OpenMC-dev.zip
Voici :)

@AxenoDev

AxenoDev commented Jul 1, 2026

Copy link
Copy Markdown
Member

je teste

@AxenoDev

AxenoDev commented Jul 1, 2026

Copy link
Copy Markdown
Member

OpenMC-dev.zip Voici :)

faut que tu build le jar avec le shadowJar

@AxenoDev

AxenoDev commented Jul 1, 2026

Copy link
Copy Markdown
Member

c'est pas possible tu n'as pas teste si tes modif fonctionnais

@gtolontop

Copy link
Copy Markdown
Contributor Author

c'est pas possible tu n'as pas teste si tes modif fonctionnais

image J'avais bien tester avant mybad j'ai été beaucoup occupé j'ai send le jar rapidement je me suis juste fail sur la commande de build j'ai pas build avc shadowjar voici [OpenMC-shadow-6c66bebc.zip](https://github.com/user-attachments/files/29576118/OpenMC-shadow-6c66bebc.zip)

@AxenoDev

AxenoDev commented Jul 1, 2026

Copy link
Copy Markdown
Member

Ok pas de soucis, juste ton lien ne fonctionne pas

@gtolontop

Copy link
Copy Markdown
Contributor Author

OpenMC-shadow-6c66bebc.zip

github me boycott bruhhh

@AxenoDev

AxenoDev commented Jul 1, 2026

Copy link
Copy Markdown
Member

https://spark.lucko.me/xOiSgERLyM avec 50joueurs @iambibi

@iambibi

iambibi commented Jul 2, 2026

Copy link
Copy Markdown
Member

https://spark.lucko.me/xOiSgERLyM avec 50joueurs @iambibi

T'es sûr d'avoir attendu le save des économies ?

@AxenoDev

AxenoDev commented Jul 2, 2026

Copy link
Copy Markdown
Member

j'ai attendu 5minutes

@iambibi

iambibi commented Jul 2, 2026

Copy link
Copy Markdown
Member

Y'a pas de bugs? Les joueurs ont bien leur thune de save?

@gtolontop

Copy link
Copy Markdown
Contributor Author

Y'a pas de bugs? Les joueurs ont bien leur thune de save?

Oui j'ai déjà tester xd

@ltuffery

ltuffery commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Mockito fera l’objet d’une autre PR, c’est ce que je voulais dire.

Concernant ta réflexion, tu dois la retirer : soit tu arrives à remplacer les tests avec l’API actuelle (ce qui serait idéal), soit tu trouves une autre approche.

@gtolontop

gtolontop commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

C’est traité dans le commit juste au dessus

J’ai retiré la réflexion du test y'a plus de getDeclaredField, getDeclaredMethod, setAccessible les tests passent maintenant par une vraie DB H2/ORMLite. Pour garder la couverture du cas finalSave, j’utilise un trigger H2 qui modifie une balance pendant l’écriture DB, ce qui permet de tester le comportement sans accéder à l’état privé de EconomyManager voilaaaa

vous pouvez testez avec

./gradlew test --tests fr.openmc.core.features.economy.EconomyManagerDirtySaveTest --no-daemon --rerun-tasks

@iambibi

iambibi commented Jul 4, 2026

Copy link
Copy Markdown
Member

Attente de review

@gtolontop

Copy link
Copy Markdown
Contributor Author
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆙 Amélioration Une amélioration sur quelque chose 🔄 Changement Un petit changement ⚡Optimisation Une amélioration niveau performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Optimisation de EconomyManager.setBalance

5 participants