/* * This file is part of Player Analytics (Plan). * * Plan is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License v3 as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Plan is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ package com.djrapitops.plan.storage.database.queries.objects; import com.djrapitops.plan.delivery.domain.DateObj; import com.djrapitops.plan.gathering.domain.TPS; import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.storage.database.queries.Query; import com.djrapitops.plan.storage.database.queries.QueryStatement; import com.djrapitops.plan.storage.database.sql.tables.ServerTable; import com.djrapitops.plan.utilities.dev.Benchmark; import com.djrapitops.plan.utilities.java.Lists; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.*; import java.util.concurrent.TimeUnit; import static com.djrapitops.plan.storage.database.sql.building.Sql.*; import static com.djrapitops.plan.storage.database.sql.tables.TPSTable.*; /** * Queries for {@link com.djrapitops.plan.gathering.domain.TPS} objects. * * @author AuroraLS3 */ public class TPSQueries { private TPSQueries() { /* Static method class */ } public static Query> fetchTPSDataOfServerInResolution(long after, long before, long resolution, ServerUUID serverUUID) { return db -> { String sql = SELECT + min("t." + DATE) + " as " + DATE + ',' + min("t." + TPS) + " as " + TPS + ',' + max("t." + PLAYERS_ONLINE) + " as " + PLAYERS_ONLINE + ',' + max("t." + RAM_USAGE) + " as " + RAM_USAGE + ',' + max("t." + CPU_USAGE) + " as " + CPU_USAGE + ',' + max("t." + ENTITIES) + " as " + ENTITIES + ',' + max("t." + CHUNKS) + " as " + CHUNKS + ',' + max("t." + FREE_DISK) + " as " + FREE_DISK + FROM + TABLE_NAME + " t" + WHERE + SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + AND + DATE + ">=?" + AND + DATE + ">(sql, 50000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, after); statement.setLong(3, before); statement.setLong(4, resolution); } @Override public List processResults(ResultSet set) throws SQLException { List data = new ArrayList<>(); while (set.next()) { data.add(extractTPS(set)); } return data; } }); }; } public static TPS extractTPS(ResultSet set) throws SQLException { return TPSBuilder.get() .date(set.getLong(DATE)) .tps(set.getDouble(TPS)) .playersOnline(set.getInt(PLAYERS_ONLINE)) .usedCPU(set.getDouble(CPU_USAGE)) .usedMemory(set.getLong(RAM_USAGE)) .entities(set.getInt(ENTITIES)) .chunksLoaded(set.getInt(CHUNKS)) .freeDiskSpace(set.getLong(FREE_DISK)) .toTPS(); } public static Query> fetchTPSDataOfServer(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "*" + FROM + TABLE_NAME + WHERE + SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + AND + DATE + ">=?" + AND + DATE + "<=?" + ORDER_BY + DATE; return new QueryStatement<>(sql, 50000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, after); statement.setLong(3, before); } @Override public List processResults(ResultSet set) throws SQLException { List data = new ArrayList<>(); while (set.next()) { TPS tps = extractTPS(set); data.add(tps); } return data; } }; } public static Query>> fetchViewPreviewGraphData(ServerUUID serverUUID) { String sql = SELECT + min(DATE) + " as " + DATE + ',' + max(PLAYERS_ONLINE) + " as " + PLAYERS_ONLINE + FROM + TABLE_NAME + WHERE + SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + GROUP_BY + floor(DATE + "/?"); return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, TimeUnit.MINUTES.toMillis(15)); } @Override public List> processResults(ResultSet set) throws SQLException { List> ofServer = new ArrayList<>(); while (set.next()) ofServer.add(new DateObj<>(set.getLong(DATE), set.getInt(PLAYERS_ONLINE))); return ofServer; } }; } public static Query>> fetchPlayersOnlineOfServer(long after, long before, ServerUUID serverUUID) { String sql = SELECT + ServerTable.SERVER_UUID + ',' + DATE + ',' + PLAYERS_ONLINE + FROM + TABLE_NAME + INNER_JOIN + ServerTable.TABLE_NAME + " on " + ServerTable.TABLE_NAME + '.' + ServerTable.ID + '=' + SERVER_ID + WHERE + ServerTable.SERVER_UUID + "=?" + AND + DATE + "?"; return new QueryStatement<>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public List> processResults(ResultSet set) throws SQLException { List> ofServer = new ArrayList<>(); while (set.next()) { ofServer.add(new DateObj<>(set.getLong(DATE), set.getInt(PLAYERS_ONLINE))); } return ofServer; } }; } public static Query>> fetchTPSDataOfAllServersBut(long after, long before, ServerUUID leaveOut) { String sql = SELECT + DATE + ',' + TPS + ',' + PLAYERS_ONLINE + ',' + SERVER_ID + FROM + TABLE_NAME + INNER_JOIN + ServerTable.TABLE_NAME + " on " + ServerTable.TABLE_NAME + '.' + ServerTable.ID + '=' + SERVER_ID + WHERE + ServerTable.SERVER_UUID + "!=?" + AND + ServerTable.INSTALLED + "=?" + AND + DATE + "?"; return new QueryStatement<>(sql, 5000) { @Override public void prepare(PreparedStatement statement) throws SQLException { if (leaveOut != null) { statement.setString(1, leaveOut.toString()); } else { statement.setNull(1, Types.VARCHAR); } statement.setBoolean(2, true); statement.setLong(3, before); statement.setLong(4, after); } @Override public Map> processResults(ResultSet set) throws SQLException { Map> byServer = new HashMap<>(); while (set.next()) { Integer serverUID = set.getInt(SERVER_ID); List ofServer = byServer.computeIfAbsent(serverUID, Lists::create); ofServer.add(TPSBuilder.get() .date(set.getLong(DATE)) .tps(set.getDouble(TPS)) .playersOnline(set.getInt(PLAYERS_ONLINE)) .toTPS()); } return byServer; } }; } public static Query>> fetchPeakPlayerCount(ServerUUID serverUUID, long afterDate) { String subQuery = '(' + SELECT + "MAX(" + PLAYERS_ONLINE + ") as " + PLAYERS_ONLINE + FROM + TABLE_NAME + WHERE + SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + AND + DATE + ">= ?" + GROUP_BY + SERVER_ID + ")"; String sql = SELECT + "t." + DATE + ',' + "t." + PLAYERS_ONLINE + FROM + TABLE_NAME + " t" + INNER_JOIN + subQuery + " max on t." + PLAYERS_ONLINE + "=max." + PLAYERS_ONLINE + WHERE + SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID + AND + "t." + DATE + ">= ?" + ORDER_BY + "t." + DATE + " DESC LIMIT 1"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, afterDate); statement.setString(3, serverUUID.toString()); statement.setLong(4, afterDate); } @Override public Optional> processResults(ResultSet set) throws SQLException { if (set.next()) { return Optional.of(new DateObj<>( set.getLong(DATE), set.getInt(PLAYERS_ONLINE) )); } return Optional.empty(); } }; } public static Query>> fetchAllTimePeakPlayerCount(ServerUUID serverUUID) { return fetchPeakPlayerCount(serverUUID, 0); } public static Query> fetchLatestTPSEntryForServer(ServerUUID serverUUID) { String sql = SELECT + "*" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + ORDER_BY + DATE + " DESC LIMIT 1"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); } @Override public Optional processResults(ResultSet set) throws SQLException { if (set.next()) { return Optional.of(TPSBuilder.get() .date(set.getLong(DATE)) .tps(set.getDouble(TPS)) .playersOnline(set.getInt(PLAYERS_ONLINE)) .usedCPU(set.getDouble(CPU_USAGE)) .usedMemory(set.getLong(RAM_USAGE)) .entities(set.getInt(ENTITIES)) .chunksLoaded(set.getInt(CHUNKS)) .freeDiskSpace(set.getLong(FREE_DISK)) .toTPS()); } return Optional.empty(); } }; } public static Query averageTPS(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "AVG(" + TPS + ") as average" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + TPS + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Double processResults(ResultSet set) throws SQLException { return set.next() ? set.getDouble("average") : -1.0; } }; } public static Query averageCPU(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "AVG(" + CPU_USAGE + ") as average" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + CPU_USAGE + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Double processResults(ResultSet set) throws SQLException { return set.next() ? set.getDouble("average") : -1.0; } }; } public static Query averageRAM(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "AVG(" + RAM_USAGE + ") as average" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + RAM_USAGE + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Long processResults(ResultSet set) throws SQLException { return set.next() ? (long) set.getDouble("average") : -1L; } }; } public static Query averageChunks(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "AVG(" + CHUNKS + ") as average" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + CHUNKS + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Long processResults(ResultSet set) throws SQLException { return set.next() ? (long) set.getDouble("average") : -1L; } }; } public static Query averageEntities(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "AVG(" + ENTITIES + ") as average" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + ENTITIES + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Long processResults(ResultSet set) throws SQLException { return set.next() ? (long) set.getDouble("average") : -1L; } }; } public static Query maxFreeDisk(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "MAX(" + FREE_DISK + ") as free" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + FREE_DISK + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Long processResults(ResultSet set) throws SQLException { return set.next() ? set.getLong("free") : -1L; } }; } public static Query minFreeDisk(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "MIN(" + FREE_DISK + ") as free" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + FREE_DISK + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Long processResults(ResultSet set) throws SQLException { return set.next() ? set.getLong("free") : -1L; } }; } public static Query averageFreeDisk(long after, long before, ServerUUID serverUUID) { String sql = SELECT + "AVG(" + FREE_DISK + ") as average" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + AND + FREE_DISK + ">=0" + AND + DATE + "?"; return new QueryStatement<>(sql) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setLong(2, before); statement.setLong(3, after); } @Override public Long processResults(ResultSet set) throws SQLException { return set.next() ? (long) set.getDouble("average") : -1L; } }; } public static Query>> fetchTPSDataOfServers(long after, long before, Collection serverUUIDs) { String sql = SELECT + "*" + FROM + TABLE_NAME + WHERE + SERVER_ID + " IN " + ServerTable.selectServerIds(serverUUIDs) + AND + DATE + ">=?" + AND + DATE + "<=?" + ORDER_BY + DATE; return new QueryStatement<>(sql, 50000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setLong(1, after); statement.setLong(2, before); } @Override public Map> processResults(ResultSet set) throws SQLException { Map> data = new HashMap<>(); while (set.next()) { int serverId = set.getInt(SERVER_ID); data.computeIfAbsent(serverId, Lists::create) .add(extractTPS(set)); } return data; } }; } @Benchmark.Slow("1s") public static Query> fetchLatestServerStartTime(ServerUUID serverUUID, long dataGapThreshold) { String selectPreviousRowNumber = SELECT + "-1+ROW_NUMBER() over (ORDER BY " + DATE + ") AS previous_rn, " + SERVER_ID + ',' + DATE + " AS d1" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + GROUP_BY + SERVER_ID + ',' + DATE + ORDER_BY + "d1 DESC"; String selectRowNumber = SELECT + "ROW_NUMBER() over (ORDER BY " + DATE + ") AS rn, " + SERVER_ID + ',' + DATE + " AS previous_date" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + GROUP_BY + SERVER_ID + ',' + DATE + ORDER_BY + "previous_date DESC"; String selectFirstEntryDate = SELECT + "MIN(" + DATE + ") as start_time," + SERVER_ID + " as server_id" + FROM + TABLE_NAME + WHERE + SERVER_ID + '=' + ServerTable.SELECT_SERVER_ID + GROUP_BY + SERVER_ID; // Finds the start time since difference between d1 and previous date is a gap, // so d1 is always first entry after a gap in the data. MAX finds the latest. // Union ensures if there are no gaps to use the first date recorded. String selectStartTime = SELECT + "MAX(d1) AS start_time," + "t1." + SERVER_ID + " as server_id" + FROM + "(" + selectPreviousRowNumber + ") t1" + INNER_JOIN + "(" + selectRowNumber + ") t2 ON t1.previous_rn=t2.rn AND t2." + SERVER_ID + "=t1." + SERVER_ID + WHERE + "d1 - previous_date > ?" + GROUP_BY + "t1." + SERVER_ID + UNION + selectFirstEntryDate; return new QueryStatement<>(selectStartTime) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); statement.setString(2, serverUUID.toString()); statement.setLong(3, dataGapThreshold); statement.setString(4, serverUUID.toString()); } @Override public Optional processResults(ResultSet set) throws SQLException { long startTime = 0; while (set.next()) { long gotStartTime = set.getLong("start_time"); if (!set.wasNull()) { startTime = Math.max(startTime, gotStartTime); } } return startTime != 0 ? Optional.of(startTime) : Optional.empty(); } }; } }