Browse Source

Fixed http4k PGClient update performance (#8531)

* new java 21 upgrades and graalvm for helidon

* refactor database code (pgclient and jdbc) to use execute batch

* Revert "refactor database code (pgclient and jdbc) to use execute batch"

This reverts commit 58083555ce66f9efd80edb123b52a4fdf823fe18.

* pull JSON representation of World out to HTTP layer instead of at DB layer

* pull out Random to be a shared value

* reupdate JDBC example to use batch execute on update

* reupdate PGclient implementations to use batch execute on update

* bump (for flakey testrun in sunhttploom (consistently passes locally)

* actually sort the list of worlds in PGclient

* Tweak SunHttpLoom configuration

* tweaks to pgclient database calls

* tweaks to pgclient database calls

* tweaks to jdbc database calls

* use prepared statements for JDBC queries

* Fixed PGClient update

* Update to Java 20 source compatibility

* Update to Java 20 source compatibility

* Reimplement update query

* upgrade kotlin and http4k

* tweak implementation of plaintext to match other implementations

* tweak implementation of plaintext to match other implementations (remove ktor netty because badly behaved on plaintext)
David Denton 1 year ago
parent
commit
e9c5060a8d

+ 0 - 1
frameworks/Kotlin/http4k/benchmark_config.json

@@ -295,7 +295,6 @@
         "db_url": "/db",
         "json_url": "/json",
         "fortune_url": "/fortunes",
-        "plaintext_url": "/plaintext",
         "query_url": "/queries?queries=",
         "update_url": "/updates?queries=",
         "database": "Postgres",

+ 5 - 5
frameworks/Kotlin/http4k/build.gradle.kts

@@ -1,9 +1,9 @@
 import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-import org.gradle.api.JavaVersion.VERSION_1_8
+import org.gradle.api.JavaVersion.*
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
-    kotlin("jvm") version "1.9.10"
+    kotlin("jvm") version "1.9.20"
     application
 }
 
@@ -34,14 +34,14 @@ allprojects {
     }
 
     java {
-        sourceCompatibility = VERSION_1_8
-        targetCompatibility = VERSION_1_8
+        sourceCompatibility = VERSION_20
+        targetCompatibility = VERSION_20
     }
 
     tasks {
         withType<KotlinCompile> {
             kotlinOptions {
-                jvmTarget = "1.8"
+                jvmTarget = "20"
                 allWarningsAsErrors = true
             }
         }

+ 0 - 1
frameworks/Kotlin/http4k/config.toml

@@ -42,7 +42,6 @@ urls.cached_query = "/cached?queries="
 urls.db = "/db"
 urls.fortune = "/fortunes"
 urls.json = "/json"
-urls.plaintext = "/plaintext"
 urls.query = "/queries?queries="
 urls.update = "/updates?queries="
 approach = "Realistic"

+ 13 - 18
frameworks/Kotlin/http4k/core-pgclient/src/main/kotlin/PostgresDatabase.kt

@@ -1,3 +1,4 @@
+import io.vertx.core.CompositeFuture
 import io.vertx.core.Future
 import io.vertx.core.Vertx
 import io.vertx.core.VertxOptions
@@ -9,6 +10,7 @@ import io.vertx.sqlclient.SqlClient
 import io.vertx.sqlclient.Tuple
 import java.util.Random
 
+
 class PostgresDatabase : Database {
     private val queryPool: SqlClient
     private val updatePool: SqlClient
@@ -44,24 +46,17 @@ class PostgresDatabase : Database {
                 (1..count).map { queryPool.findWorld(random.world()) }
             ).toCompletionStage().toCompletableFuture().get().list<World>()
 
-    override fun updateWorlds(count: Int): List<Pair<Int, Int>> {
-        val updatedAndSorted = Future
-            .all(
-                (1..count)
-                    .map {
-                        queryPool.findWorld(random.world())
-                            .map { it.first to random.world() }
-                    }
-            )
-            .toCompletionStage().toCompletableFuture().get().list<World>()
-            .sortedBy { it.first }
-
-        updatePool.preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2")
-            .executeBatch(updatedAndSorted.map { Tuple.of(it.first, it.second) })
-            .toCompletionStage().toCompletableFuture().get()
-
-        return updatedAndSorted
-    }
+    override fun updateWorlds(count: Int) =
+        Future.all((1..count)
+            .map { queryPool.findWorld(random.world()) })
+            .map<List<World>>(CompositeFuture::list)
+            .toCompletionStage()
+            .thenCompose { worlds ->
+                updatePool.preparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2")
+                    .executeBatch((1..count).map { Tuple.of(random.world(), random.world()) })
+                    .toCompletionStage()
+                    .thenApply { worlds }
+            }.toCompletableFuture().get()
 
     override fun fortunes() = queryPool.preparedQuery("SELECT id, message FROM fortune")
         .execute()

+ 3 - 3
frameworks/Kotlin/http4k/core/build.gradle.kts

@@ -4,9 +4,9 @@ plugins {
 }
 
 dependencies {
-    api(platform("org.http4k:http4k-bom:5.9.0.0"))
-    api("org.jetbrains.kotlin:kotlin-stdlib:1.9.10")
-    api("org.jetbrains.kotlin:kotlin-reflect:1.9.10")
+    api(platform("org.http4k:http4k-bom:5.10.2.0"))
+    api("org.jetbrains.kotlin:kotlin-stdlib:1.9.20")
+    api("org.jetbrains.kotlin:kotlin-reflect:1.9.20")
     api("org.http4k:http4k-core")
     api("org.http4k:http4k-format-argo")
     api("org.http4k:http4k-template-rocker")

+ 6 - 4
frameworks/Kotlin/http4k/core/src/main/kotlin/PlainTextRoute.kt

@@ -1,15 +1,17 @@
+import org.http4k.core.Body
 import org.http4k.core.ContentType.Companion.TEXT_PLAIN
 import org.http4k.core.Method.GET
 import org.http4k.core.Response
 import org.http4k.core.Status.Companion.OK
-import org.http4k.core.with
-import org.http4k.lens.Header.CONTENT_TYPE
 import org.http4k.routing.bind
+import java.nio.ByteBuffer.wrap
+import java.nio.charset.StandardCharsets.UTF_8
 
 object PlainTextRoute {
-    private const val preAllocatedHelloWorldText = "Hello, World!"
+    private val body = Body(wrap("Hello, world!".toByteArray(UTF_8)))
+    private val contentType = TEXT_PLAIN.toHeaderValue()
 
     operator fun invoke() = "/plaintext" bind GET to {
-        Response(OK).body(preAllocatedHelloWorldText).with(CONTENT_TYPE of TEXT_PLAIN)
+        Response(OK).body(body).header("Content-Type", contentType)
     }
 }