Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frameworks/neton-hyper/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Runtime output: the entry writes its log files next to the binary.
logs/
29 changes: 29 additions & 0 deletions frameworks/neton-hyper/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Kotlin/Native compiles to a native executable, so the runtime image carries no
# JDK — only the binary and its libc. The JDK below is Gradle's, not the app's.
#
# The build runs ./gradlew, not the image's own gradle: the wrapper pins the
# exact Gradle the entry is verified against, so the container cannot silently
# build with a different one.
FROM eclipse-temurin:21-jdk AS build
WORKDIR /app

# Warm the Gradle distribution, the Kotlin/Native toolchain and the Maven
# dependencies before the sources land, so editing Main.kt does not re-download
# the konan distribution.
COPY gradlew ./
COPY gradle ./gradle
COPY build.gradle.kts settings.gradle.kts gradle.properties ./
RUN ./gradlew --no-daemon -q dependencies --configuration linuxX64CompileKlibraries 2>/dev/null || true

COPY src ./src
RUN ./gradlew --no-daemon -q linkReleaseExecutableLinuxX64

FROM debian:bookworm-slim
WORKDIR /app
COPY --from=build /app/build/bin/linuxX64/releaseExecutable/neton-httparena.kexe /app/server
# Read at startup from the working directory; it sets the arena's port map and
# connection ceiling and outranks the DSL.
COPY config ./config
# 8080 HTTP/1.1, 8082 HTTP/2 cleartext (prior knowledge).
EXPOSE 8080 8082
CMD ["/app/server"]
41 changes: 41 additions & 0 deletions frameworks/neton-hyper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# neton-hyper

Neton 1.0.0-beta22 with Hyper4k (Rust Tokio + Hyper).

The engine is selected explicitly, alongside the framework core, logging, HTTP and routing:

```kotlin
implementation("com.netonstream:neton-http-hyper4k:1.0.0-beta22")
```

Both entries build entirely from Maven Central. Main.kt, compiler, GC and request
admission settings are identical; only Engine.kt and the engine dependency differ.
No local repository, composite build or benchmark-only business fast path is used.

## Ports

| Port | Protocol |
|---|---|
| 8080 | HTTP/1.1 |
| 8082 | HTTP/2 cleartext (prior knowledge) |
| 8081 | HTTP/1.1 TLS |
| 8443 | HTTP/2 TLS (ALPN) |

All listeners serve one route table. TLS starts when the harness mounts certificates.

## Running it outside the container

The harness mounts the dataset at `/data/dataset.json`. To run on a developer
machine, point `ARENA_DATASET` somewhere writable:

```bash
./gradlew linkReleaseExecutableMacosArm64
ARENA_DATASET=../../data/dataset.json ./build/bin/macosArm64/releaseExecutable/neton-httparena.kexe
```

## Comparison

Run neton and neton-hyper baseline separately on the same runner, repeating in
alternating order. Do not run both servers at once. Existing profile subscriptions
are retained, but the first requested comparison is baseline; new-engine
high-load results are not yet known.
56 changes: 56 additions & 0 deletions frameworks/neton-hyper/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
plugins {
kotlin("multiplatform") version "2.4.0"
kotlin("plugin.serialization") version "2.4.0"
}

repositories {
mavenCentral()
}

// Same framework release and business path in both entries; only the engine differs.
// All dependencies resolve from Maven Central, without local repositories or source substitution.
val netonVersion = "1.0.0-beta22"

kotlin {
// The arena builds linuxX64; macosArm64 is here so the endpoints can be
// exercised on a developer machine.
listOf(macosArm64(), linuxX64(), linuxArm64()).forEach { target ->
target.binaries.executable {
entryPoint = "main"
// Preserve the existing Linux link policy in both entries for this comparison.
// The Hyper4k entry links Rust-backed engine and database static libraries.
if (target.konanTarget.family == org.jetbrains.kotlin.konan.target.Family.LINUX) {
linkerOpts("--allow-multiple-definition")
}
}
}

sourceSets {
// This entry drives the engine adapter directly for its second listener
// (:8082), and that adapter is native-only, so the code lives in
// nativeMain rather than commonMain.
val nativeMain by creating {
dependsOn(commonMain.get())
dependencies {
implementation("com.netonstream:neton-core:$netonVersion")
implementation("com.netonstream:neton-logging:$netonVersion")
implementation("com.netonstream:neton-http:$netonVersion")
implementation("com.netonstream:neton-routing:$netonVersion")
implementation("com.netonstream:neton-http-hyper4k:$netonVersion")
// async-db / fortunes: async Postgres via sqlx4k.
implementation("com.netonstream:neton-database:$netonVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0")
// Encoding straight into a byte buffer, rather than to a String the
// response layer then re-encodes, is worth about 18% of the JSON
// path at the item counts this profile uses. Same serializer, same
// pipeline — only the intermediate String goes away.
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-io:1.11.0")
implementation("org.jetbrains.kotlinx:kotlinx-io-core:0.9.0")
}
}
macosArm64Main.get().dependsOn(nativeMain)
linuxX64Main.get().dependsOn(nativeMain)
linuxArm64Main.get().dependsOn(nativeMain)
}
}
46 changes: 46 additions & 0 deletions frameworks/neton-hyper/config/application.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# HTTP Arena configuration for the Neton entry.
#
# application.conf outranks the DSL, so the arena's port map and connection
# ceiling are set here rather than in code.

[application]
name = "neton-httparena"
debug = false

[server]
port = 8080
host = "0.0.0.0"

[http]
# 0 disables the per-request timeout. The wrapper costs two coroutine objects and
# their JobSupport state transitions on every request, and every arena profile
# already carries its own deadline, so a second one on the server buys nothing
# here. Shutdown draining is unaffected — the adapter keeps its own 5s grace.
timeout = 0
# Ceiling on requests in flight, not on connections: the framework's admission
# gate answers 503 the moment it is reached.
#
# It has to clear what the profiles actually offer, which for h2 is connections
# times streams, not connections:
#
# baseline / limited-conn 4096 x 1 = 4,096
# async 32000 x 1 = 32,000
# pipelined 4096 x 16 = 65,536
# json-h2c 4096 x 32 = 131,072
# baseline-h2c 4096 x 100 = 409,600 <- peak
#
# The previous 65,536 was the pipelined figure and nothing more; baseline-h2c at
# 4096 connections offered 409,600 and the entry shed 411,799 requests as 5xx,
# 55.7% of the run. The reported rps counts 2xx only, so the failures did not
# show up in the number — they were only visible in the saved status counters.
#
# 524,288 clears the peak with room to spare. The counter itself costs nothing;
# the memory belongs to the requests actually in flight, and the box has 251 GiB.
maxConnections = 524288
# Dynamic gzip response compression (beta15+): compressible responses are gzipped
# when the client sends Accept-Encoding. Required for json-comp.
enableCompression = true

[logging]
# Measured runs must not spend request time on log lines.
level = "WARN"
7 changes: 7 additions & 0 deletions frameworks/neton-hyper/config/database.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Postgres sidecar the arena starts for the DB profiles (async-db / fortunes).
# Host networking, so the framework reaches it on localhost:5432. Fixed
# credentials/db name are set by the harness (validate.sh / benchmark.sh).
[default]
driver = "POSTGRESQL"
uri = "postgresql://bench:bench@localhost:5432/benchmark"
debug = false
2 changes: 2 additions & 0 deletions frameworks/neton-hyper/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
kotlin.code.style=official
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading