Wrote a performance test generation utility

Not yet finished, does not yet generate all necessary data for tests.

It would be a good idea to store this data in
SQLite files and then import those to MySQL if
necessary when running performance tests.

Idea is to allow generating multiple
different scenarios and then measure the
performance of different endpoints to estimate
time complexity and find bottlenecks.
This commit is contained in:
Rsl1122 2022-07-02 20:05:00 +03:00
parent d4b4aacd69
commit 2240b7094e
3 changed files with 166 additions and 5 deletions

View File

@ -0,0 +1,122 @@
package utilities;
import com.djrapitops.plan.gathering.domain.BaseUser;
import com.djrapitops.plan.gathering.domain.FinishedSession;
import com.djrapitops.plan.identification.Server;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.storage.database.DaggerDatabaseTestComponent;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.DatabaseTestComponent;
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
import com.djrapitops.plan.storage.database.transactions.events.PlayerServerRegisterTransaction;
import com.djrapitops.plan.storage.database.transactions.events.StoreJoinAddressTransaction;
import com.djrapitops.plan.storage.database.transactions.events.StoreSessionTransaction;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import net.playeranalytics.plugin.scheduling.TimeAmount;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
public class PerformanceTestDataGenerator {
public static Path tempDir;
private static Database database;
public static void main(String[] args) throws Exception {
tempDir = createTempDir();
DatabaseTestComponent component = DaggerDatabaseTestComponent.builder()
.bindTemporaryDirectory(tempDir)
.build();
DBPreparer dbPreparer = new DBPreparer(component, RandomData.randomNonPrivilegedPort());
database = dbPreparer.prepareMySQL()
.orElseThrow(() -> new IllegalStateException("Could not initialize database"));
int numberOfServers = 1;
int worldsPerServer = 1;
int numberOfJoinAddresses = 50;
int numberOfPlayers = 1000;
int sessionsPerDay = 100;
long timespan = TimeAmount.MONTH.toMillis(2);
long earliestDate = System.currentTimeMillis() - timespan;
long numberOfSessions = sessionsPerDay * TimeUnit.MILLISECONDS.toDays(timespan);
long numberOfTPSEntries = TimeUnit.MILLISECONDS.toMinutes(timespan);
long numberOfPingEntries = TimeUnit.MILLISECONDS.toMinutes(timespan) * sessionsPerDay / 20;
List<ServerUUID> serverUUIDs = RandomData.pickMultiple(numberOfServers, ServerUUID::randomUUID);
Map<ServerUUID, String[]> allWorlds = new HashMap<>();
for (ServerUUID serverUUID : serverUUIDs) {
Server server = RandomData.randomServer(serverUUID);
database.executeTransaction(new StoreServerInformationTransaction(server));
List<String> worlds = RandomData.pickMultiple(worldsPerServer, () -> RandomData.randomString(30));
allWorlds.put(serverUUID, worlds.toArray(new String[0]));
for (String world : worlds) {
database.executeTransaction(new WorldNameStoreTransaction(serverUUID, world));
}
}
List<String> joinAddresses = RandomData.pickMultiple(numberOfJoinAddresses, () -> RandomData.randomString(255));
for (String joinAddress : joinAddresses) {
database.executeTransaction(new StoreJoinAddressTransaction(joinAddress));
}
List<BaseUser> players = RandomData.pickMultiple(numberOfPlayers, () -> RandomData.randomBaseUser(earliestDate));
Map<UUID, BaseUser> playerLookup = players.stream()
.collect(Collectors.toMap(BaseUser::getUuid, Function.identity()));
List<FinishedSession> sessions = RandomData.pickMultiple(numberOfSessions, () -> {
ServerUUID server = RandomData.pickAtRandom(serverUUIDs);
UUID[] playerUUIDs = RandomData.pickMultiple(RandomData.randomInt(1, 8), () -> RandomData.pickAtRandom(players))
.stream()
.map(BaseUser::getUuid)
.distinct()
.toArray(UUID[]::new);
return RandomData.randomSession(
server,
allWorlds.get(server),
playerUUIDs
);
});
Map<ServerUUID, Set<BaseUser>> playersToRegister = new HashMap<>();
for (FinishedSession session : sessions) {
UUID playerUUID = session.getPlayerUUID();
ServerUUID serverUUID = session.getServerUUID();
Set<BaseUser> usersOfServer = playersToRegister.computeIfAbsent(serverUUID, k -> new HashSet<>());
usersOfServer.add(playerLookup.get(playerUUID));
}
for (var usersOfServer : playersToRegister.entrySet()) {
ServerUUID serverUUID = usersOfServer.getKey();
for (BaseUser baseUser : usersOfServer.getValue()) {
database.executeTransaction(new PlayerServerRegisterTransaction(
baseUser.getUuid(),
baseUser::getRegistered,
baseUser.getName(),
serverUUID,
() -> RandomData.pickAtRandom(joinAddresses)));
}
}
for (FinishedSession session : sessions) {
database.executeTransaction(new StoreSessionTransaction(session));
}
}
private static Path createTempDir() throws IOException {
return Files.createTempDirectory("plan-performance-test-data-generator");
}
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.delivery.domain.Nickname;
import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point;
import com.djrapitops.plan.gathering.domain.*;
import com.djrapitops.plan.gathering.domain.event.JoinAddress;
import com.djrapitops.plan.identification.Server;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.storage.database.sql.tables.KillsTable;
import org.apache.commons.lang3.RandomStringUtils;
@ -47,6 +48,11 @@ public class RandomData {
return ThreadLocalRandom.current().nextInt(rangeStart, rangeEnd);
}
public static int randomNonPrivilegedPort() {
return ThreadLocalRandom.current()
.nextInt(65535 - 1024) + 1024;
}
public static long randomTime() {
return randomTimeAfter(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60L));
}
@ -99,7 +105,11 @@ public class RandomData {
return from[randomInt(0, from.length)];
}
public static <T> List<T> pickMultiple(int howMany, Supplier<T> supplier) {
public static <T> T pickAtRandom(List<T> from) {
return from.get(randomInt(0, from.size()));
}
public static <T> List<T> pickMultiple(long howMany, Supplier<T> supplier) {
List<T> picked = new ArrayList<>();
for (int i = 0; i < howMany; i++) {
picked.add(supplier.get());
@ -112,9 +122,13 @@ public class RandomData {
}
public static FinishedSession randomSession(ServerUUID serverUUID, String[] worlds, UUID... uuids) {
return randomSession(RandomData.randomTime(), serverUUID, worlds, uuids);
}
public static FinishedSession randomSession(long after, ServerUUID serverUUID, String[] worlds, UUID... uuids) {
DataMap extraData = new DataMap();
extraData.put(WorldTimes.class, RandomData.randomWorldTimes(worlds));
long start = RandomData.randomTime();
long start = RandomData.randomTimeAfter(after);
long end = RandomData.randomTimeAfter(start);
if (uuids.length >= 2) {
@ -210,4 +224,30 @@ public class RandomData {
.map(JoinAddress::new)
.collect(Collectors.toList());
}
public static Server randomServer(ServerUUID serverUUID) {
return new Server(
serverUUID,
RandomData.randomString(16),
"http://localhost",
RandomData.randomVersion()
);
}
private static String randomVersion() {
return RandomData.randomInt(1, 20)
+ "." +
RandomData.randomInt(0, 200)
+ " build " +
RandomData.randomInt(0, 100000);
}
public static BaseUser randomBaseUser(long earliestDate) {
return new BaseUser(
UUID.randomUUID(),
RandomData.randomString(20),
RandomData.randomLong(earliestDate, System.currentTimeMillis()),
RandomData.randomInt(0, 100)
);
}
}

View File

@ -22,11 +22,11 @@ import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.storage.database.SQLDB;
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
import net.playeranalytics.plugin.PlatformAbstractionLayer;
import utilities.RandomData;
import utilities.dagger.DaggerPlanPluginComponent;
import utilities.dagger.PlanPluginComponent;
import java.nio.file.Path;
import java.util.concurrent.ThreadLocalRandom;
/**
* Test utility for creating a dagger PlanComponent using a mocked Plan.
@ -58,8 +58,7 @@ public class PluginMockComponent {
public PlanSystem getPlanSystem() throws Exception {
initComponent();
PlanSystem system = component.system();
system.getConfigSystem().getConfig().set(WebserverSettings.PORT, ThreadLocalRandom.current()
.nextInt(65535 - 1024) + 1024); // Random non-privileged port
system.getConfigSystem().getConfig().set(WebserverSettings.PORT, RandomData.randomNonPrivilegedPort());
return system;
}