Преглед изворни кода

Upgrade framework and fix wrong test (#2782) (#2811)

* Update Hexagon benchmark

* Update Hexagon benchmark

* Remove features not used in tests

* Remove MySql storage

* Enable Resin test

* Upgrade Hexagon to v0.11

* Disable Resin

* Fix dependencies

* Improve template rendering

* Test in travis

* Fix build script

* Restore Travis script

* Restore Travis script

* MySQL store support and MongoDB fix

* Fix MySQL test

* Fix MySQL test

* Fix MySQL test

* Travis testing

* Fix MySQL test

* Restore Travis

* Take MySQL URL from other test

* Add PostgreSQL support

* Update version and tests

* Update version and tests

* Only test Hexagon in fork

* Fix config

* Fix config

* Update version and tests

* Remove Resin tests

* Restore Travis

* Restore Travis
Juanjo Aguililla пре 8 година
родитељ
комит
40280b2534

+ 1 - 0
frameworks/Kotlin/hexagon/.gitignore

@@ -2,4 +2,5 @@
 build/
 log/
 .gradle/
+*~
 

+ 46 - 52
frameworks/Kotlin/hexagon/benchmark_config.json

@@ -1,58 +1,52 @@
 {
-    "framework" : "hexagon",
-    "tests" : [
+    "framework": "hexagon",
+    "tests": [
         {
-            "default" : {
-                "json_url" : "/json",
-                "db_url" : "/db",
-                "query_url" : "/query?queries=",
-                "fortune_url" : "/fortunes",
-                "update_url" : "/update?queries=",
-                "plaintext_url" : "/plaintext",
-
-                "port" : 9090,
-
-                "approach" : "Realistic",
-                "classification" : "Micro",
-                "database" : "MongoDB",
-                "framework" : "Hexagon",
-                "language" : "Kotlin",
-                "orm" : "Raw",
-                "platform" : "Servlet",
-                "webserver" : "None",
-                "os" : "Linux",
-                "database_os" : "Linux",
-                "display_name" : "Hexagon Jetty MongoDB",
-                "notes" : "http://there4.co/hexagon",
-
-                "setup_file" : "setup",
-                "versus" : "servlet"
+            "default": {
+                "json_url": "/json",
+                "db_url": "/db",
+                "query_url": "/query?queries=",
+                "fortune_url": "/fortunes",
+                "update_url": "/update?queries=",
+                "plaintext_url": "/plaintext",
+                "port": 9090,
+                "approach": "Realistic",
+                "classification": "Micro",
+                "database": "mongodb",
+                "framework": "Hexagon",
+                "language": "Kotlin",
+                "orm": "Raw",
+                "platform": "Servlet",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "Hexagon Jetty MongoDB",
+                "notes": "http://there4.co/hexagon",
+                "setup_file": "setup_jetty_mongodb",
+                "versus": "servlet"
             },
-            "mysql" : {
-                "json_url" : "/json",
-                "db_url" : "/db",
-                "query_url" : "/query?queries=",
-                "fortune_url" : "/fortunes",
-                "update_url" : "/update?queries=",
-                "plaintext_url" : "/plaintext",
-
-                "port" : 9090,
-
-                "approach" : "Realistic",
-                "classification" : "Micro",
-                "database" : "MySQL",
-                "framework" : "Hexagon",
-                "language" : "Kotlin",
-                "orm" : "Raw",
-                "platform" : "Servlet",
-                "webserver" : "None",
-                "os" : "Linux",
-                "database_os" : "Linux",
-                "display_name" : "Hexagon Jetty MySQL",
-                "notes" : "http://there4.co/hexagon",
-
-                "setup_file" : "setup_mysql",
-                "versus" : "servlet"
+            "jetty_postgresql": {
+                "json_url": "/json",
+                "db_url": "/db",
+                "query_url": "/query?queries=",
+                "fortune_url": "/fortunes",
+                "update_url": "/update?queries=",
+                "plaintext_url": "/plaintext",
+                "port": 9090,
+                "approach": "Realistic",
+                "classification": "Micro",
+                "database": "postgres",
+                "framework": "Hexagon",
+                "language": "Kotlin",
+                "orm": "Raw",
+                "platform": "Servlet",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "Hexagon Jetty PostgreSQL",
+                "notes": "http://there4.co/hexagon",
+                "setup_file": "setup_jetty_postgresql",
+                "versus": "servlet"
             }
         }
     ]

+ 6 - 11
frameworks/Kotlin/hexagon/build.gradle

@@ -1,23 +1,18 @@
 
 plugins {
-    id 'org.jetbrains.kotlin.jvm' version '1.1.1'
+    id 'org.jetbrains.kotlin.jvm' version '1.1.2-2'
 }
 
 apply from: "$gradleScripts/kotlin.gradle"
 apply from: "$gradleScripts/service.gradle"
-apply from: "$gradleScripts/deploy.gradle"
 
 apply plugin: 'war'
 
-defaultTasks 'systemd'
+defaultTasks 'installDist'
 
 mainClassName = "co.there4.hexagon.BenchmarkKt"
 applicationDefaultJvmArgs = [
-    '-Xms64M',
-    '-Xmx2G',
-    '-XX:+UseNUMA',
-    '-XX:+UseParallelGC',
-    '-XX:+AggressiveOpts'
+    '-Xms64M', '-Xmx2G', '-XX:+UseNUMA', '-XX:+UseParallelGC', '-XX:+AggressiveOpts'
 ]
 
 war {
@@ -27,18 +22,18 @@ war {
 installDist.dependsOn 'war'
 
 dependencies {
-    compile ("co.there4:hexagon:$hexagonVersion")
+    compile ("co.there4.hexagon:core:$hexagonVersion")
 
+    compile ("ch.qos.logback:logback-classic:$logbackVersion")
     compile ("com.mitchellbosecke:pebble:$pebbleVersion")
     compile ("org.mongodb:mongodb-driver:$mongodbVersion")
     compile ("com.zaxxer:HikariCP:$hikariVersion")
-    compile ("mysql:mysql-connector-java:$mysqlConnectorVersion")
+    compile ("org.postgresql:postgresql:$postgresqlVersion")
 
     // providedCompile excludes the dependency only in the WAR, not in the distribution
     providedCompile ("org.eclipse.jetty:jetty-webapp:$jettyVersion") { exclude module: "slf4j-api" }
 
     testCompile ("org.testng:testng:$testngVersion")
-    testCompile ("co.there4:hexagon:$hexagonVersion:test")
 }
 
 test {

+ 9 - 13
frameworks/Kotlin/hexagon/gradle.properties

@@ -1,15 +1,11 @@
-# suppress inspection "UnusedProperty" for whole file
-
 description=Hexagon web framework's benchmark
-
-gradleScripts=https://raw.githubusercontent.com/jaguililla/hexagon/0.11.1/gradle
-
-kotlinVersion=1.1.1
-hexagonVersion=0.11.1
-pebbleVersion=2.3.0
-jettyVersion=9.4.2.v20170220
-mongodbVersion=3.4.2
+gradleScripts=https\://raw.githubusercontent.com/jaguililla/hexagon/0.14.0/gradle
+hexagonVersion=0.14.0
 hikariVersion=2.6.1
-mysqlConnectorVersion=5.1.41
-
-testngVersion=6.10
+jettyVersion=9.4.4.v20170414
+kotlinVersion=1.1.2-2
+logbackVersion=1.2.2
+mongodbVersion=3.4.2
+pebbleVersion=2.3.0
+postgresqlVersion=42.0.0
+testngVersion=6.11

+ 1 - 30
frameworks/Kotlin/hexagon/readme.md

@@ -11,7 +11,7 @@ development platforms. The test utilizes Hexagon routes, serialization and datab
 
 ## Infrastructure Software Versions
 
-* [Hexagon 0.10.x](http://there4.co/hexagon)
+* [Hexagon 0.12.x](http://there4.co/hexagon)
 
 ## Test URLs
 
@@ -32,35 +32,6 @@ development platforms. The test utilizes Hexagon routes, serialization and datab
 * Fortunes: http://localhost:8080/fortunes
 * Database updates: http://localhost:8080/update
 * Database queries: http://localhost:8080/query
-    
-#### Resin configuration
-
-    "resin" : { 
-      "json_url" : "/json",
-      "db_url" : "/db",
-      "query_url" : "/query?queries=",
-      "fortune_url" : "/fortunes",
-      "update_url" : "/update?queries=",
-      "plaintext_url" : "/plaintext",
- 
-      "port" : 8080,
- 
-      "approach" : "Realistic",
-      "classification" : "Micro",
-      "database" : "MongoDB",
-      "framework" : "Hexagon",
-      "language" : "Kotlin",
-      "orm" : "Raw",
-      "platform" : "Servlet",
-      "webserver" : "Resin",
-      "os" : "Linux",
-      "database_os" : "Linux",
-      "display_name" : "Hexagon Resin MongoDB",
-      "notes" : "http://there4.co/hexagon",
- 
-      "setup_file" : "setup_resin",
-      "versus" : "servlet"
-    }                                   
 
 ## Run inside vagrant
 

+ 0 - 7
frameworks/Kotlin/hexagon/setup.sh

@@ -1,7 +0,0 @@
-#!/bin/bash
-
-fw_depends mongodb java
-
-gradle/wrapper
-
-nohup build/install/hexagon/bin/hexagon &

+ 11 - 0
frameworks/Kotlin/hexagon/setup_jetty_mongodb.sh

@@ -0,0 +1,11 @@
+
+#!/bin/bash
+
+fw_depends java mongodb
+
+gradle/wrapper -x test
+
+nohup build/install/hexagon/bin/hexagon mongodb &
+#rm -rf $RESIN_HOME/webapps/*
+#cp build/libs/ROOT.war $RESIN_HOME/webapps
+#resinctl start

+ 11 - 0
frameworks/Kotlin/hexagon/setup_jetty_postgresql.sh

@@ -0,0 +1,11 @@
+
+#!/bin/bash
+
+fw_depends java postgresql
+
+gradle/wrapper -x test
+
+nohup build/install/hexagon/bin/hexagon postgresql &
+#rm -rf $RESIN_HOME/webapps/*
+#cp build/libs/ROOT.war $RESIN_HOME/webapps
+#resinctl start

+ 0 - 7
frameworks/Kotlin/hexagon/setup_mysql.sh

@@ -1,7 +0,0 @@
-#!/bin/bash
-
-fw_depends mysql java
-
-gradle/wrapper
-
-nohup build/install/hexagon/bin/hexagon mysql &

+ 0 - 9
frameworks/Kotlin/hexagon/setup_resin.sh

@@ -1,9 +0,0 @@
-#!/bin/bash
-
-fw_depends mongodb java resin
-
-gradle/wrapper
-
-rm -rf $RESIN_HOME/webapps/*
-cp build/libs/ROOT.war $RESIN_HOME/webapps
-resinctl start

+ 6 - 5
frameworks/Kotlin/hexagon/source_code

@@ -1,9 +1,10 @@
+./hexagon/build.gradle
+./hexagon/gradle.properties
 ./hexagon/src/main/kotlin/co/there4/hexagon/Benchmark.kt
 ./hexagon/src/main/kotlin/co/there4/hexagon/BenchmarkStorage.kt
-./hexagon/src/main/resources/templates/fortunes.html
-./hexagon/src/main/resources/service.properties
 ./hexagon/src/main/resources/logback.xml
+./hexagon/src/main/resources/service.yaml
+./hexagon/src/main/resources/templates/fortunes.html
 ./hexagon/src/test/kotlin/co/there4/hexagon/BenchmarkTest.kt
-./hexagon/src/test/resources/service.yaml
-./hexagon/build.gradle
-./hexagon/gradle.properties
+./hexagon/src/test/resources/logback-test.xml
+./hexagon/src/test/resources/service_test.yaml

+ 46 - 43
frameworks/Kotlin/hexagon/src/main/kotlin/co/there4/hexagon/Benchmark.kt

@@ -2,79 +2,82 @@ package co.there4.hexagon
 
 import co.there4.hexagon.serialization.convertToMap
 import co.there4.hexagon.serialization.serialize
-import co.there4.hexagon.web.*
-import co.there4.hexagon.web.servlet.ServletServer
+import co.there4.hexagon.server.*
+import co.there4.hexagon.server.engine.servlet.JettyServletEngine
+import co.there4.hexagon.server.engine.servlet.ServletServer
+import co.there4.hexagon.settings.SettingsManager.settings
+import java.lang.System.getenv
 
 import java.net.InetAddress.getByName as address
-import java.time.LocalDateTime.now
 import java.util.concurrent.ThreadLocalRandom
 import javax.servlet.annotation.WebListener
 
 // DATA CLASSES
-internal data class Message(val message: String = "Hello, World!")
+internal data class Message(val message: String)
 internal data class Fortune(val _id: Int, val message: String)
-internal data class World(val _id: Int, val id: Int = _id, val randomNumber: Int = rnd())
+internal data class World(val _id: Int, val id: Int, val randomNumber: Int)
 
 // CONSTANTS
-private val CONTENT_TYPE_JSON = "application/json"
-private val QUERIES_PARAM = "queries"
+private const val TEXT_MESSAGE: String = "Hello, World!"
+private const val CONTENT_TYPE_JSON = "application/json"
+private const val QUERIES_PARAM = "queries"
 
-// UTILITIES
-internal fun rnd() = ThreadLocalRandom.current().nextInt(DB_ROWS) + 1
+internal var server: Server? = null
 
-private fun Exchange.returnWorlds(worlds: List<World>) {
-    fun World.strip(): Map<*, *> = this.convertToMap().filterKeys { it != "_id" }
+// UTILITIES
+internal fun randomWorld() = ThreadLocalRandom.current().nextInt(WORLD_ROWS) + 1
 
-    val result =
-        if (request[QUERIES_PARAM] == null) worlds[0].strip().serialize()
-        else worlds.map(World::strip).serialize()
+private fun Call.returnWorlds(worldsList: List<World>) {
+    val worlds = worldsList.map { it.convertToMap() - "_id" }
+    val result = if (worlds.size == 1) worlds.first().serialize() else worlds.serialize()
 
     ok(result, CONTENT_TYPE_JSON)
 }
 
-private fun Exchange.getQueries() =
-    try {
-        val queries = request[QUERIES_PARAM]?.toInt() ?: 1
-        when {
-            queries < 1 -> 1
-            queries > 500 -> 500
-            else -> queries
-        }
-    }
-    catch (ex: NumberFormatException) {
-        1
+private fun Call.getWorldsCount() = (request[QUERIES_PARAM]?.toIntOrNull() ?: 1).let {
+    when {
+        it < 1 -> 1
+        it > 500 -> 500
+        else -> it
     }
+}
 
 // HANDLERS
-private fun Exchange.listFortunes(store: Repository) {
-    val fortunes = store.findFortunes() + Fortune(0, "Additional fortune added at request time.")
+private fun Call.listFortunes(store: Store) {
+    val fortunes = store.findAllFortunes() + Fortune(0, "Additional fortune added at request time.")
     response.contentType = "text/html; charset=utf-8"
     template("fortunes.html", "fortunes" to fortunes.sortedBy { it.message })
 }
 
-private fun benchmarkRoutes(store: Repository, srv: Router = server) {
-    srv.before {
+private fun Call.getWorlds(store: Store) {
+    returnWorlds(store.findWorlds(getWorldsCount()))
+}
+
+private fun Call.updateWorlds(store: Store) {
+    returnWorlds(store.replaceWorlds(getWorldsCount()))
+}
+
+private fun router(store: Store): Router = router {
+    before {
         response.addHeader("Server", "Servlet/3.1")
         response.addHeader("Transfer-Encoding", "chunked")
-        response.addHeader("Date", httpDate(now()))
+        response.addHeader("Date", httpDate())
     }
 
-    srv.get("/plaintext") { ok("Hello, World!", "text/plain") }
-    srv.get("/json") { ok(Message().serialize(), CONTENT_TYPE_JSON) }
-    srv.get("/fortunes") { listFortunes(store) }
-    srv.get("/db") { returnWorlds(store.findWorlds(getQueries())) }
-    srv.get("/query") { returnWorlds(store.findWorlds(getQueries())) }
-    srv.get("/update") { returnWorlds(store.replaceWorlds(getQueries())) }
+    get("/plaintext") { ok(TEXT_MESSAGE, "text/plain") }
+    get("/json") { ok(Message(TEXT_MESSAGE).serialize(), CONTENT_TYPE_JSON) }
+    get("/fortunes") { listFortunes(store) }
+    get("/db") { getWorlds(store) }
+    get("/query") { getWorlds(store) }
+    get("/update") { updateWorlds(store) }
 }
 
 @WebListener class Web : ServletServer () {
-    override fun init() {
-        benchmarkRoutes(createStore("mongodb"), this)
-    }
+    override fun createRouter() = router (createStore(getenv("DBSTORE") ?: "mongodb"))
 }
 
-fun main(args: Array<String>) {
-    val store = createStore(if (args.isEmpty()) "mongodb" else args[0])
-    benchmarkRoutes(store)
-    run()
+fun main(vararg args: String) {
+    val store = createStore(if (args.isEmpty()) getenv("DBSTORE") ?: "mongodb" else args[0])
+    server = Server(JettyServletEngine(), settings, router(store))
+    server?.run()
 }

+ 78 - 85
frameworks/Kotlin/hexagon/src/main/kotlin/co/there4/hexagon/BenchmarkStorage.kt

@@ -1,106 +1,103 @@
 package co.there4.hexagon
 
-import co.there4.hexagon.repository.MongoIdRepository
-import co.there4.hexagon.repository.mongoCollection
-import java.lang.System.getenv
+import co.there4.hexagon.settings.SettingsManager.settings
+import co.there4.hexagon.store.MongoIdRepository
+import co.there4.hexagon.store.mongoCollection
+import co.there4.hexagon.store.mongoDatabase
 
-import co.there4.hexagon.settings.SettingsManager.setting
-import co.there4.hexagon.repository.mongoDatabase
-import co.there4.hexagon.util.err
 import com.zaxxer.hikari.HikariConfig
 import com.zaxxer.hikari.HikariDataSource
-import java.io.Closeable
+
+import java.lang.System.getenv
 import java.sql.Connection
 import java.sql.ResultSet.CONCUR_READ_ONLY
 import java.sql.ResultSet.TYPE_FORWARD_ONLY
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
 import javax.sql.DataSource
+
 import kotlin.reflect.KProperty1
 
-internal val DB_ROWS = 10000
+internal const val WORLD_ROWS = 10000
 
 private val DB_HOST = getenv("DBHOST") ?: "localhost"
-private val DB = setting<String>("database") ?: "hello_world"
-private val WORLD: String = setting<String>("worldCollection") ?: "world"
-private val FORTUNE: String = setting<String>("fortuneCollection") ?: "fortune"
-
-private val executor: ExecutorService = Executors.newFixedThreadPool(8)
-
-internal fun createStore(engine: String): Repository = when (engine) {
-    "mongodb" -> MongoDbRepository()
-    "mysql" -> MySqlRepository()
+private val DB_NAME = settings["database"] as? String ?: "hello_world"
+private val WORLD_NAME: String = settings["worldCollection"] as? String ?: "world"
+private val FORTUNE_NAME: String = settings["fortuneCollection"] as? String ?: "fortune"
+
+private val postgresqlUrl = "jdbc:postgresql://$DB_HOST/$DB_NAME?" +
+    "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"
+
+internal fun createStore(engine: String): Store = when (engine) {
+    "mongodb" -> MongoDbStore()
+    "postgresql" -> SqlStore(postgresqlUrl)
     else -> error("Unsupported database")
 }
 
-internal interface Repository {
-    fun findFortunes(): List<Fortune>
-    fun findWorlds(queries: Int): List<World>
-    fun replaceWorlds(queries: Int): List<World>
+internal interface Store {
+    fun findAllFortunes(): List<Fortune>
+    fun findWorlds(count: Int): List<World>
+    fun replaceWorlds(count: Int): List<World>
 }
 
-internal class MongoDbRepository : Repository {
-    private val database = mongoDatabase("mongodb://$DB_HOST/$DB")
+private class MongoDbStore : Store {
+    private val database = mongoDatabase("mongodb://$DB_HOST/$DB_NAME")
 
-    internal val worldRepository = repository(WORLD, World::_id)
-    internal val fortuneRepository = repository(FORTUNE, Fortune::_id)
+    private val worldRepository = repository(WORLD_NAME, World::_id)
+    private val fortuneRepository = repository(FORTUNE_NAME, Fortune::_id)
 
     // TODO Find out why it fails when creating index '_id' with background: true
     private inline fun <reified T : Any> repository(name: String, key: KProperty1<T, Int>) =
         MongoIdRepository(T::class, mongoCollection(name, database), key, indexOrder = null)
 
-    override fun findFortunes() = fortuneRepository.findObjects().toList()
+    override fun findAllFortunes() = fortuneRepository.findObjects().toList()
 
-    override fun findWorlds(queries: Int) =
-        (1..queries).map { worldRepository.find(rnd()) }.filterNotNull()
+    override fun findWorlds(count: Int) =
+        (1..count).map { worldRepository.find(randomWorld()) }.filterNotNull()
 
-    override fun replaceWorlds(queries: Int) = (1..queries).map {
-        val id = rnd()
-        val newWorld = worldRepository.find(id)?.copy(randomNumber = rnd()) ?: err
-        executor.execute { worldRepository.replaceObject(newWorld) }
-        newWorld
-    }
+    override fun replaceWorlds(count: Int) = (1..count)
+        .map { worldRepository.find(randomWorld())?.copy(randomNumber = randomWorld()) }
+        .toList()
+        .filterNotNull()
+        .map {
+            worldRepository.replaceObjects(it, bulk = true)
+            it
+        }
 }
 
-internal class MySqlRepository : Repository {
+private class SqlStore(jdbcUrl: String) : Store {
     private val SELECT_WORLD = "select * from world where id = ?"
     private val UPDATE_WORLD = "update world set randomNumber = ? where id = ?"
-    private val SELECT_FORTUNES = "select * from fortune"
+    private val SELECT_ALL_FORTUNES = "select * from fortune"
 
     private val DATA_SOURCE: DataSource
 
     init {
         val config = HikariConfig()
-        config.jdbcUrl = "jdbc:mysql://$DB_HOST/$DB?" +
-            "useSSL=false&" +
-            "rewriteBatchedStatements=true&" +
-            "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"
-        config.maximumPoolSize = 256
+        config.jdbcUrl = jdbcUrl
+        config.maximumPoolSize = 32 // TODO Extract to settings
         config.username = "benchmarkdbuser"
         config.password = "benchmarkdbpass"
         DATA_SOURCE = HikariDataSource(config)
     }
 
-    override fun findFortunes(): List<Fortune> {
+    override fun findAllFortunes(): List<Fortune> {
         var fortunes = listOf<Fortune>()
 
-        val connection = KConnection(DATA_SOURCE.connection ?: err)
-        connection.use { con: Connection ->
-            val rs = con.prepareStatement(SELECT_FORTUNES).executeQuery()
+        DATA_SOURCE.connection.use { con: Connection ->
+            val rs = con.prepareStatement(SELECT_ALL_FORTUNES).executeQuery()
             while (rs.next())
                 fortunes += Fortune(rs.getInt(1), rs.getString(2))
         }
@@ -108,53 +105,49 @@ internal class MySqlRepository : Repository {
         return fortunes
     }
 
-    class KConnection(conn: Connection) : Connection by conn, Closeable
-
-    override fun findWorlds(queries: Int): List<World> {
+    override fun findWorlds(count: Int): List<World> {
         var worlds: List<World> = listOf()
 
-        KConnection(DATA_SOURCE.connection).use { con: Connection ->
+        DATA_SOURCE.connection.use { con: Connection ->
             val stmtSelect = con.prepareStatement(SELECT_WORLD)
 
-            for (ii in 0..queries - 1) {
-                stmtSelect.setInt(1, rnd())
+            for (ii in 0..count - 1) {
+                stmtSelect.setInt(1, randomWorld())
                 val rs = stmtSelect.executeQuery()
                 rs.next()
-                worlds += World(rs.getInt(1), rs.getInt(2))
+                val _id = rs.getInt(1)
+                worlds += World(_id, _id, rs.getInt(2))
             }
         }
 
         return worlds
     }
 
-    override fun replaceWorlds(queries: Int): List<World> {
+    override fun replaceWorlds(count: Int): List<World> {
         var worlds: List<World> = listOf()
 
-        KConnection(DATA_SOURCE.connection).use { con: Connection ->
+        DATA_SOURCE.connection.use { con: Connection ->
             val stmtSelect = con.prepareStatement(SELECT_WORLD, TYPE_FORWARD_ONLY, CONCUR_READ_ONLY)
+            val stmtUpdate = con.prepareStatement(UPDATE_WORLD)
 
-            for (ii in 0..queries - 1) {
-                stmtSelect.setInt(1, rnd())
+            for (ii in 0..count - 1) {
+                stmtSelect.setInt(1, randomWorld())
                 val rs = stmtSelect.executeQuery()
                 rs.next()
 
-                val world = World(rs.getInt(1), rs.getInt(2)).copy(randomNumber = rnd())
+                val _id = rs.getInt(1)
+                val world = World(_id, _id, rs.getInt(2)).copy(randomNumber = randomWorld())
                 worlds += world
-            }
-        }
-
-        executor.execute {
-            KConnection(DATA_SOURCE.connection).use { con: Connection ->
-                val stmtUpdate = con.prepareStatement(UPDATE_WORLD)
 
-                for ((_, id, randomNumber) in worlds) {
-                    stmtUpdate.setInt(1, randomNumber)
-                    stmtUpdate.setInt(2, id)
-                    stmtUpdate.addBatch()
-                }
+                stmtUpdate.setInt(1, world.randomNumber)
+                stmtUpdate.setInt(2, world.id)
+                stmtUpdate.addBatch()
 
-                stmtUpdate.executeBatch()
+                if (ii % 25 == 0)
+                    stmtUpdate.executeBatch()
             }
+
+            stmtUpdate.executeBatch()
         }
 
         return worlds

+ 0 - 2
frameworks/Kotlin/hexagon/src/main/resources/logback.xml

@@ -22,6 +22,4 @@
     <appender-ref ref="console" />
     <appender-ref ref="file" />
   </root>
-
-  <logger name="co.there4.hexagon" level="off" />
 </configuration>

+ 5 - 2
frameworks/Kotlin/hexagon/src/main/resources/service.yaml

@@ -1,9 +1,12 @@
 
+serviceName : Hexagon Benchmark
+
 bindPort : 9090
 bindAddress : 0.0.0.0
 
-ignoreResources : true
-
 database : hello_world
 worldCollection : world
 fortuneCollection : fortune
+
+databaseUsername : benchmarkdbuser
+databasePassword : benchmarkdbpass

+ 10 - 14
frameworks/Kotlin/hexagon/src/test/kotlin/co/there4/hexagon/BenchmarkTest.kt

@@ -1,30 +1,25 @@
 package co.there4.hexagon
 
 import co.there4.hexagon.serialization.parse
-import co.there4.hexagon.web.Client
-import co.there4.hexagon.web.HttpMethod.GET
-import co.there4.hexagon.web.reset
-import co.there4.hexagon.web.server
-import co.there4.hexagon.web.stop
+import co.there4.hexagon.client.Client
+import co.there4.hexagon.server.HttpMethod.GET
 import org.asynchttpclient.Response
 import org.testng.annotations.BeforeClass
 import org.testng.annotations.Test
 import kotlin.test.assertFailsWith
 
 internal const val THREADS = 4
-internal const val TIMES = 4
+internal const val TIMES = 2
 
-class BenchmarkMongoDbTest : BenchmarkTest("mongodb")
-class BenchmarkMySqlTest : BenchmarkTest("mysql")
+//class BenchmarkMongoDbTest : BenchmarkTest("mongodb")
+//class BenchmarkPostgreSqlTest : BenchmarkTest("postgresql")
 
 //@Test(threadPoolSize = THREADS, invocationCount = TIMES)
 abstract class BenchmarkTest(val databaseEngine: String) {
-    private val client by lazy { Client("http://localhost:${server.runtimePort}") }
+    private val client by lazy { Client("http://localhost:${server?.runtimePort}") }
 
     @BeforeClass fun warmup() {
-        stop()
-        reset()
-        main(arrayOf(databaseEngine))
+        main(databaseEngine)
 
         val warmupRounds = if (THREADS > 1) 2 else 0
         (1..warmupRounds).forEach {
@@ -60,9 +55,10 @@ abstract class BenchmarkTest(val databaseEngine: String) {
 
     fun web() {
         val web = Web()
-        web.init()
 
-        val webRoutes = web.routes.map { it.key.method to it.key.path.path }
+        val webRoutes = web.serverRouter.requestHandlers
+            .map { it.route.method.first() to it.route.path.path }
+
         val benchmarkRoutes = listOf(
             GET to "/plaintext",
             GET to "/json",

+ 16 - 0
frameworks/Kotlin/hexagon/src/test/resources/logback-test.xml

@@ -0,0 +1,16 @@
+<!--
+ | Logback configuration for tests
+ |
+ | All output is ignored by default, to enable logs change root logger level.
+ !-->
+<configuration>
+  <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <Pattern>%d{HH:mm:ss.SSS} %highlight(%-5p) %magenta([%-18.18t]) %-30c{30} | %m%n</Pattern>
+    </encoder>
+  </appender>
+
+  <root level="warn">
+    <appender-ref ref="console" />
+  </root>
+</configuration>

+ 0 - 7
frameworks/Kotlin/hexagon/src/test/resources/service.yaml

@@ -1,7 +0,0 @@
-
-bindPort : 0
-bindAddress : 0.0.0.0
-
-database : hello_world
-worldCollection : world
-fortuneCollection : fortune

+ 2 - 0
frameworks/Kotlin/hexagon/src/test/resources/service_test.yaml

@@ -0,0 +1,2 @@
+
+bindPort : 0