|
@@ -1,39 +1,39 @@
|
|
import com.zaxxer.hikari.HikariConfig
|
|
import com.zaxxer.hikari.HikariConfig
|
|
import com.zaxxer.hikari.HikariDataSource
|
|
import com.zaxxer.hikari.HikariDataSource
|
|
-import org.http4k.format.Argo.number
|
|
|
|
-import org.http4k.format.Argo.obj
|
|
|
|
import java.sql.Connection
|
|
import java.sql.Connection
|
|
-import java.sql.PreparedStatement
|
|
|
|
import java.sql.ResultSet
|
|
import java.sql.ResultSet
|
|
|
|
+import java.util.Random
|
|
import javax.sql.DataSource
|
|
import javax.sql.DataSource
|
|
|
|
|
|
class PostgresDatabase private constructor(private val dataSource: DataSource) : Database {
|
|
class PostgresDatabase private constructor(private val dataSource: DataSource) : Database {
|
|
|
|
+ private val random = Random()
|
|
|
|
|
|
- override fun findWorld() = withConnection { findWorld(randomWorld()) }
|
|
|
|
|
|
+ override fun findWorld() = withConnection { findWorld(random.world()) }
|
|
|
|
|
|
- override fun loadAll() = withConnection { findAll() }
|
|
|
|
|
|
+ override fun loadAll() = withConnection {
|
|
|
|
+ executeQuery("SELECT id, randomNumber FROM world") { it.toResultsList(::toWorld) }
|
|
|
|
+ }
|
|
|
|
|
|
override fun findWorlds(count: Int) = withConnection {
|
|
override fun findWorlds(count: Int) = withConnection {
|
|
- (1..count).map { findWorld(randomWorld()) }
|
|
|
|
|
|
+ (1..count).map { findWorld(random.world()) }
|
|
}
|
|
}
|
|
|
|
|
|
override fun updateWorlds(count: Int) = withConnection {
|
|
override fun updateWorlds(count: Int) = withConnection {
|
|
- (1..count).map {
|
|
|
|
- val id = randomWorld()
|
|
|
|
- updateWorld(id)
|
|
|
|
- findWorld(id)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ val updatedAndSorted = (1..count)
|
|
|
|
+ .map { findWorld(random.world()).first to random.world() }
|
|
|
|
+ .sortedBy { it.first }
|
|
|
|
|
|
- private fun Connection.updateWorld(id: Int) = withStatement("UPDATE world SET randomNumber = ? WHERE id = ?") {
|
|
|
|
- setInt(1, randomWorld())
|
|
|
|
- setInt(2, id)
|
|
|
|
- executeUpdate()
|
|
|
|
|
|
+ createStatement().use { stmt ->
|
|
|
|
+ updatedAndSorted.forEach {
|
|
|
|
+ stmt.addBatch("UPDATE world SET randomNumber = ${it.second} WHERE id = ${it.first}")
|
|
|
|
+ }
|
|
|
|
+ stmt.executeBatch()
|
|
|
|
+ }
|
|
|
|
+ updatedAndSorted
|
|
}
|
|
}
|
|
|
|
|
|
override fun fortunes() = withConnection {
|
|
override fun fortunes() = withConnection {
|
|
- val original =
|
|
|
|
- withStatement("select * from fortune") { executeQuery().toResultsList { Fortune(getInt(1), getString(2)) } }
|
|
|
|
|
|
+ val original = executeQuery("select * from fortune") { it.toResultsList(::toFortune) }
|
|
(original + Fortune(0, "Additional fortune added at request time.")).sortedBy { it.message }
|
|
(original + Fortune(0, "Additional fortune added at request time.")).sortedBy { it.message }
|
|
}
|
|
}
|
|
|
|
|
|
@@ -43,52 +43,42 @@ class PostgresDatabase private constructor(private val dataSource: DataSource) :
|
|
username = "benchmarkdbuser"
|
|
username = "benchmarkdbuser"
|
|
password = "benchmarkdbpass"
|
|
password = "benchmarkdbpass"
|
|
jdbcUrl = "jdbc:postgresql://tfb-database:5432/hello_world?" +
|
|
jdbcUrl = "jdbc:postgresql://tfb-database:5432/hello_world?" +
|
|
- "useSSL=false&" +
|
|
|
|
- "jdbcCompliantTruncation=false&" +
|
|
|
|
- "elideSetAutoCommits=true&" +
|
|
|
|
- "useLocalSessionState=true&" +
|
|
|
|
- "cachePrepStmts=true&" +
|
|
|
|
- "cacheCallableStmts=true&" +
|
|
|
|
- "alwaysSendSetIsolation=false&" +
|
|
|
|
- "prepStmtCacheSize=4096&" +
|
|
|
|
- "cacheServerConfiguration=true&" +
|
|
|
|
- "prepStmtCacheSqlLimit=2048&" +
|
|
|
|
- "traceProtocol=false&" +
|
|
|
|
- "useUnbufferedInput=false&" +
|
|
|
|
- "useReadAheadInput=false&" +
|
|
|
|
- "maintainTimeStats=false&" +
|
|
|
|
- "useServerPrepStmts=true&" +
|
|
|
|
- "cacheRSMetadata=true"
|
|
|
|
|
|
+ "useSSL=false&" +
|
|
|
|
+ "jdbcCompliantTruncation=false&" +
|
|
|
|
+ "elideSetAutoCommits=true&" +
|
|
|
|
+ "useLocalSessionState=true&" +
|
|
|
|
+ "cachePrepStmts=true&" +
|
|
|
|
+ "cacheCallableStmts=true&" +
|
|
|
|
+ "alwaysSendSetIsolation=false&" +
|
|
|
|
+ "prepStmtCacheSize=4096&" +
|
|
|
|
+ "cacheServerConfiguration=true&" +
|
|
|
|
+ "prepStmtCacheSqlLimit=2048&" +
|
|
|
|
+ "traceProtocol=false&" +
|
|
|
|
+ "useUnbufferedInput=false&" +
|
|
|
|
+ "useReadAheadInput=false&" +
|
|
|
|
+ "maintainTimeStats=false&" +
|
|
|
|
+ "useServerPrepStmts=true&" +
|
|
|
|
+ "cacheRSMetadata=true"
|
|
maximumPoolSize = 100
|
|
maximumPoolSize = 100
|
|
HikariDataSource(this)
|
|
HikariDataSource(this)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
private inline fun <T> withConnection(fn: Connection.() -> T): T = dataSource.connection.use(fn)
|
|
private inline fun <T> withConnection(fn: Connection.() -> T): T = dataSource.connection.use(fn)
|
|
|
|
+}
|
|
|
|
|
|
- private inline fun <T> Connection.withStatement(stmt: String, fn: PreparedStatement.() -> T): T =
|
|
|
|
- prepareStatement(stmt).use(fn)
|
|
|
|
|
|
+private fun Connection.findWorld(id: Int) = prepareStatement("SELECT id, randomNumber FROM world WHERE id = ?").use {
|
|
|
|
+ toWorld(it.apply { setInt(1, id) }.executeQuery().also { it.next() })
|
|
|
|
+}
|
|
|
|
|
|
- private fun Connection.findWorld(id: Int) =
|
|
|
|
- withStatement("SELECT id, randomNumber FROM world WHERE id = ?") {
|
|
|
|
- setInt(1, id)
|
|
|
|
- executeQuery().toResultsList {
|
|
|
|
- obj("id" to number(getInt("id")), "randomNumber" to number(getInt("randomNumber")))
|
|
|
|
- }.first()
|
|
|
|
- }
|
|
|
|
|
|
+private inline fun <T> ResultSet.toResultsList(fn: (ResultSet) -> T): List<T> =
|
|
|
|
+ mutableListOf<T>().apply {
|
|
|
|
+ while (next()) add(fn(this@toResultsList))
|
|
|
|
+ }
|
|
|
|
|
|
- private fun Connection.findAll() =
|
|
|
|
- withStatement("SELECT id, randomNumber FROM world") {
|
|
|
|
- executeQuery().toResultsList {
|
|
|
|
- val id = getInt("id")
|
|
|
|
- id to obj("id" to number(id), "randomNumber" to number(getInt("randomNumber")))
|
|
|
|
- }.toMap()
|
|
|
|
- }
|
|
|
|
|
|
+private inline fun <T> Connection.executeQuery(stmt: String, fn: (ResultSet) -> T): T =
|
|
|
|
+ prepareStatement(stmt).use { fn(it.executeQuery()) }
|
|
|
|
|
|
- private inline fun <T> ResultSet.toResultsList(fn: ResultSet.() -> T): List<T> =
|
|
|
|
- mutableListOf<T>().apply {
|
|
|
|
- while (next()) {
|
|
|
|
- add(fn(this@toResultsList))
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+private fun toFortune(it: ResultSet) = Fortune(it.getInt(1), it.getString(2))
|
|
|
|
+
|
|
|
|
+private fun toWorld(resultSet: ResultSet) = resultSet.getInt("id") to resultSet.getInt("randomNumber")
|