|
@@ -1,28 +1,21 @@
|
|
import com.github.jasync.sql.db.ConnectionPoolConfiguration
|
|
import com.github.jasync.sql.db.ConnectionPoolConfiguration
|
|
|
|
+import com.github.jasync.sql.db.QueryResult
|
|
import com.github.jasync.sql.db.SuspendingConnection
|
|
import com.github.jasync.sql.db.SuspendingConnection
|
|
import com.github.jasync.sql.db.asSuspending
|
|
import com.github.jasync.sql.db.asSuspending
|
|
import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder
|
|
import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder
|
|
-import io.ktor.application.call
|
|
|
|
-import io.ktor.application.install
|
|
|
|
-import io.ktor.features.DefaultHeaders
|
|
|
|
-import io.ktor.html.Placeholder
|
|
|
|
-import io.ktor.html.Template
|
|
|
|
-import io.ktor.html.insert
|
|
|
|
-import io.ktor.html.respondHtmlTemplate
|
|
|
|
import io.ktor.http.ContentType
|
|
import io.ktor.http.ContentType
|
|
-import io.ktor.response.respondText
|
|
|
|
-import io.ktor.routing.get
|
|
|
|
-import io.ktor.routing.routing
|
|
|
|
|
|
+import io.ktor.server.application.*
|
|
import io.ktor.server.engine.embeddedServer
|
|
import io.ktor.server.engine.embeddedServer
|
|
|
|
+import io.ktor.server.html.*
|
|
import io.ktor.server.netty.Netty
|
|
import io.ktor.server.netty.Netty
|
|
-import io.reactiverse.kotlin.pgclient.getConnectionAwait
|
|
|
|
-import io.reactiverse.kotlin.pgclient.preparedBatchAwait
|
|
|
|
-import io.reactiverse.kotlin.pgclient.preparedQueryAwait
|
|
|
|
-import io.reactiverse.pgclient.*
|
|
|
|
|
|
+import io.ktor.server.plugins.defaultheaders.*
|
|
|
|
+import io.ktor.server.response.*
|
|
|
|
+import io.ktor.server.routing.*
|
|
|
|
+import kotlinx.coroutines.*
|
|
import kotlinx.html.*
|
|
import kotlinx.html.*
|
|
import kotlinx.serialization.Serializable
|
|
import kotlinx.serialization.Serializable
|
|
-import kotlinx.serialization.json.JSON
|
|
|
|
-import kotlinx.serialization.list
|
|
|
|
|
|
+import kotlinx.serialization.encodeToString
|
|
|
|
+import kotlinx.serialization.json.Json
|
|
import java.lang.IllegalArgumentException
|
|
import java.lang.IllegalArgumentException
|
|
import kotlin.random.Random
|
|
import kotlin.random.Random
|
|
import kotlin.random.nextInt
|
|
import kotlin.random.nextInt
|
|
@@ -44,73 +37,48 @@ interface Repository {
|
|
}
|
|
}
|
|
|
|
|
|
class JasyncRepository() : Repository {
|
|
class JasyncRepository() : Repository {
|
|
- private val dbConfig: ConnectionPoolConfiguration
|
|
|
|
- private val db: SuspendingConnection
|
|
|
|
-
|
|
|
|
- init {
|
|
|
|
- dbConfig = ConnectionPoolConfiguration(
|
|
|
|
- "tfb-database",
|
|
|
|
- database = "hello_world",
|
|
|
|
- username = "benchmarkdbuser",
|
|
|
|
- password = "benchmarkdbpass",
|
|
|
|
- maxActiveConnections = 64
|
|
|
|
- )
|
|
|
|
- db = PostgreSQLConnectionBuilder.createConnectionPool(dbConfig).asSuspending
|
|
|
|
|
|
+ companion object {
|
|
|
|
+ const val WORLD_QUERY = "select id, randomNumber from world where id = ?"
|
|
|
|
+ const val FORTUNES_QUERY = "select id, message from fortune"
|
|
|
|
+ const val UPDATE_QUERY = "update world set randomNumber = ? where id = ?"
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private val dbConfig: ConnectionPoolConfiguration = ConnectionPoolConfiguration(
|
|
|
|
+ "tfb-database",
|
|
|
|
+ database = "hello_world",
|
|
|
|
+ username = "benchmarkdbuser",
|
|
|
|
+ password = "benchmarkdbpass",
|
|
|
|
+ maxActiveConnections = 64
|
|
|
|
+ )
|
|
|
|
+ private val db: SuspendingConnection = PostgreSQLConnectionBuilder.createConnectionPool(dbConfig).asSuspending
|
|
|
|
+
|
|
override suspend fun getWorld(): World {
|
|
override suspend fun getWorld(): World {
|
|
val worldId = rand.nextInt(1, 10000)
|
|
val worldId = rand.nextInt(1, 10000)
|
|
- val result = db.sendPreparedStatement("select id, randomNumber from world where id = ?", listOf(worldId))
|
|
|
|
|
|
+ val result = db.sendPreparedStatement(WORLD_QUERY, listOf(worldId))
|
|
val row = result.rows.first()
|
|
val row = result.rows.first()
|
|
return World(row.getInt(0)!!, row.getInt(1)!!)
|
|
return World(row.getInt(0)!!, row.getInt(1)!!)
|
|
}
|
|
}
|
|
|
|
|
|
override suspend fun getFortunes(): List<Fortune> {
|
|
override suspend fun getFortunes(): List<Fortune> {
|
|
- val results = db.sendPreparedStatement("select id, message from fortune")
|
|
|
|
|
|
+ val results = db.sendPreparedStatement(FORTUNES_QUERY)
|
|
return results.rows.map { Fortune(it.getInt(0)!!, it.getString(1)!!) }
|
|
return results.rows.map { Fortune(it.getInt(0)!!, it.getString(1)!!) }
|
|
}
|
|
}
|
|
|
|
|
|
override suspend fun updateWorlds(worlds: List<World>) {
|
|
override suspend fun updateWorlds(worlds: List<World>) {
|
|
- worlds.forEach { world ->
|
|
|
|
- db.sendPreparedStatement(
|
|
|
|
- "update world set randomNumber = ? where id = ?",
|
|
|
|
- listOf(world.randomNumber, world.id)
|
|
|
|
- )
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+ coroutineScope {
|
|
|
|
+ val jobs = ArrayList<Deferred<QueryResult>>(worlds.size)
|
|
|
|
+ worlds.forEach { world ->
|
|
|
|
+ val deferred = async(Dispatchers.IO) {
|
|
|
|
+ db.sendPreparedStatement(
|
|
|
|
+ UPDATE_QUERY,
|
|
|
|
+ listOf(world.randomNumber, world.id)
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ jobs.add(deferred)
|
|
|
|
+ }
|
|
|
|
|
|
-class ReactivePGRepository : Repository {
|
|
|
|
- private val db: PgPool
|
|
|
|
-
|
|
|
|
- init {
|
|
|
|
- val poolOptions = PgPoolOptions()
|
|
|
|
- poolOptions.apply {
|
|
|
|
- host = "tfb-database"
|
|
|
|
- database = "hello_world"
|
|
|
|
- user = "benchmarkdbuser"
|
|
|
|
- password = "benchmarkdbpass"
|
|
|
|
- maxSize = 64
|
|
|
|
- cachePreparedStatements = true
|
|
|
|
|
|
+ jobs.awaitAll()
|
|
}
|
|
}
|
|
- db = PgClient.pool(poolOptions)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- override suspend fun getFortunes(): List<Fortune> {
|
|
|
|
- val results = db.preparedQueryAwait("select id, message from fortune")
|
|
|
|
- return results.map { Fortune(it.getInteger(0), it.getString(1)) }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- override suspend fun getWorld(): World {
|
|
|
|
- val worldId = rand.nextInt(1, 10000)
|
|
|
|
- val result = db.preparedQueryAwait("select id, randomNumber from world where id = $1", Tuple.of(worldId))
|
|
|
|
- val row = result.first()
|
|
|
|
- return World(row.getInteger(0), row.getInteger(1)!!)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- override suspend fun updateWorlds(worlds: List<World>) {
|
|
|
|
- val batch = worlds.map { Tuple.of(it.id, it.randomNumber) }
|
|
|
|
- db.preparedBatchAwait("update world set randomNumber = $1 where id = $2", batch)
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -132,7 +100,7 @@ class MainTemplate : Template<HTML> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-class FortuneTemplate(val fortunes: List<Fortune>, val main: MainTemplate = MainTemplate()) : Template<HTML> {
|
|
|
|
|
|
+class FortuneTemplate(private val fortunes: List<Fortune>, private val main: MainTemplate = MainTemplate()) : Template<HTML> {
|
|
override fun HTML.apply() {
|
|
override fun HTML.apply() {
|
|
insert(main) {
|
|
insert(main) {
|
|
content {
|
|
content {
|
|
@@ -156,13 +124,9 @@ class FortuneTemplate(val fortunes: List<Fortune>, val main: MainTemplate = Main
|
|
fun main(args: Array<String>) {
|
|
fun main(args: Array<String>) {
|
|
val db = when(args.firstOrNull()) {
|
|
val db = when(args.firstOrNull()) {
|
|
"jasync-sql" -> JasyncRepository()
|
|
"jasync-sql" -> JasyncRepository()
|
|
- "reactive-pg" -> ReactivePGRepository()
|
|
|
|
else -> throw IllegalArgumentException("Must specify a postgres client")
|
|
else -> throw IllegalArgumentException("Must specify a postgres client")
|
|
}
|
|
}
|
|
|
|
|
|
- val messageSerializer = Message.serializer()
|
|
|
|
- val worldSerializer = World.serializer()
|
|
|
|
-
|
|
|
|
val server = embeddedServer(Netty, 8080, configure = {
|
|
val server = embeddedServer(Netty, 8080, configure = {
|
|
shareWorkGroup = true
|
|
shareWorkGroup = true
|
|
}) {
|
|
}) {
|
|
@@ -174,19 +138,19 @@ fun main(args: Array<String>) {
|
|
|
|
|
|
get("/json") {
|
|
get("/json") {
|
|
call.respondText(
|
|
call.respondText(
|
|
- JSON.stringify(messageSerializer, Message("Hello, World!")),
|
|
|
|
|
|
+ Json.encodeToString(Message("Hello, World!")),
|
|
ContentType.Application.Json
|
|
ContentType.Application.Json
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
get("/db") {
|
|
get("/db") {
|
|
- call.respondText(JSON.stringify(worldSerializer, db.getWorld()), ContentType.Application.Json)
|
|
|
|
|
|
+ call.respondText(Json.encodeToString(db.getWorld()), ContentType.Application.Json)
|
|
}
|
|
}
|
|
|
|
|
|
get("/query/") {
|
|
get("/query/") {
|
|
val queries = call.parameters["queries"]?.toBoxedInt(1..500) ?: 1
|
|
val queries = call.parameters["queries"]?.toBoxedInt(1..500) ?: 1
|
|
val worlds = (1..queries).map { db.getWorld() }
|
|
val worlds = (1..queries).map { db.getWorld() }
|
|
- call.respondText(JSON.stringify(worldSerializer.list, worlds), ContentType.Application.Json)
|
|
|
|
|
|
+ call.respondText(Json.encodeToString(worlds), ContentType.Application.Json)
|
|
}
|
|
}
|
|
|
|
|
|
get("/fortunes") {
|
|
get("/fortunes") {
|
|
@@ -204,7 +168,7 @@ fun main(args: Array<String>) {
|
|
|
|
|
|
db.updateWorlds(newWorlds)
|
|
db.updateWorlds(newWorlds)
|
|
|
|
|
|
- call.respondText(JSON.stringify(worldSerializer.list, newWorlds), ContentType.Application.Json)
|
|
|
|
|
|
+ call.respondText(Json.encodeToString(newWorlds), ContentType.Application.Json)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|