Skip to content

Commit

Permalink
Follow rollczi feedback's
Browse files Browse the repository at this point in the history
  • Loading branch information
vLuckyyy committed Feb 13, 2025
1 parent 0b02ce1 commit e57c7ad
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 282 deletions.
Original file line number Diff line number Diff line change
@@ -1,68 +1,68 @@
package com.eternalcode.economy.account;

import com.eternalcode.economy.account.database.AccountRepositoryInMemory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Warmup;

import java.util.UUID;

@State(Scope.Thread)
public class AccountBenchmark {

private static final Logger LOGGER = Logger.getLogger(AccountBenchmark.class.getName());
private static final char[] ALPHABET = "abcdefghijklmnopqrstuvwxyz".toCharArray();

private final List<String> searches = new ArrayList<>();
private AccountManager accountManager;

@Setup
public void setUp() {
accountManager = new AccountManager(new AccountRepositoryInMemory());

// zapełnienie TreeMapy różnymi nazwami zapewnia, że będzie ona miała optymalne wyniki
// tree mapa rozdziela elementy na podstawie ich klucza, więc im bardziej zróżnicowane klucze, tym "lepsze' wyniki
for (char first : ALPHABET) {
for (char second : ALPHABET) {
for (char third : ALPHABET) {
String name = String.valueOf(first) + second + third;

accountManager.create(UUID.randomUUID(), name);
}
}
}

// pre-generowanie losowych wyszukiwań, które będą wykonywane w benchmarku (nie wpływamy na czas wykonania samego benchmarku)
for (char first : ALPHABET) {
searches.add(String.valueOf(first));
for (char second : ALPHABET) {
searches.add(String.valueOf(first) + second);
for (char third : ALPHABET) {
searches.add(String.valueOf(first) + second + third);
}
}
}

Collections.shuffle(searches); // mieszamy, aby zapewnić losowy dostęp

LOGGER.info("Acounts size: " + accountManager.getAccounts().size() + ", Searches size: " + searches.size());
}

// mimo że nie jest to bezpieczne dla wielu wątków, to w przypadku JMH można to zignorować i tak potrzebujemy losowości
private int index = 0;

@Benchmark
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 10, time = 1)
public void benchmarkGetAccountStartingWith() {
accountManager.getAccountStartingWith(searches.get(index++ % searches.size()));
}

}
// package com.eternalcode.economy.account;
//
// import com.eternalcode.economy.account.database.AccountRepositoryInMemory;
// import java.util.ArrayList;
// import java.util.Collections;
// import java.util.List;
// import java.util.logging.Logger;
// import org.openjdk.jmh.annotations.Benchmark;
// import org.openjdk.jmh.annotations.Setup;
// import org.openjdk.jmh.annotations.State;
// import org.openjdk.jmh.annotations.Scope;
// import org.openjdk.jmh.annotations.Measurement;
// import org.openjdk.jmh.annotations.Warmup;
//
// import java.util.UUID;
//
// @State(Scope.Thread)
// public class AccountBenchmark {
//
// private static final Logger LOGGER = Logger.getLogger(AccountBenchmark.class.getName());
// private static final char[] ALPHABET = "abcdefghijklmnopqrstuvwxyz".toCharArray();
//
// private final List<String> searches = new ArrayList<>();
// private AccountManager accountManager;
//
// @Setup
// public void setUp() {
// accountManager = new AccountManager(new AccountRepositoryInMemory());
//
// // zapełnienie TreeMapy różnymi nazwami zapewnia, że będzie ona miała optymalne wyniki
// // tree mapa rozdziela elementy na podstawie ich klucza, więc im bardziej zróżnicowane klucze, tym "lepsze' wyniki
// for (char first : ALPHABET) {
// for (char second : ALPHABET) {
// for (char third : ALPHABET) {
// String name = String.valueOf(first) + second + third;
//
// accountManager.create(UUID.randomUUID(), name);
// }
// }
// }
//
// // pre-generowanie losowych wyszukiwań, które będą wykonywane w benchmarku (nie wpływamy na czas wykonania samego benchmarku)
// for (char first : ALPHABET) {
// searches.add(String.valueOf(first));
// for (char second : ALPHABET) {
// searches.add(String.valueOf(first) + second);
// for (char third : ALPHABET) {
// searches.add(String.valueOf(first) + second + third);
// }
// }
// }
//
// Collections.shuffle(searches); // mieszamy, aby zapewnić losowy dostęp
//
// LOGGER.info("Acounts size: " + accountManager.getAccounts().size() + ", Searches size: " + searches.size());
// }
//
// // mimo że nie jest to bezpieczne dla wielu wątków, to w przypadku JMH można to zignorować i tak potrzebujemy losowości
// private int index = 0;
//
// @Benchmark
// @Warmup(iterations = 5, time = 1)
// @Measurement(iterations = 10, time = 1)
// public void benchmarkGetAccountStartingWith() {
// accountManager.getAccountStartingWith(searches.get(index++ % searches.size()));
// }
//
// }
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.eternalcode.economy.leaderboard;

import com.eternalcode.economy.account.Account;
import com.eternalcode.economy.account.AccountManager;
import com.eternalcode.economy.account.database.AccountRepositoryInMemory;
import com.eternalcode.economy.config.implementation.PluginConfig;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
Expand All @@ -20,36 +24,59 @@
import org.openjdk.jmh.infra.Blackhole;

@State(Scope.Benchmark)
@Fork(value = 2)
@Warmup(iterations = 5, time = 3)
@Measurement(iterations = 10, time = 3)
public class LeaderboardServiceBenchmark {

private LeaderboardService leaderboardService;

@Setup
public void setUp() {
AccountRepositoryInMemory accountRepository = new AccountRepositoryInMemory();
AccountManager accountManager = new AccountManager(new AccountRepositoryInMemory());

PluginConfig pluginConfig = new PluginConfig();
pluginConfig.leaderboardEntriesPerPage = 100;

this.leaderboardService = new LeaderboardService(accountRepository);
this.leaderboardService = new LeaderboardService(accountManager);

for (int i = 0; i < 10_000; i++) {
UUID uuid = UUID.randomUUID();
String name = "Player" + i;
BigDecimal balance = BigDecimal.valueOf(10_000 - i);
BigDecimal balance = BigDecimal.valueOf(ThreadLocalRandom.current().nextInt(0, 10_001));
Account account = new Account(uuid, name, balance);
accountRepository.save(account);
accountManager.create(account);
}
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 10, time = 1)
public void benchmarkGetLeaderboard(Blackhole blackhole) {
CompletableFuture<Collection<Account>> future = this.leaderboardService.getLeaderboard();
Collection<Account> leaderboard = future.join();
CompletableFuture<List<LeaderboardEntry>> future = this.leaderboardService.getLeaderboard();
List<LeaderboardEntry> leaderboard = future.join();
blackhole.consume(leaderboard);
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void benchmarkGetLeaderboardPage(Blackhole blackhole) {
CompletableFuture<LeaderboardPage> future = this.leaderboardService.getLeaderboardPage(1, 100);
LeaderboardPage leaderboardPage = future.join();
blackhole.consume(leaderboardPage);
}

@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void benchmarkGetLeaderboardPosition(Blackhole blackhole) {
CompletableFuture<List<LeaderboardEntry>> future = this.leaderboardService.getLeaderboard();
List<LeaderboardEntry> leaderboard = future.join();
LeaderboardEntry targetAccount = leaderboard.iterator().next();

CompletableFuture<LeaderboardEntry> positionFuture = this.leaderboardService.getLeaderboardPosition(targetAccount.account());
LeaderboardEntry position = positionFuture.join();
blackhole.consume(position);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void onEnable() {
AccountRepository accountRepository = new AccountRepositoryImpl(this.databaseManager, scheduler);
AccountManager accountManager = AccountManager.create(accountRepository);

LeaderboardService leaderboardService = new LeaderboardService(accountRepository);
LeaderboardService leaderboardService = new LeaderboardService(accountManager);

DecimalFormatter decimalFormatter = new DecimalFormatterImpl(pluginConfig);
AccountPaymentService accountPaymentService = new AccountPaymentService(accountManager, pluginConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@

import com.eternalcode.economy.account.database.AccountRepository;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.Collection;
import java.util.Collections;

public class AccountManager {

private final Map<UUID, Account> accountByUniqueId = new HashMap<>();
private final Map<String, Account> accountByName = new HashMap<>();
private final TreeMap<String, Account> accountIndex = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private final NavigableMap<BigDecimal, Set<Account>> accountsByBalance = new TreeMap<>(Comparator.reverseOrder());

private final AccountRepository accountRepository;

Expand All @@ -28,6 +33,8 @@ public static AccountManager create(AccountRepository accountRepository) {
for (Account account : accounts) {
accountManager.accountByUniqueId.put(account.uuid(), account);
accountManager.accountByName.put(account.name(), account);
accountManager.accountIndex.put(account.name(), account);
accountManager.accountsByBalance.computeIfAbsent(account.balance(), k -> new HashSet<>()).add(account);
}
});

Expand Down Expand Up @@ -67,13 +74,39 @@ public Account create(UUID uuid, String name) {
this.accountByUniqueId.put(uuid, account);
this.accountByName.put(name, account);
this.accountIndex.put(name, account);
this.accountsByBalance.computeIfAbsent(account.balance(), k -> new HashSet<>()).add(account);

return account;
}

void save(Account account) {
public Account create(Account account) {
if (this.accountByUniqueId.containsKey(account.uuid())) {
throw new IllegalArgumentException("Account already exists: " + account.uuid());
}

this.accountByUniqueId.put(account.uuid(), account);
this.accountByName.put(account.name(), account);
this.accountIndex.put(account.name(), account);
this.accountsByBalance.computeIfAbsent(account.balance(), k -> new HashSet<>()).add(account);

return account;
}

public void save(Account account) {
Account previous = this.accountByUniqueId.put(account.uuid(), account);
if (previous != null) {
Set<Account> accountsWithPreviousBalance = this.accountsByBalance.get(previous.balance());
if (accountsWithPreviousBalance != null) {
accountsWithPreviousBalance.remove(previous);
if (accountsWithPreviousBalance.isEmpty()) {
this.accountsByBalance.remove(previous.balance());
}
}
}

this.accountByName.put(account.name(), account);
this.accountIndex.put(account.name(), account);
this.accountsByBalance.computeIfAbsent(account.balance(), k -> new HashSet<>()).add(account);
this.accountRepository.save(account);
}

Expand All @@ -86,4 +119,8 @@ public Collection<Account> getAccountStartingWith(String prefix) {
public Collection<Account> getAccounts() {
return Collections.unmodifiableCollection(this.accountByUniqueId.values());
}

public NavigableMap<BigDecimal, Set<Account>> getAccountsByBalance() {
return Collections.unmodifiableNavigableMap(this.accountsByBalance);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public class MessagesPlayerSubSection extends OkaeriConfig {
"Use {BALANCE} placeholder to show the player's formatted balance",
"Use {BALANCE_RAW} placeholder to show the player's raw balance"
})
public Notice leaderboardEntry = Notice.chat("<white>#{POSITION} <gradient:#00FFA2:#34AE00>{PLAYER}</gradient> - <gradient:#00FFA2:#34AE00>{BALANCE}</gradient></white>");
public Notice leaderboardEntry = Notice.chat(" <white>#{POSITION} <gradient:#00FFA2:#34AE00>{PLAYER}</gradient> -"
+ " <gradient:#00FFA2:#34AE00>{BALANCE}</gradient></white>");

@Comment({
"Leaderboard position notice, only displayed if showLeaderboardPosition is set to true in the config.yml",
Expand All @@ -59,7 +60,7 @@ public class MessagesPlayerSubSection extends OkaeriConfig {
"Use {TOTAL_PAGES} placeholder to show the total number of pages",
"Use {PAGE} placeholder to show the current page number"
})
public Notice leaderboardFooter = Notice.chat("<newline> <white>Click <gradient:#00FFA2:#34AE00><hover:show_text:'<white>Go to page {PAGE}</white>'>/baltop {PAGE}</hover></gradient> to go to the next page.</white>");
public Notice leaderboardFooter = Notice.chat("<newline> <white>Click <gradient:#00FFA2:#34AE00><hover:show_text:'<white>Go to page {NEXT_PAGE}</white>'><click:run_command:'/baltop {NEXT_PAGE}'>/baltop {NEXT_PAGE}</click></hover></gradient> to go to the next page.</white>");

@Comment("Leaderboard is empty notice")
public Notice leaderboardEmpty = Notice.chat("<b><gradient:#00FFA2:#34AE00>ECONOMY</gradient></b> "
Expand Down
Loading

0 comments on commit e57c7ad

Please sign in to comment.