server: database improves:

- fixed broken database in some use cases (example: AI and choose name dialog, related to #11285);
- added docs and debug tools for sql queries, caches and memory analyse (see DebugUtil);
- refactor code to use shared settings;
- deleted outdated and un-used code (db logs, stats, etc);
This commit is contained in:
Oleg Agafonov 2024-07-18 21:22:10 +04:00
parent c448612c97
commit bf3f26ccc1
18 changed files with 376 additions and 394 deletions

View file

@ -10,6 +10,7 @@ import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.table.TableUtils;
import mage.cards.repository.CardRepository;
import mage.cards.repository.DatabaseUtils;
import mage.cards.repository.RepositoryUtil;
import org.apache.log4j.Logger;
import org.apache.shiro.crypto.RandomNumberGenerator;
@ -24,7 +25,6 @@ import java.util.List;
public class AuthorizedUserRepository {
private static final String JDBC_URL = "jdbc:h2:file:./db/authorized_user.h2;AUTO_SERVER=TRUE";
private static final String VERSION_ENTITY_NAME = "authorized_user";
// raise this if db structure was changed
private static final long DB_VERSION = 2;
@ -32,10 +32,10 @@ public class AuthorizedUserRepository {
private static final AuthorizedUserRepository instance;
static {
instance = new AuthorizedUserRepository(JDBC_URL);
instance = new AuthorizedUserRepository(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_USERS, false));
}
private Dao<AuthorizedUser, Object> dao;
private Dao<AuthorizedUser, Object> usersDao;
public AuthorizedUserRepository(String connectionString) {
File file = new File("db");
@ -45,7 +45,7 @@ public class AuthorizedUserRepository {
try {
ConnectionSource connectionSource = new JdbcConnectionSource(connectionString);
TableUtils.createTableIfNotExists(connectionSource, AuthorizedUser.class);
dao = DaoManager.createDao(connectionSource, AuthorizedUser.class);
usersDao = DaoManager.createDao(connectionSource, AuthorizedUser.class);
} catch (SQLException ex) {
Logger.getLogger(AuthorizedUserRepository.class).error("Error creating / assigning authorized_user repository - ", ex);
}
@ -59,7 +59,7 @@ public class AuthorizedUserRepository {
try {
Hash hash = new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, rng.nextBytes(), 1024);
AuthorizedUser user = new AuthorizedUser(userName, hash, email);
dao.create(user);
usersDao.create(user);
} catch (SQLException ex) {
Logger.getLogger(AuthorizedUserRepository.class).error("Error adding a user to DB - ", ex);
}
@ -67,7 +67,7 @@ public class AuthorizedUserRepository {
public void remove(final String userName) {
try {
DeleteBuilder<AuthorizedUser, Object> db = dao.deleteBuilder();
DeleteBuilder<AuthorizedUser, Object> db = usersDao.deleteBuilder();
db.where().eq("name", new SelectArg(userName));
db.delete();
} catch (SQLException ex) {
@ -77,9 +77,9 @@ public class AuthorizedUserRepository {
public AuthorizedUser getByName(String userName) {
try {
QueryBuilder<AuthorizedUser, Object> qb = dao.queryBuilder();
QueryBuilder<AuthorizedUser, Object> qb = usersDao.queryBuilder();
qb.where().eq("name", new SelectArg(userName));
List<AuthorizedUser> results = dao.query(qb.prepare());
List<AuthorizedUser> results = usersDao.query(qb.prepare());
if (results.size() == 1) {
return results.get(0);
}
@ -92,7 +92,7 @@ public class AuthorizedUserRepository {
public void update(AuthorizedUser authorizedUser) {
try {
dao.update(authorizedUser);
usersDao.update(authorizedUser);
} catch (SQLException ex) {
Logger.getLogger(AuthorizedUserRepository.class).error("Error updating authorized_user", ex);
}
@ -100,9 +100,9 @@ public class AuthorizedUserRepository {
public AuthorizedUser getByEmail(String userName) {
try {
QueryBuilder<AuthorizedUser, Object> qb = dao.queryBuilder();
QueryBuilder<AuthorizedUser, Object> qb = usersDao.queryBuilder();
qb.where().eq("email", new SelectArg(userName));
List<AuthorizedUser> results = dao.query(qb.prepare());
List<AuthorizedUser> results = usersDao.query(qb.prepare());
if (results.size() == 1) {
return results.get(0);
}
@ -115,9 +115,10 @@ public class AuthorizedUserRepository {
public void closeDB() {
try {
if (dao != null && dao.getConnectionSource() != null) {
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection(dao.getTableName());
conn.executeStatement("shutdown compact", 0);
if (usersDao != null && usersDao.getConnectionSource() != null) {
DatabaseConnection conn = usersDao.getConnectionSource().getReadWriteConnection(usersDao.getTableName());
conn.executeStatement("SHUTDOWN IMMEDIATELY", 0);
usersDao.getConnectionSource().releaseConnection(conn);
}
} catch (SQLException ex) {
Logger.getLogger(AuthorizedUserRepository.class).error("Error closing authorized_user repository - ", ex);
@ -126,7 +127,7 @@ public class AuthorizedUserRepository {
public long getDBVersionFromDB() {
try {
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_USERS, false));
return RepositoryUtil.getDatabaseVersion(connectionSource, VERSION_ENTITY_NAME);
} catch (SQLException ex) {
Logger.getLogger(CardRepository.class).error("Error getting DB version from DB - ", ex);
@ -145,11 +146,11 @@ public class AuthorizedUserRepository {
private boolean migrateFrom1To2() {
try {
Logger.getLogger(AuthorizedUserRepository.class).info("Starting " + VERSION_ENTITY_NAME + " DB migration from version 1 to version 2");
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN active BOOLEAN DEFAULT true;");
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lockedUntil DATETIME;");
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN chatLockedUntil DATETIME;");
dao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lastConnection DATETIME;");
RepositoryUtil.updateVersion(dao.getConnectionSource(), VERSION_ENTITY_NAME, DB_VERSION);
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN active BOOLEAN DEFAULT true;");
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lockedUntil DATETIME;");
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN chatLockedUntil DATETIME;");
usersDao.executeRaw("ALTER TABLE authorized_user ADD COLUMN lastConnection DATETIME;");
RepositoryUtil.updateVersion(usersDao.getConnectionSource(), VERSION_ENTITY_NAME, DB_VERSION);
Logger.getLogger(AuthorizedUserRepository.class).info("Migration finished.");
return true;
} catch (SQLException ex) {

View file

@ -8,6 +8,7 @@ import com.j256.ormlite.stmt.SelectArg;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.table.TableUtils;
import mage.cards.repository.DatabaseUtils;
import mage.cards.repository.RepositoryUtil;
import org.apache.log4j.Logger;
@ -20,12 +21,11 @@ public enum TableRecordRepository {
instance;
private static final String JDBC_URL = "jdbc:sqlite:./db/table_record.db";
private static final String VERSION_ENTITY_NAME = "table_record";
// raise this if db structure was changed
private static final long DB_VERSION = 0;
private Dao<TableRecord, Object> dao;
private Dao<TableRecord, Object> recordsDao;
TableRecordRepository() {
File file = new File("db");
@ -33,7 +33,7 @@ public enum TableRecordRepository {
file.mkdirs();
}
try {
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareSqliteConnection(DatabaseUtils.DB_NAME_RECORDS));
boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, DB_VERSION);
if (obsolete) {
@ -41,7 +41,7 @@ public enum TableRecordRepository {
}
TableUtils.createTableIfNotExists(connectionSource, TableRecord.class);
dao = DaoManager.createDao(connectionSource, TableRecord.class);
recordsDao = DaoManager.createDao(connectionSource, TableRecord.class);
} catch (SQLException ex) {
Logger.getLogger(TableRecordRepository.class).error("Error creating table_record repository - ", ex);
}
@ -49,7 +49,7 @@ public enum TableRecordRepository {
public void add(TableRecord tableHistory) {
try {
dao.create(tableHistory);
recordsDao.create(tableHistory);
} catch (SQLException ex) {
Logger.getLogger(TableRecordRepository.class).error("Error adding a table_record to DB - ", ex);
}
@ -57,10 +57,10 @@ public enum TableRecordRepository {
public List<TableRecord> getAfter(long endTimeMs) {
try {
QueryBuilder<TableRecord, Object> qb = dao.queryBuilder();
QueryBuilder<TableRecord, Object> qb = recordsDao.queryBuilder();
qb.where().gt("endTimeMs", new SelectArg(endTimeMs));
qb.orderBy("endTimeMs", true);
return dao.query(qb.prepare());
return recordsDao.query(qb.prepare());
} catch (SQLException ex) {
Logger.getLogger(TableRecordRepository.class).error("Error getting table_records from DB - ", ex);
}
@ -69,9 +69,10 @@ public enum TableRecordRepository {
public void closeDB() {
try {
if (dao != null && dao.getConnectionSource() != null) {
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection(dao.getTableName());
conn.executeStatement("shutdown compact", 0);
if (recordsDao != null && recordsDao.getConnectionSource() != null) {
DatabaseConnection conn = recordsDao.getConnectionSource().getReadWriteConnection(recordsDao.getTableName());
conn.executeStatement("SHUTDOWN IMMEDIATELY", 0);
recordsDao.getConnectionSource().releaseConnection(conn);
}
} catch (SQLException ex) {
Logger.getLogger(TableRecordRepository.class).error("Error closing table_record repository - ", ex);

View file

@ -8,6 +8,7 @@ import com.j256.ormlite.stmt.SelectArg;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.table.TableUtils;
import mage.cards.repository.DatabaseUtils;
import mage.cards.repository.RepositoryUtil;
import mage.game.result.ResultProtos;
import mage.server.rating.GlickoRating;
@ -22,12 +23,11 @@ public enum UserStatsRepository {
instance;
private static final String JDBC_URL = "jdbc:sqlite:./db/user_stats.db";
private static final String VERSION_ENTITY_NAME = "user_stats";
// raise this if db structure was changed
private static final long DB_VERSION = 0;
private Dao<UserStats, Object> dao;
private Dao<UserStats, Object> statsDao;
UserStatsRepository() {
File file = new File("db");
@ -35,7 +35,7 @@ public enum UserStatsRepository {
file.mkdirs();
}
try {
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareSqliteConnection(DatabaseUtils.DB_NAME_STATS));
boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, DB_VERSION);
if (obsolete) {
@ -43,7 +43,7 @@ public enum UserStatsRepository {
}
TableUtils.createTableIfNotExists(connectionSource, UserStats.class);
dao = DaoManager.createDao(connectionSource, UserStats.class);
statsDao = DaoManager.createDao(connectionSource, UserStats.class);
} catch (SQLException ex) {
Logger.getLogger(UserStatsRepository.class).error("Error creating user_stats repository - ", ex);
}
@ -51,7 +51,7 @@ public enum UserStatsRepository {
public void add(UserStats userStats) {
try {
dao.create(userStats);
statsDao.create(userStats);
} catch (SQLException ex) {
Logger.getLogger(UserStatsRepository.class).error("Error adding a user_stats to DB - ", ex);
}
@ -59,7 +59,7 @@ public enum UserStatsRepository {
public void update(UserStats userStats) {
try {
dao.update(userStats);
statsDao.update(userStats);
} catch (SQLException ex) {
Logger.getLogger(UserStatsRepository.class).error("Error updating a user_stats in DB - ", ex);
}
@ -67,9 +67,9 @@ public enum UserStatsRepository {
public UserStats getUser(String userName) {
try {
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
QueryBuilder<UserStats, Object> qb = statsDao.queryBuilder();
qb.limit(1L).where().eq("userName", new SelectArg(userName));
List<UserStats> users = dao.query(qb.prepare());
List<UserStats> users = statsDao.query(qb.prepare());
if (!users.isEmpty()) {
return users.get(0);
}
@ -81,8 +81,8 @@ public enum UserStatsRepository {
public List<UserStats> getAllUsers() {
try {
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
return dao.query(qb.prepare());
QueryBuilder<UserStats, Object> qb = statsDao.queryBuilder();
return statsDao.query(qb.prepare());
} catch (SQLException ex) {
Logger.getLogger(UserStatsRepository.class).error("Error getting all users from DB - ", ex);
}
@ -91,9 +91,9 @@ public enum UserStatsRepository {
public long getLatestEndTimeMs() {
try {
QueryBuilder<UserStats, Object> qb = dao.queryBuilder();
QueryBuilder<UserStats, Object> qb = statsDao.queryBuilder();
qb.orderBy("endTimeMs", false).limit(1L);
List<UserStats> users = dao.query(qb.prepare());
List<UserStats> users = statsDao.query(qb.prepare());
if (!users.isEmpty()) {
return users.get(0).getEndTimeMs();
}
@ -366,9 +366,10 @@ public enum UserStatsRepository {
public void closeDB() {
try {
if (dao != null && dao.getConnectionSource() != null) {
DatabaseConnection conn = dao.getConnectionSource().getReadWriteConnection(dao.getTableName());
conn.executeStatement("shutdown compact", 0);
if (statsDao != null && statsDao.getConnectionSource() != null) {
DatabaseConnection conn = statsDao.getConnectionSource().getReadWriteConnection(statsDao.getTableName());
conn.executeStatement("SHUTDOWN IMMEDIATELY", 0);
statsDao.getConnectionSource().releaseConnection(conn);
}
} catch (SQLException ex) {
Logger.getLogger(UserStatsRepository.class).error("Error closing user_stats repository - ", ex);