From 30e556ae2e644c49860409ee8d0a094f6db1dbbf Mon Sep 17 00:00:00 2001 From: James Ward Date: Wed, 31 Jan 2024 16:37:32 -0700 Subject: [PATCH] more 10 --- java-loom/src/main/java/Main.java | 71 ++++++++++++++++++- kotlin-coroutines/build.gradle.kts | 2 +- kotlin-coroutines/src/main/kotlin/Main.kt | 50 +++++++++++++ .../src/main/scala/EasyRacerServer.scala | 23 +++--- 4 files changed, 132 insertions(+), 14 deletions(-) diff --git a/java-loom/src/main/java/Main.java b/java-loom/src/main/java/Main.java index 011c7491..796545ff 100644 --- a/java-loom/src/main/java/Main.java +++ b/java-loom/src/main/java/Main.java @@ -1,14 +1,22 @@ +import com.sun.management.OperatingSystemMXBean; + import java.io.IOException; +import java.lang.management.ManagementFactory; import java.net.URI; import java.net.URISyntaxException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.Comparator; import java.util.List; +import java.util.Random; +import java.util.UUID; import java.util.concurrent.*; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.IntStream; import java.util.concurrent.StructuredTaskScope; @@ -218,13 +226,70 @@ record TimedResponse(Instant instant, HttpResponse response) { } } - List results() throws ExecutionException, InterruptedException { + public String scenario10() throws NoSuchAlgorithmException, InterruptedException { + var id = UUID.randomUUID().toString(); + + Supplier blocker = () -> { + try (var scope = new StructuredTaskScope.ShutdownOnSuccess>()) { + var req = HttpRequest.newBuilder(url.resolve(STR."/10?\{id}")).build(); + var messageDigest = MessageDigest.getInstance("SHA-512"); + + scope.fork(() -> client.send(req, HttpResponse.BodyHandlers.ofString())); + scope.fork(() -> { + var result = new byte[512]; + new Random().nextBytes(result); + while (!Thread.interrupted()) + result = messageDigest.digest(result); + return null; + }); + scope.join(); + return scope.result().body(); + } catch (ExecutionException | InterruptedException | NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + }; + + class Recursive { + public I func; + } + + Recursive> recursive = new Recursive<>(); + recursive.func = () -> { + var osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class); + var load = osBean.getProcessCpuLoad() * osBean.getAvailableProcessors(); + var req = HttpRequest.newBuilder(url.resolve(STR."/10?\{id}=\{load}")).build(); + try { + var resp = client.send(req, HttpResponse.BodyHandlers.ofString()); + if ((resp.statusCode() >= 200) && (resp.statusCode() < 300)) { + return resp.body(); + } + else if ((resp.statusCode() >= 300) && (resp.statusCode() < 400)) { + Thread.sleep(1000); + return recursive.func.get(); + } + else { + throw new RuntimeException(resp.body()); + } + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + }; + + try (var scope = new StructuredTaskScope()) { + scope.fork(blocker::get); + var task = scope.fork(recursive.func::get); + scope.join(); + return task.get(); + } + } + + List results() throws ExecutionException, InterruptedException, NoSuchAlgorithmException { return List.of(scenario1(), scenario2(), scenario3(), scenario4(), scenario5(), scenario6(), scenario7(), scenario8(), scenario9()); - //return List.of(scenario9()); + //return List.of(scenario10()); } } - void main() throws URISyntaxException, ExecutionException, InterruptedException { + void main() throws URISyntaxException, ExecutionException, InterruptedException, NoSuchAlgorithmException { var scenarios = new Scenarios(new URI("http://localhost:8080")); scenarios.results().forEach(System.out::println); } diff --git a/kotlin-coroutines/build.gradle.kts b/kotlin-coroutines/build.gradle.kts index 97e4db1c..3a407295 100644 --- a/kotlin-coroutines/build.gradle.kts +++ b/kotlin-coroutines/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { testImplementation("io.kotest:kotest-runner-junit5:5.8.0") testImplementation("io.kotest:kotest-assertions-core:5.8.0") testImplementation("io.kotest.extensions:kotest-extensions-testcontainers:2.0.2") - testRuntimeOnly("org.slf4j:slf4j-simple:2.0.11") + runtimeOnly("org.slf4j:slf4j-simple:2.0.11") } tasks.withType().configureEach { diff --git a/kotlin-coroutines/src/main/kotlin/Main.kt b/kotlin-coroutines/src/main/kotlin/Main.kt index 234a961a..785e2328 100644 --- a/kotlin-coroutines/src/main/kotlin/Main.kt +++ b/kotlin-coroutines/src/main/kotlin/Main.kt @@ -1,3 +1,4 @@ +import com.sun.management.OperatingSystemMXBean import io.ktor.client.* import io.ktor.client.plugins.* import io.ktor.client.request.* @@ -6,11 +7,16 @@ import io.ktor.http.* import kotlinx.coroutines.* import kotlinx.coroutines.selects.select import kotlinx.coroutines.time.delay +import java.lang.management.ManagementFactory +import java.security.MessageDigest import java.time.Duration import java.time.Instant +import java.util.UUID +import kotlin.random.Random val client = HttpClient { install(HttpTimeout) + followRedirects = false } // Note: Intentionally, only url handling code is shared across scenarios @@ -168,6 +174,49 @@ suspend fun scenario9(url: (Int) -> String): String = coroutineScope { letters.filterNotNull().sortedBy { it.first }.joinToString("") { it.second } } +suspend fun scenario10(url: (Int) -> String): String = coroutineScope { + val id = UUID.randomUUID().toString() + + val messageDigest = MessageDigest.getInstance("SHA-512") + + suspend fun blocking() = coroutineScope { + var result = Random.nextBytes(512) + while (isActive) { + result = messageDigest.digest(result) + } + } + + suspend fun blocker(): Unit = coroutineScope { + try { + select { + async { client.get(url(10) + "?$id") }.onAwait { } + async(Dispatchers.IO) { blocking() }.onAwait { } + } + } finally { + coroutineContext.cancelChildren() + } + } + + suspend fun reporter(): String = coroutineScope { + val osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean::class.java) + val load = osBean.processCpuLoad * osBean.availableProcessors + val resp = client.get(url(10) + "?$id=$load") + if (resp.status.isSuccess()) { + resp.bodyAsText() + } + else if ((resp.status.value >= 300) && (resp.status.value < 400)) { + delay(1000) + reporter() + } + else { + throw Exception(resp.bodyAsText()) + } + } + + launch { blocker() } + reporter() +} + val scenarios = listOf( ::scenario1, ::scenario2, @@ -178,6 +227,7 @@ val scenarios = listOf( ::scenario7, ::scenario8, ::scenario9, + ::scenario10, ) suspend fun results(url: (Int) -> String) = scenarios.map { diff --git a/scenario-server/src/main/scala/EasyRacerServer.scala b/scenario-server/src/main/scala/EasyRacerServer.scala index 49397d72..037b5f8b 100644 --- a/scenario-server/src/main/scala/EasyRacerServer.scala +++ b/scenario-server/src/main/scala/EasyRacerServer.scala @@ -9,7 +9,7 @@ import scala.annotation.unused object EasyRacerServer extends ZIOAppDefault: - private val wrong = Response.internalServerError("wrong") + private val wrong = Response(Status.InternalServerError, body = Body.fromString("wrong")) private type SessionPromise[T] = Promise[Nothing, T] @@ -267,7 +267,7 @@ object EasyRacerServer extends ZIOAppDefault: defer: request.url.queryParams.map.keys.headOption match case None => - Response.badRequest("You need to specify a query parameter") + Response(Status.BadRequest, body = Body.fromString("You need to specify a query parameter")) case Some(id) => val now = Clock.instant.run @@ -282,7 +282,7 @@ object EasyRacerServer extends ZIOAppDefault: case Some(loadString) => loadString.toDoubleOption match case None => - Response.badRequest("load was not a double") + Response(Status.BadRequest, body = Body.fromString("load was not a double")) case Some(load) => sessions.get(id).run match case None => @@ -295,14 +295,17 @@ object EasyRacerServer extends ZIOAppDefault: sessions.put(id, newData).run Response.status(Status.Found) else - val meanLoad = data.readings.sum / data.readings.size - ZIO.log(s"Mean load while connected to blocker = $meanLoad, Current load = $load").run - if load > 0.3 then - Response(Status.Found, body = Body.fromString(s"Load was still too high: $load")) - else if meanLoad < 0.9 then - Response.badRequest(s"A CPU was not near fully loaded - mean load = $meanLoad") + if data.readings.size < data.duration.getSeconds - 1 then + Response(Status.BadRequest, body = Body.fromString("Not enough readings")) else - Response.text("right") + val meanLoad = data.readings.sum / data.readings.size + ZIO.log(s"Mean load while connected to blocker = $meanLoad, Current load = $load").run + if load > 0.3 then + Response(Status.Found, body = Body.fromString(s"Load was still too high: $load")) + else if meanLoad < 0.9 then + Response.badRequest(s"A CPU was not near fully loaded - mean load = $meanLoad") + else + Response.text("right") def app(scenarios: Seq[Request => ZIO[Any, Nothing, Response]]): Routes[Any, Nothing] =