Register plan_user row if missing when required

- PingStoreTransaction
- GeoInfoStoreTransaction
- SessionEndTransaction

Affects issues:
- Fixed #2361
- Fixed #2343
This commit is contained in:
Aurora Lahtela 2022-05-07 10:40:50 +03:00
parent 8cae81fb05
commit 6fa552ca5e
10 changed files with 105 additions and 11 deletions

View File

@ -29,6 +29,7 @@ import java.util.Optional;
*/
public class DBOpException extends IllegalStateException implements ExceptionWithContext {
public static final String CONSTRAINT_VIOLATION = "Constraint Violation";
private final ErrorContext context;
public DBOpException(String message) {
@ -101,7 +102,7 @@ public class DBOpException extends IllegalStateException implements ExceptionWit
case 1364:
case 1451:
case 1557:
context.related("Constraint Violation")
context.related(CONSTRAINT_VIOLATION)
.whatToDo("Report this, there is an SQL Constraint Violation.");
break;
// Custom rules based on reported errors
@ -146,4 +147,10 @@ public class DBOpException extends IllegalStateException implements ExceptionWit
public Optional<ErrorContext> getContext() {
return Optional.ofNullable(context);
}
public boolean isUserIdConstraintViolation() {
return context != null
&& context.getRelated().contains(DBOpException.CONSTRAINT_VIOLATION)
&& getMessage().contains("user_id");
}
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.delivery.web.resolver.request.Request;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -38,11 +39,13 @@ public class Identifiers {
protected final DBSystem dbSystem;
private final UUIDUtility uuidUtility;
private final ErrorLogger errorLogger;
@Inject
public Identifiers(DBSystem dbSystem, UUIDUtility uuidUtility) {
public Identifiers(DBSystem dbSystem, UUIDUtility uuidUtility, ErrorLogger errorLogger) {
this.dbSystem = dbSystem;
this.uuidUtility = uuidUtility;
this.errorLogger = errorLogger;
}
/**
@ -105,6 +108,10 @@ public class Identifiers {
return uuidUtility.getUUIDOf(name);
}
public Optional<Integer> getPlayerUserId(UUID playerUUID) {
return dbSystem.getDatabase().query(UserIdentifierQueries.fetchUserId(playerUUID));
}
public static Optional<Long> getTimestamp(Request request) {
try {
long currentTime = System.currentTimeMillis();

View File

@ -146,7 +146,7 @@ public class DataStoreQueries {
* @param playerName Name of the player.
* @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction}
*/
public static Executable registerBaseUser(UUID playerUUID, long registered, String playerName) {
public static ExecStatement registerBaseUser(UUID playerUUID, long registered, String playerName) {
return new ExecStatement(UsersTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {

View File

@ -218,4 +218,23 @@ public class UserIdentifierQueries {
}
};
}
public static Query<Optional<Integer>> fetchUserId(UUID playerUUID) {
String sql = Select.from(UsersTable.TABLE_NAME, UsersTable.ID).where(UsersTable.USER_UUID + "=?").toString();
return new QueryStatement<Optional<Integer>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Optional<Integer> processResults(ResultSet set) throws SQLException {
if (set.next()) {
return Optional.of(set.getInt(UsersTable.ID));
}
return Optional.empty();
}
};
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.storage.database.transactions.events;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.gathering.domain.GeoInfo;
import com.djrapitops.plan.storage.database.queries.DataStoreQueries;
import com.djrapitops.plan.storage.database.transactions.Transaction;
@ -72,6 +73,19 @@ public class GeoInfoStoreTransaction extends Transaction {
protected void performOperations() {
if (geoInfo == null) geoInfo = createGeoInfo();
if (geoInfo.getGeolocation() == null) return; // Don't save null geolocation.
try {
execute(DataStoreQueries.storeGeoInfo(playerUUID, geoInfo));
} catch (DBOpException failed) {
if (failed.isUserIdConstraintViolation()) {
retry();
} else {
throw failed;
}
}
}
private void retry() {
executeOther(new PlayerRegisterTransaction(playerUUID, System::currentTimeMillis, playerUUID.toString()));
execute(DataStoreQueries.storeGeoInfo(playerUUID, geoInfo));
}
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.storage.database.transactions.events;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.gathering.domain.Ping;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.storage.database.queries.DataStoreQueries;
@ -48,6 +49,19 @@ public class PingStoreTransaction extends Transaction {
@Override
protected void performOperations() {
Ping ping = calculateAggregatePing();
try {
execute(DataStoreQueries.storePing(playerUUID, serverUUID, ping));
} catch (DBOpException failed) {
if (failed.isUserIdConstraintViolation()) {
retry(ping);
} else {
throw failed;
}
}
}
private void retry(Ping ping) {
executeOther(new PlayerRegisterTransaction(playerUUID, System::currentTimeMillis, playerUUID.toString()));
execute(DataStoreQueries.storePing(playerUUID, serverUUID, ping));
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.storage.database.queries.DataStoreQueries;
import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import java.util.Optional;
import java.util.UUID;
import java.util.function.LongSupplier;
@ -36,6 +37,8 @@ public class PlayerRegisterTransaction extends Transaction {
protected final LongSupplier registered;
private final String playerName;
private Integer userId;
public PlayerRegisterTransaction(UUID playerUUID, LongSupplier registered, String playerName) {
this.playerUUID = playerUUID;
this.registered = registered;
@ -54,12 +57,14 @@ public class PlayerRegisterTransaction extends Transaction {
insertUser(registerDate);
SessionCache.getCachedSession(playerUUID).ifPresent(session -> session.setAsFirstSessionIfMatches(registerDate));
}
execute(DataStoreQueries.updatePlayerName(playerUUID, playerName));
if (!playerUUID.toString().equals(playerName)) {
execute(DataStoreQueries.updatePlayerName(playerUUID, playerName));
}
}
private void insertUser(long registerDate) {
try {
execute(DataStoreQueries.registerBaseUser(playerUUID, registerDate, playerName));
userId = executeReturningId(DataStoreQueries.registerBaseUser(playerUUID, registerDate, playerName));
} catch (DBOpException failed) {
boolean alreadySaved = failed.getMessage().contains("Duplicate entry");
if (!alreadySaved) {
@ -67,4 +72,8 @@ public class PlayerRegisterTransaction extends Transaction {
}
}
}
public Optional<Integer> getUserId() {
return Optional.ofNullable(userId);
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.storage.database.transactions.events;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.gathering.domain.FinishedSession;
import com.djrapitops.plan.storage.database.queries.DataStoreQueries;
import com.djrapitops.plan.storage.database.transactions.Transaction;
@ -41,6 +42,20 @@ public class SessionEndTransaction extends Transaction {
@Override
protected void performOperations() {
try {
execute(DataStoreQueries.storeSession(session));
} catch (DBOpException failed) {
if (failed.isUserIdConstraintViolation()) {
retry();
} else {
throw failed;
}
}
}
private void retry() {
UUID playerUUID = session.getPlayerUUID();
executeOther(new PlayerRegisterTransaction(playerUUID, System::currentTimeMillis, playerUUID.toString()));
execute(DataStoreQueries.storeSession(session));
}
}

View File

@ -26,7 +26,7 @@ import java.util.*;
*/
public class ErrorContext implements Serializable {
private final List<Object> related;
private final transient List<Object> related;
private String whatToDo;
private ErrorContext() {
@ -55,6 +55,10 @@ public class ErrorContext implements Serializable {
if (this.whatToDo == null && context.whatToDo != null) this.whatToDo = context.whatToDo;
}
public List<Object> getRelated() {
return related;
}
public static class Builder {
private final ErrorContext context;

View File

@ -182,8 +182,6 @@ public class PlayerOnlineListener implements FabricListener {
Database database = dbSystem.getDatabase();
database.executeTransaction(new WorldNameStoreTransaction(serverUUID, world));
InetSocketAddress socketAddress = (InetSocketAddress) player.networkHandler.connection.getAddress();
InetAddress address = InetAddresses.forString(socketAddress.getAddress().toString().replace("/", ""));
Supplier<String> getHostName = () -> getHostname(player);
String playerName = player.getEntityName();
@ -195,9 +193,7 @@ public class PlayerOnlineListener implements FabricListener {
.thenRunAsync(() -> {
boolean gatheringGeolocations = config.isTrue(DataGatheringSettings.GEOLOCATIONS);
if (gatheringGeolocations) {
database.executeTransaction(
new GeoInfoStoreTransaction(playerUUID, address, time, geolocationCache::getCountry)
);
gatherGeolocation(player, playerUUID, time, database);
}
database.executeTransaction(new OperatorStatusTransaction(playerUUID, serverUUID, server.getPlayerManager().getOpList().isOp(player.getGameProfile())));
@ -220,6 +216,15 @@ public class PlayerOnlineListener implements FabricListener {
});
}
private void gatherGeolocation(ServerPlayerEntity player, UUID playerUUID, long time, Database database) {
InetSocketAddress socketAddress = (InetSocketAddress) player.networkHandler.connection.getAddress();
if (socketAddress == null) return;
InetAddress address = InetAddresses.forString(socketAddress.getAddress().toString().replace("/", ""));
database.executeTransaction(
new GeoInfoStoreTransaction(playerUUID, address, time, geolocationCache::getCountry)
);
}
private String getHostname(ServerPlayerEntity player) {
return joinAddresses.get(player.getUuid());
}