|
@@ -9,14 +9,12 @@ import org.http4k.format.Jackson.number
|
|
|
import org.http4k.format.Jackson.obj
|
|
|
import java.sql.Connection
|
|
|
import java.sql.PreparedStatement
|
|
|
-import java.sql.ResultSet
|
|
|
-import java.util.*
|
|
|
+import java.util.Random
|
|
|
import java.util.concurrent.CompletableFuture
|
|
|
import javax.sql.DataSource
|
|
|
|
|
|
-
|
|
|
interface Database {
|
|
|
- fun findWorld(): JsonNode?
|
|
|
+ fun findWorld(): JsonNode
|
|
|
fun findWorlds(count: Int): List<JsonNode>
|
|
|
fun updateWorlds(count: Int): List<JsonNode>
|
|
|
fun fortunes(): List<Fortune>
|
|
@@ -29,11 +27,11 @@ class PostgresDatabase private constructor(private val dataSource: DataSource) :
|
|
|
}
|
|
|
|
|
|
override fun findWorlds(count: Int) = withConnection {
|
|
|
- (1..count).mapNotNull { findWorld(randomWorld()) }
|
|
|
+ (1..count).map { findWorld(randomWorld()) }
|
|
|
}
|
|
|
|
|
|
override fun updateWorlds(count: Int) = withConnection {
|
|
|
- (1..count).mapNotNull {
|
|
|
+ (1..count).map {
|
|
|
val id = randomWorld()
|
|
|
updateWorld(id)
|
|
|
findWorld(id)
|
|
@@ -47,7 +45,15 @@ class PostgresDatabase private constructor(private val dataSource: DataSource) :
|
|
|
}
|
|
|
|
|
|
override fun fortunes() = withConnection {
|
|
|
- val original = withStatement("select * from fortune") { executeQuery().toResultsList { Fortune(getInt(1), getString(2)) } }
|
|
|
+ val original = withStatement("select * from fortune") {
|
|
|
+ with(executeQuery()) {
|
|
|
+ mutableListOf<Fortune>().apply {
|
|
|
+ while (next()) {
|
|
|
+ add(Fortune(getInt(1), getString(2)))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
(original + Fortune(0, "Additional fortune added at request time.")).sortedBy { it.message }
|
|
|
}
|
|
|
|
|
@@ -57,49 +63,41 @@ class PostgresDatabase private constructor(private val dataSource: DataSource) :
|
|
|
username = "benchmarkdbuser"
|
|
|
password = "benchmarkdbpass"
|
|
|
jdbcUrl = "jdbc:postgresql://$host: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"
|
|
|
- maximumPoolSize = 100
|
|
|
+ "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 = Runtime.getRuntime().availableProcessors() * 2
|
|
|
HikariDataSource(this)
|
|
|
}
|
|
|
return PostgresDatabase(dataSource)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private 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 fun <T> Connection.withStatement(stmt: String, fn: PreparedStatement.() -> T): T = prepareStatement(stmt).use(fn)
|
|
|
+ private inline fun <T> Connection.withStatement(stmt: String, fn: PreparedStatement.() -> T): T = prepareStatement(stmt).use(fn)
|
|
|
|
|
|
private fun Connection.findWorld(id: Int) =
|
|
|
- withStatement("SELECT * FROM world WHERE id = ?") {
|
|
|
- setInt(1, id)
|
|
|
- executeQuery().toResultsList {
|
|
|
- obj("id" to number(getInt("id")), "randomNumber" to number(getInt("randomNumber")))
|
|
|
- }.firstOrNull()
|
|
|
- }
|
|
|
-
|
|
|
- private fun <T> ResultSet.toResultsList(fn: ResultSet.() -> T): List<T> =
|
|
|
- use {
|
|
|
- mutableListOf<T>().apply {
|
|
|
- while (next()) {
|
|
|
- add(fn(this@toResultsList))
|
|
|
- }
|
|
|
- }
|
|
|
+ withStatement("SELECT * FROM world WHERE id = ?") {
|
|
|
+ setInt(1, id)
|
|
|
+ with(executeQuery()) {
|
|
|
+ next()
|
|
|
+ obj("id" to number(getInt("id")), "randomNumber" to number(getInt("randomNumber")))
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
class ReactivePostgresDatabase private constructor(private val db: PgPool) : Database {
|
|
@@ -111,15 +109,15 @@ class ReactivePostgresDatabase private constructor(private val db: PgPool) : Dat
|
|
|
port = 5432
|
|
|
user = "benchmarkdbuser"
|
|
|
password = "benchmarkdbpass"
|
|
|
- maxSize = 100
|
|
|
+ maxSize = Runtime.getRuntime().availableProcessors() * 2
|
|
|
cachePreparedStatements = true
|
|
|
}
|
|
|
return ReactivePostgresDatabase(pool(PgPoolOptions(options)))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- override fun findWorld(): JsonNode? {
|
|
|
- val deferred = CompletableFuture<JsonNode?>()
|
|
|
+ override fun findWorld(): JsonNode {
|
|
|
+ val deferred = CompletableFuture<JsonNode>()
|
|
|
db.preparedQuery("SELECT id, randomnumber from WORLD where id=$1", Tuple.of(randomWorld())) {
|
|
|
with(it.result().first()) {
|
|
|
deferred.complete(obj("id" to number(getInteger(0)), "randomNumber" to number(getInteger(1))))
|
|
@@ -147,20 +145,20 @@ class ReactivePostgresDatabase private constructor(private val db: PgPool) : Dat
|
|
|
override fun updateWorlds(count: Int): List<JsonNode> {
|
|
|
val deferred = CompletableFuture<List<JsonNode>>()
|
|
|
val worlds = mutableListOf<Tuple>()
|
|
|
- (1..count).forEach {
|
|
|
- db.preparedQuery("SELECT id from WORLD where id=$1", Tuple.of(randomWorld())) { ar ->
|
|
|
- with(ar.result().first()) {
|
|
|
- worlds.add(Tuple.of(getInteger(0), randomWorld()))
|
|
|
-
|
|
|
- if (worlds.size == count) {
|
|
|
- db.preparedBatch("UPDATE world SET randomnumber=$1 WHERE id=$2", worlds) {
|
|
|
- deferred.complete(worlds.map {
|
|
|
- obj("id" to number(it.getInteger(0)), "randomNumber" to number(it.getInteger(1)))
|
|
|
- })
|
|
|
- }
|
|
|
+ (1..count).forEach {
|
|
|
+ db.preparedQuery("SELECT id from WORLD where id=$1", Tuple.of(randomWorld())) { ar ->
|
|
|
+ with(ar.result().first()) {
|
|
|
+ worlds.add(Tuple.of(getInteger(0), randomWorld()))
|
|
|
+
|
|
|
+ if (worlds.size == count) {
|
|
|
+ db.preparedBatch("UPDATE world SET randomnumber=$1 WHERE id=$2", worlds) {
|
|
|
+ deferred.complete(worlds.map {
|
|
|
+ obj("id" to number(it.getInteger(0)), "randomNumber" to number(it.getInteger(1)))
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
return deferred.get()
|
|
|
}
|