diff --git a/Plan/common/src/test/java/utilities/PerformanceTestDataGenerator.java b/Plan/common/src/test/java/utilities/PerformanceTestDataGenerator.java new file mode 100644 index 000000000..56d14c6c9 --- /dev/null +++ b/Plan/common/src/test/java/utilities/PerformanceTestDataGenerator.java @@ -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 serverUUIDs = RandomData.pickMultiple(numberOfServers, ServerUUID::randomUUID); + Map allWorlds = new HashMap<>(); + for (ServerUUID serverUUID : serverUUIDs) { + Server server = RandomData.randomServer(serverUUID); + database.executeTransaction(new StoreServerInformationTransaction(server)); + + List 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 joinAddresses = RandomData.pickMultiple(numberOfJoinAddresses, () -> RandomData.randomString(255)); + for (String joinAddress : joinAddresses) { + database.executeTransaction(new StoreJoinAddressTransaction(joinAddress)); + } + + + List players = RandomData.pickMultiple(numberOfPlayers, () -> RandomData.randomBaseUser(earliestDate)); + Map playerLookup = players.stream() + .collect(Collectors.toMap(BaseUser::getUuid, Function.identity())); + + List 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> playersToRegister = new HashMap<>(); + for (FinishedSession session : sessions) { + UUID playerUUID = session.getPlayerUUID(); + ServerUUID serverUUID = session.getServerUUID(); + + Set 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"); + } + +} diff --git a/Plan/common/src/test/java/utilities/RandomData.java b/Plan/common/src/test/java/utilities/RandomData.java index d7847926b..14afd3ee5 100644 --- a/Plan/common/src/test/java/utilities/RandomData.java +++ b/Plan/common/src/test/java/utilities/RandomData.java @@ -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 List pickMultiple(int howMany, Supplier supplier) { + public static T pickAtRandom(List from) { + return from.get(randomInt(0, from.size())); + } + + public static List pickMultiple(long howMany, Supplier supplier) { List 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) + ); + } } diff --git a/Plan/common/src/test/java/utilities/mocks/PluginMockComponent.java b/Plan/common/src/test/java/utilities/mocks/PluginMockComponent.java index ebff0087f..fefe998ff 100644 --- a/Plan/common/src/test/java/utilities/mocks/PluginMockComponent.java +++ b/Plan/common/src/test/java/utilities/mocks/PluginMockComponent.java @@ -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; }