Skip to content

Commit

Permalink
Rewrote database management. While still not using CivModCores handle…
Browse files Browse the repository at this point in the history
…rs, this is first step in achieving that if we want. Would be a quick change to move to it. Includes removal of all extraneous prepared statements and obsessive reconnection handling, consolidates on a single Database class that owns the prepared strings, statements made on demand and let the connection handler deal with caching. Also, instead of janky write and read channels, using a modern connection pool (HikariCP). Also let me fix the chunk on unload commit and batching. And remove the weird keepalive nonsense. Anyway, initial tests look good, going into production tomorrow.
  • Loading branch information
ProgrammerDan committed Jul 28, 2017
1 parent b6d94cf commit fec29fa
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 393 deletions.
1 change: 1 addition & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ realistic_biomes:
database_user: "tekkit"
database_password: ""
database_prefix: "rb"
database_poolsize: 10

cache_entire_database: true
persistence_enabled: true
Expand Down
33 changes: 32 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>com.untamedears</groupId>
<artifactId>RealisticBiomes</artifactId>
<packaging>jar</packaging>
<version>1.3.1</version>
<version>1.3.2</version>
<name>RealisticBiomes</name>
<url>https://github.com/Civcraft/RealisticBiomes</url>

Expand All @@ -29,6 +29,31 @@
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<filters>
<filter>
<artifact>com.zaxxer:HikariCP</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<dependencies>
Expand All @@ -38,6 +63,12 @@
<version>1.10.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
</dependency>
</dependencies>

<repositories>
Expand Down
2 changes: 2 additions & 0 deletions src/com/untamedears/realisticbiomes/PersistConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class PersistConfig {
public String user;
public String password;
public String prefix;

public int poolSize;

public boolean enabled;

Expand Down
18 changes: 18 additions & 0 deletions src/com/untamedears/realisticbiomes/RealisticBiomes.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.untamedears.realisticbiomes;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -128,6 +129,21 @@ public static void doLog(Level level, String message) {
}
}
}


public static void doLog(Level level, String message, Throwable wrong) {

if (RealisticBiomes.LOG != null) {

// here we make sure that we only log messages that are loggable with the given Level
// so if its set to INFO (800) and we try to log a FINER message (400), then it wont work
// However if its ALL, then its set to Integer.MIN_VALUE, so everything will get logged. etc etc
if (level.intValue() >= RealisticBiomes.minLogLevel.intValue() ) {
RealisticBiomes.LOG.log(level, "[" + level.toString() + "] " + message, wrong);

}
}
}

private void loadPersistConfig(ConfigurationSection config) {
persistConfig = new PersistConfig();
Expand All @@ -138,6 +154,7 @@ private void loadPersistConfig(ConfigurationSection config) {
persistConfig.user = config.getString("database_user");
persistConfig.password = config.getString("database_password");
persistConfig.prefix = config.getString("database_prefix");
persistConfig.poolSize = config.getInt("database_poolsize", 10);

persistConfig.enabled = config.getBoolean("persistence_enabled");
persistConfig.unloadBatchPeriod = config.getInt("unload_batch_period");
Expand Down Expand Up @@ -464,4 +481,5 @@ public void growPlant(Plant plant, Block block, GrowthConfig growthConfig, Block
public PlantManager getPlantManager() {
return plantManager;
}

}
61 changes: 0 additions & 61 deletions src/com/untamedears/realisticbiomes/persist/ChunkWriter.java

This file was deleted.

156 changes: 156 additions & 0 deletions src/com/untamedears/realisticbiomes/persist/Database.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package com.untamedears.realisticbiomes.persist;

import com.zaxxer.hikari.HikariDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Level;

import com.untamedears.realisticbiomes.PersistConfig;
import com.untamedears.realisticbiomes.RealisticBiomes;
import com.zaxxer.hikari.HikariConfig;

/**
* Wrapper for Connection Pool, and holder for instance-static strings.
*
* @author ProgrammerDan
*/
public class Database {

private HikariDataSource datasource;

public Database(PersistConfig baseconfig) {

initStatements(baseconfig);

if (baseconfig.user != null && baseconfig.host != null &&
baseconfig.port != null && baseconfig.databaseName != null) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://" + baseconfig.host + ":" + baseconfig.port + "/" + baseconfig.databaseName);
// TODO make these config'd
config.setConnectionTimeout(1000l);
config.setIdleTimeout(600000l);
config.setMaxLifetime(7200000l);
// END TODO
config.setMaximumPoolSize(baseconfig.poolSize);
config.setUsername(baseconfig.user);
if (baseconfig.password != null) {
config.setPassword(baseconfig.password);
}
this.datasource = new HikariDataSource(config);

RealisticBiomes.doLog(Level.FINER, "creating chunk table (if necessary) with prepared statement:" + Database.makeTableChunk);

try (Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement(Database.makeTableChunk);) {
statement.execute();
statement.close();
} catch (SQLException se) {
RealisticBiomes.doLog(Level.SEVERE, "Unable to initialize chunk table in Database!", se);
this.datasource = null;
return;
}

try (Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement(Database.makeTablePlant);) {
statement.execute();
} catch (SQLException se) {
RealisticBiomes.doLog(Level.SEVERE, "Unable to initialize plant table in Database!", se);
this.datasource = null;
return;
}

/* MIGRATIONS */

// update database schema: try and catch
try (Connection connection = getConnection();
PreparedStatement upgradeTablePlant = connection.prepareStatement(Database.migration0001);){
upgradeTablePlant.execute();
} catch (SQLException e) {
RealisticBiomes.LOG.info("Could not update table - ignore if already updated. Error code: " + e.getErrorCode() + ", error message: " + e.getMessage());
}

} else {
this.datasource = null;
RealisticBiomes.doLog(Level.SEVERE, "Database not configured and is unavaiable");
}
}

/**
* Gets a single connection from the pool for use. Checks for null database first.
*
* @return A new Connection
* @throws SQLException
*/
public Connection getConnection() throws SQLException {
available();
return this.datasource.getConnection();
}

/**
* Closes all connections and this connection pool.
* @throws SQLException
*/
public void close() throws SQLException {
available();
this.datasource.close();
}

/**
* Quick test; either ends or throws an exception if data source isn't configured.
* @throws SQLException
*/
public void available() throws SQLException {
if (this.datasource == null) {
throw new SQLException("No Datasource Available");
}
}

public static String deleteOldDataStmt = null;
public static String deleteChunkStmt = null;
public static String addChunkStmt = null;
public static String updatePlantStmt = null;
public static String addPlantStmt = null;
public static String deleteOldPlantsStmt = null;
public static String loadPlantsStmt = null;
public static String makeTableChunk = null;
public static String makeTablePlant = null;
public static String selectAllFromChunk = null;

// MIGRATIONS -- TODO: convert to civmodcore migrations
public static String migration0001 = null;


private void initStatements(PersistConfig config) {

deleteOldDataStmt = String.format("DELETE FROM %s_plant WHERE chunkid = ?", config.prefix);

addChunkStmt = String.format("INSERT INTO %s_chunk (w, x, z) VALUES (?, ?, ?)", config.prefix);

addPlantStmt = String.format("INSERT INTO %s_plant (chunkid, w, x, y, z, date, growth, fruitGrowth) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", config.prefix);

// don't need for now,...maybe later?
//updatePlantStmt = String.format("UPDATE %s_plant SET date = ?, growth = ? where chunkid = ?", config.prefix);
deleteOldPlantsStmt = String.format("DELETE FROM %s_plant WHERE chunkid = ?", config.prefix);

loadPlantsStmt = String.format("SELECT w, x, y, z, date, growth, fruitGrowth FROM %s_plant WHERE chunkid = ?", config.prefix);

makeTableChunk = String.format("CREATE TABLE IF NOT EXISTS %s_chunk " +
"(id BIGINT PRIMARY KEY AUTO_INCREMENT, " +
"w INTEGER, x INTEGER, z INTEGER," +
"INDEX chunk_coords_idx (w, x, z)) " +
"ENGINE INNODB", config.prefix);

// we need InnoDB storage engine or else we can't do foreign keys!
makeTablePlant = String.format("CREATE TABLE IF NOT EXISTS %s_plant" +
"(chunkId BIGINT, w INTEGER, x INTEGER, y INTEGER, z INTEGER, date INTEGER UNSIGNED, growth REAL, fruitGrowth REAL, " +
"INDEX plant_chunk_idx (chunkId), " +
"CONSTRAINT chunkIdConstraint FOREIGN KEY (chunkId) REFERENCES %s_chunk (id))" +
"ENGINE INNODB", config.prefix, config.prefix);

selectAllFromChunk = String.format("SELECT id, w, x, z FROM %s_chunk", config.prefix);

migration0001 = String.format("ALTER TABLE %s_plant ADD fruitGrowth REAL AFTER growth", config.prefix);
}
}
Loading

0 comments on commit fec29fa

Please sign in to comment.