libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 743cf9ed4559f27a7fd21c314a69cb1d64b7d6af
parent 101fa06fd5fa863a90eb4cbc0b4fcd2e28fda583
Author: MS <ms@taler.net>
Date:   Sun,  9 Apr 2023 17:20:52 +0200

monitoring scheduler perf.

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/Scheduling.kt | 20+++++++++++++++++---
Anexus/src/test/kotlin/SchedulingTest.kt | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dnexus/src/test/kotlin/SchedulingTesting.kt | 44--------------------------------------------
3 files changed, 186 insertions(+), 47 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Scheduling.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Scheduling.kt @@ -34,8 +34,9 @@ import java.lang.IllegalArgumentException import java.time.Duration import java.time.Instant import java.time.ZonedDateTime -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit +import java.util.* +import kotlin.concurrent.schedule +import kotlin.coroutines.coroutineContext import kotlin.system.exitProcess private data class TaskSchedule( @@ -151,10 +152,23 @@ private suspend fun operationScheduler(httpClient: HttpClient) { } } + +// Alternative scheduler based on Java Timer, but same perf. as the while-true one. +/* +private val javaTimer = Timer() +suspend fun javaTimerOperationScheduler(httpClient: HttpClient) { + operationScheduler(httpClient) + javaTimer.schedule( + delay = 1000L, + action = { runBlocking { javaTimerOperationScheduler(httpClient) } } + ) +} +*/ + suspend fun whileTrueOperationScheduler(httpClient: HttpClient) { while (true) { operationScheduler(httpClient) // Wait a bit - delay(Duration.ofSeconds(1)) + delay(Duration.ofSeconds(5)) } } \ No newline at end of file diff --git a/nexus/src/test/kotlin/SchedulingTest.kt b/nexus/src/test/kotlin/SchedulingTest.kt @@ -0,0 +1,168 @@ +import io.ktor.client.* +import io.ktor.client.plugins.* +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.server.testing.* +import kotlinx.coroutines.* +import org.junit.Ignore +import org.junit.Test +import tech.libeufin.nexus.bankaccount.fetchBankAccountTransactions +import tech.libeufin.nexus.server.FetchLevel +import tech.libeufin.nexus.server.FetchSpecAllJson +import tech.libeufin.nexus.whileTrueOperationScheduler +import tech.libeufin.sandbox.sandboxApp +import java.util.* +import kotlin.concurrent.schedule +import kotlin.text.get + +/** + * This test suite helps to _measure_ the scheduler performance. + * It is NOT meant to assert on values, but rather to _launch_ and + * give the chance to monitor the CPU usage with TOP(1) + */ + +// This class focuses on the perf. of Nexus scheduling. +class SchedulingTest { + // Launching the scheduler to measure its perf with TOP(1) + @Ignore // Ignoring because no assert takes place. + @Test + fun normalOperation() { + withTestDatabase { + prepNexusDb() + prepSandboxDb() + testApplication { + application(sandboxApp) + whileTrueOperationScheduler(client) + // javaTimerOperationScheduler(client) + } + } + runBlocking { + launch { awaitCancellation() } + } + } + + // Allows TOP(1) on the bare connection operations without the scheduling overhead. + // Not strictly related to scheduling, but perf. is a major part of scheduling. + @Test + @Ignore // Ignoring because no assert takes place. + fun bareOperationXLibeufinBank() { + withTestDatabase { + prepNexusDb() + prepSandboxDb() + testApplication { + application(sandboxApp) + runBlocking { + while (true) { + // Even x-libeufin-bank takes 10-20% CPU + fetchBankAccountTransactions( + client, + fetchSpec = FetchSpecAllJson( + level = FetchLevel.STATEMENT, + bankConnection = "bar" + ), + accountId = "bar" + ) + delay(1000L) + } + } + } + } + } + // Same as the previous, but on a EBICS connection. + // Perf. is only slightly worse than the JSON based x-libeufin-bank connection. + @Ignore // Ignoring because no assert takes place. + @Test + fun bareOperationEbics() { + withTestDatabase { + prepNexusDb() + prepSandboxDb() + testApplication { + application(sandboxApp) + runBlocking { + while (true) { + fetchBankAccountTransactions( + client, + fetchSpec = FetchSpecAllJson( + level = FetchLevel.STATEMENT, + bankConnection = "foo" + ), + accountId = "foo" + ) + delay(1000L) + } + } + } + } + } + + // HTTP requests loop, to measure perf. via TOP(1) + @Ignore // because no assert takes place. + @Test + fun plainSandboxReqLoop() { + withTestDatabase { + prepSandboxDb() + testApplication { + application(sandboxApp) + while (true) { + // This brings the CPU to > 10% + /*client.get("/demobanks/default/access-api/accounts/foo") { + expectSuccess = true + contentType(ContentType.Application.Json) + basicAuth("foo", "foo") + }*/ + // This brings the CPU between 3 and 10% + /*client.get("/demobanks/default/integration-api/config") { + expectSuccess = true + contentType(ContentType.Application.Json) + // This caused 3 to 9% CPU => did not cause more usage. + // basicAuth("foo", "foo") + }*/ + // Between 2 and 3% CPU. + client.get("/") + delay(1000L) + } + } + } + } +} + +// This class investigates two ways of scheduling, regardless of the one used by Nexus. +class PlainJavaScheduling { + val instanceTimer = Timer() + // Below 5% CPU time. + private fun loopWithJavaTimer() { + println("with Java Timer " + + "doing at ${System.currentTimeMillis() / 1000}.." + ) // uncertain time goes by. + instanceTimer.schedule( + delay = 1200, + action = { loopWithJavaTimer() } + ) + } + // Below 5% CPU time. + private suspend fun loopWithWhileTrue() { + val client = HttpClient() + while (true) { + println("With while-true " + + "doing at ${System.currentTimeMillis() / 1000}.." + ) // uncertain time goes by. + client.get("https://exchange.demo.taler.net/wrong") { + basicAuth("foo", "foo") + } + delay(1000) + } + } + @Ignore // due to no assert. + @Test + fun javaTimerLoop() { + loopWithJavaTimer() + runBlocking { delay(timeMillis = 30000) } + } + @Ignore // due to no assert. + @Test + fun whileTrueLoop() { + runBlocking { + loopWithWhileTrue() + } + } +} +\ No newline at end of file diff --git a/nexus/src/test/kotlin/SchedulingTesting.kt b/nexus/src/test/kotlin/SchedulingTesting.kt @@ -1,43 +0,0 @@ -import io.ktor.client.* -import io.ktor.server.testing.* -import kotlinx.coroutines.* -import org.junit.Ignore -import org.junit.Test -import tech.libeufin.nexus.whileTrueOperationScheduler -import tech.libeufin.sandbox.sandboxApp -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - -class SchedulingTesting { - // Testing the 'sleep' technique of the scheduler, to watch with TOP(1) - @Ignore // Just an experimental piece. No assertion takes place, nor its logic is used anywhere. - @Test - fun sleep1SecWithDelay() { - val sched = Executors.newScheduledThreadPool(1) - sched.scheduleAtFixedRate( - { println(".") }, - 1, - 1, - TimeUnit.SECONDS - ) - runBlocking { - launch { awaitCancellation() } - } - } - // Launching the scheduler to measure its perf with TOP(1) - @Ignore - @Test - fun normalOperation() { - withTestDatabase { - prepNexusDb() - prepSandboxDb() - testApplication { - application(sandboxApp) - whileTrueOperationScheduler(client) - } - } - runBlocking { - launch { awaitCancellation() } - } - } -} -\ No newline at end of file