Skip to content

Commit

Permalink
Seperate the load and save data sources (#7100)
Browse files Browse the repository at this point in the history
* Seperate the load and save data sources

* Properly propagate exceptions during db init
  • Loading branch information
Warriorrrr authored Dec 26, 2023
1 parent 24e2b3b commit b439148
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 79 deletions.
44 changes: 22 additions & 22 deletions Towny/src/main/java/com/palmergames/bukkit/towny/TownyUniverse.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ public class TownyUniverse {

private final Map<WorldCoord, TownyMapData> wildernessMapDataMap = new ConcurrentHashMap<WorldCoord, TownyMapData>();
private final String rootFolder;
private TownyDataSource dataSource;
private TownyDataSource loadDataSource;
private TownyDataSource saveDataSource;
private TownyPermissionSource permissionSource;

private TownyUniverse() {
Expand Down Expand Up @@ -141,21 +142,17 @@ public void clearAllObjects() {
* @param loadDbType - load setting from the config.
* @param saveDbType - save setting from the config.
*/
void loadAndSaveDatabase(String loadDbType, String saveDbType) {
void loadAndSaveDatabase(String loadDbType, String saveDbType) throws TownyInitException {
towny.getLogger().info("Database: [Load] " + loadDbType + " [Save] " + saveDbType);
try {
// Try loading the database.
loadDatabase(loadDbType);
} catch (TownyInitException e) {
throw new TownyInitException(e.getMessage(), e.getError());

loadDatabase(loadDbType);
saveDatabase(saveDbType);

// Dispose of the load data source if it's no longer needed
if (this.loadDataSource != this.saveDataSource) {
this.loadDataSource.finishTasks();
this.loadDataSource = null;
}

try {
// Try saving the database.
saveDatabase(saveDbType);
} catch (TownyInitException e) {
throw new TownyInitException(e.getMessage(), e.getError());
}
}

/**
Expand All @@ -174,22 +171,25 @@ private boolean loadDatabase(String loadDbType) {
switch (loadDbType.toLowerCase(Locale.ROOT)) {
case "ff":
case "flatfile": {
this.dataSource = new TownyFlatFileSource(towny, this);
this.loadDataSource = new TownyFlatFileSource(towny, this);
break;
}
case "mysql": {
this.dataSource = new TownySQLSource(towny, this);
this.loadDataSource = new TownySQLSource(towny, this);
break;
}
default: {
throw new TownyInitException("Database: Database.yml unsupported load format: " + loadDbType, TownyInitException.TownyError.DATABASE_CONFIG);
}
}

// Loading the database can cause saving, so save that back to the load source
this.saveDataSource = this.loadDataSource;

/*
* Load the actual database.
*/
if (!dataSource.loadAll())
if (!loadDataSource.loadAll())
throw new TownyInitException("Database: Failed to load database.", TownyInitException.TownyError.DATABASE);

long time = System.currentTimeMillis() - startTime;
Expand All @@ -215,11 +215,11 @@ private boolean saveDatabase(String saveDbType) {
switch (saveDbType.toLowerCase(Locale.ROOT)) {
case "ff":
case "flatfile": {
this.dataSource = new TownyFlatFileSource(towny, this);
this.saveDataSource = this.loadDataSource instanceof TownyFlatFileSource ? this.loadDataSource : new TownyFlatFileSource(towny, this);
break;
}
case "mysql": {
this.dataSource = new TownySQLSource(towny, this);
this.saveDataSource = this.loadDataSource instanceof TownySQLSource ? this.loadDataSource : new TownySQLSource(towny, this);
break;
}
default: {
Expand All @@ -229,10 +229,10 @@ private boolean saveDatabase(String saveDbType) {

if (TownySettings.getLoadDatabase().equalsIgnoreCase(saveDbType)) {
// Update all Worlds data files
dataSource.saveAllWorlds();
saveDataSource.saveAllWorlds();
} else {
//Formats are different so save ALL data.
dataSource.saveAll();
saveDataSource.saveAll();
}
return true;
} catch (UnsupportedOperationException e) {
Expand Down Expand Up @@ -261,7 +261,7 @@ public void finishTasks() {
*/

public TownyDataSource getDataSource() {
return dataSource;
return saveDataSource;
}

public TownyPermissionSource getPermissionSource() {
Expand Down
25 changes: 17 additions & 8 deletions Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import java.util.Map;

@ApiStatus.Internal
public class SQLTask {
public class SQLTask implements Runnable {
private final TownySQLSource source;

// Update flags this for an insert/update or delete.
public final boolean update;
Expand All @@ -21,9 +22,9 @@ public class SQLTask {
* @param tb_name - Table name.
* @param args - Arguments.
*/
public SQLTask(String tb_name, Map<String, ?> args) {
public SQLTask(TownySQLSource source, String tb_name, Map<String, ?> args) {

this(false, tb_name, args, null);
this(source, false, tb_name, args, null);

}

Expand All @@ -34,19 +35,27 @@ public SQLTask(String tb_name, Map<String, ?> args) {
* @param args - Arguments.
* @param keys - Keys to add to table.
*/
public SQLTask(String tb_name, Map<String, ?> args, List<String> keys) {
public SQLTask(TownySQLSource source, String tb_name, Map<String, ?> args, List<String> keys) {

this(true, tb_name, args, keys);
this(source, true, tb_name, args, keys);

}

private SQLTask(boolean update, String tb_name, Map<String, ?> args, List<String> keys) {

private SQLTask(TownySQLSource source, boolean update, String tb_name, Map<String, ?> args, List<String> keys) {
this.source = source;
this.update = update;
this.tb_name = tb_name;
this.args = args;
this.keys = keys;

}

}
@Override
public void run() {
if (this.update) {
source.queueUpdateDB(this.tb_name, this.args, this.keys);
} else {
source.queueDeleteDB(this.tb_name, this.args);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,14 @@ protected TownyDatabaseHandler(Towny plugin, TownyUniverse universe) {
}

/*
* Start our Async queue for pushing data to the flatfile database.
* Start our async queue for pushing data to the database.
*/
task = plugin.getScheduler().runAsyncRepeating(() -> {
while (!this.queryQueue.isEmpty()) {
Runnable operation = this.queryQueue.poll();
operation.run();
synchronized(queryQueue) {
while (!this.queryQueue.isEmpty()) {
Runnable operation = this.queryQueue.poll();
operation.run();
}
}
}, 5L, 5L);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.palmergames.bukkit.towny.object.WorldCoord;
import com.palmergames.bukkit.towny.object.metadata.MetadataLoader;
import com.palmergames.bukkit.towny.object.jail.Jail;
import com.palmergames.bukkit.towny.scheduling.ScheduledTask;
import com.palmergames.bukkit.towny.tasks.CooldownTimerTask;
import com.palmergames.bukkit.towny.utils.MapUtil;
import com.palmergames.bukkit.util.BukkitTools;
Expand Down Expand Up @@ -57,19 +56,13 @@
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.stream.Collectors;

public final class TownySQLSource extends TownyDatabaseHandler {

private final Queue<SQLTask> queryQueue = new ConcurrentLinkedQueue<>();
private boolean isPolling = false;
private final ScheduledTask task;

private final String tb_prefix;

private final HikariDataSource hikariDataSource;
Expand Down Expand Up @@ -140,47 +133,12 @@ public TownySQLSource(Towny plugin, TownyUniverse universe) {
} catch (SQLException e) {
logger.error("Failed to connect to the database", e);
}

/*
* Start our Async queue for pushing data to the database.
*/
task = plugin.getScheduler().runAsyncRepeating(() -> {
if (this.isPolling)
return;

this.isPolling = true;
try {
SQLTask query;
while ((query = this.queryQueue.poll()) != null) {
if (query.update) {
TownySQLSource.this.queueUpdateDB(query.tb_name, query.args, query.keys);
} else {
TownySQLSource.this.queueDeleteDB(query.tb_name, query.args);
}
}
} finally {
this.isPolling = false;
}

}, 5L, 5L);
}

@Override
public void finishTasks() {
// Cancel the repeating task as its not needed anymore.
if (task != null)
task.cancel();

// Make sure that *all* tasks are saved before shutting down.
while (!queryQueue.isEmpty()) {
SQLTask query = TownySQLSource.this.queryQueue.poll();
super.finishTasks();

if (query.update) {
TownySQLSource.this.queueUpdateDB(query.tb_name, query.args, query.keys);
} else {
TownySQLSource.this.queueDeleteDB(query.tb_name, query.args);
}
}
// Close the database sources on shutdown to get GC
if (hikariDataSource != null)
hikariDataSource.close();
Expand Down Expand Up @@ -232,7 +190,7 @@ public boolean updateDB(String tb_name, Map<String, ?> args, List<String> keys)
* Make sure we only execute queries in async
*/

this.queryQueue.add(new SQLTask(tb_name, args, keys));
this.queryQueue.add(new SQLTask(this, tb_name, args, keys));

return true;

Expand Down Expand Up @@ -398,7 +356,7 @@ public boolean DeleteDB(String tb_name, HashMap<String, Object> args) {

// Make sure we only execute queries in async

this.queryQueue.add(new SQLTask(tb_name, args));
this.queryQueue.add(new SQLTask(this, tb_name, args));

return true;

Expand Down

0 comments on commit b439148

Please sign in to comment.