Skip to content

Commit f66f958

Browse files
MDA2AVclaude
andauthored
Add fishcake (#796)
* Add fishcake framework: CodeGreen (Kotlin GenHTTP port) A Kotlin entry running CodeGreen — a 1:1 port of GenHTTP — on its internal Netty-based engine, configured like the genhttp-11 entry. - Composite Gradle build: CodeGreen is pulled in via includeBuild; the Dockerfile clones its `port` branch and builds a shadow fat jar (JDK 21 runtime). - Endpoints: /pipeline, /baseline11 (GET+POST), /baseline2, /json/:count?m=, /upload, /async-db, /crud/items (list, cached get with X-Cache, upsert, update). - Postgres via JDBC + HikariCP (DATABASE_URL contract), kotlinx.serialization, 200 ms TTL cache for single-item reads. - meta.json declares baseline, pipelined, limited-conn, json, upload, async-db, crud, api-4 and api-16. TLS/HTTP-2, compression, static files, websockets and gRPC are omitted (those modules are not part of the port yet). 🤖 Generated with [Claude Code](https://claude.com/claude-code) * add fishcake * Revert unrelated aspnet-minimal Twinflow changes These experimental edits (a Twinflow package reference and a commented-out UseTwinflow call) are unrelated to fishcake and were swept into the previous commit. Restore Program.cs and aspnet-minimal.csproj to their upstream state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix fishcake single-core bottleneck: drop io.netty.eventLoopThreads=0 CodeGreen's engine builds its worker pool with a bare NioEventLoopGroup(), whose size is Netty's DEFAULT_EVENT_LOOP_THREADS = max(1, io.netty.eventLoopThreads ?: cores*2). The JVM flag set it to 0, so max(1,0)=1 — the whole server ran on a single event-loop thread (handlers execute on the channel's event loop), pinning it to one core. Removing the flag restores the 2x-cores default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fishcake: stream uploads, build against CodeGreen stream-bodies Upload.compute now reads the request body on Dispatchers.IO (the streamed body must be drained off the event loop) and is suspend. The Dockerfile clones CodeGreen's stream-bodies branch, which streams large request bodies instead of buffering them — fixing the upload profile's memory blow-up. Folds back to port once benchmarked. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 085aaab commit f66f958

25 files changed

Lines changed: 1078 additions & 3 deletions

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,27 @@ Add your GitHub username to the `maintainers` array in your framework's `meta.js
6868
```json
6969
"maintainers": ["your-github-username"]
7070
```
71+
72+
## Add the badge
73+
74+
Benchmarked on HttpArena? Add the badge to your project's README — it links to the live leaderboard and adapts to light & dark themes automatically.
75+
76+
```md
77+
[![Benchmarked by HttpArena](https://cdn.jsdelivr.net/gh/MDA2AV/httparena-badge/wordmark.svg)](https://www.http-arena.com/leaderboard/)
78+
```
79+
80+
Prefer HTML, e.g. to set the size:
81+
82+
```html
83+
<a href="https://www.http-arena.com/leaderboard/">
84+
<img src="https://cdn.jsdelivr.net/gh/MDA2AV/httparena-badge/wordmark.svg" alt="Benchmarked by HttpArena" height="44">
85+
</a>
86+
```
87+
88+
---
89+
90+
<div align="left">
91+
<a href="https://www.http-arena.com/leaderboard/">
92+
<img alt="Benchmarked by HttpArena" src="https://cdn.jsdelivr.net/gh/MDA2AV/httparena-badge/wordmark.svg" width="235">
93+
</a>
94+
</div>

frameworks/fishcake/.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
codegreen/
2+
.gradle/
3+
build/
4+
.git

frameworks/fishcake/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
codegreen/
2+
.gradle/
3+
build/

frameworks/fishcake/Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# ---- Build: clone the CodeGreen port and build a fat jar via a Gradle composite build ----
2+
FROM eclipse-temurin:21-jdk AS build
3+
RUN apt-get update \
4+
&& apt-get install -y --no-install-recommends git \
5+
&& rm -rf /var/lib/apt/lists/*
6+
WORKDIR /app
7+
8+
COPY . .
9+
10+
# CodeGreen (the Kotlin GenHTTP port) is consumed as a composite build, wired in
11+
# settings.gradle.kts via includeBuild("codegreen"). Pinned to the stream-bodies branch
12+
# while the request-body streaming work is benchmarked; folds back into port once proven.
13+
RUN git clone --depth 1 --branch stream-bodies https://github.com/dotnet-web-stack/CodeGreen.git codegreen
14+
15+
RUN chmod +x ./gradlew && ./gradlew shadowJar --no-daemon
16+
17+
# ---- Runtime ----
18+
FROM eclipse-temurin:21-jre
19+
WORKDIR /app
20+
COPY --from=build /app/build/libs/fishcake.jar .
21+
EXPOSE 8080
22+
ENTRYPOINT ["java", \
23+
"-server", \
24+
"-XX:+UseG1GC", \
25+
"-XX:+UseNUMA", \
26+
"-XX:+AlwaysPreTouch", \
27+
"-XX:-OmitStackTraceInFastThrow", \
28+
"-Dio.netty.buffer.checkBounds=false", \
29+
"-Dio.netty.buffer.checkAccessible=false", \
30+
"-Dio.netty.leakDetection.level=disabled", \
31+
"-Dio.netty.recycler.maxCapacityPerThread=4096", \
32+
"-jar", "fishcake.jar"]

frameworks/fishcake/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# fishcake
2+
3+
A HttpArena entry for **CodeGreen** — a 1:1 Kotlin port of the C# [GenHTTP](https://github.com/Kaliumhexacyanoferrat/GenHTTP)
4+
web server — running on its internal Netty-based engine. It is configured like the
5+
sibling [`genhttp-11`](../genhttp-11) entry, rebuilt on the port's webservice stack
6+
(Conversion + Reflection + Webservices + Layouting).
7+
8+
## Stack
9+
10+
- **Language:** Kotlin / JDK 21
11+
- **Framework:** CodeGreen (Kotlin port of GenHTTP)
12+
- **Engine:** CodeGreen internal engine (non-blocking, Netty)
13+
- **Serialization:** kotlinx.serialization
14+
- **Database:** PostgreSQL via JDBC + HikariCP (queried directly, as `genhttp-11` uses Npgsql)
15+
- **Build:** Gradle composite build — the app pulls CodeGreen in via `includeBuild`
16+
17+
## Endpoints
18+
19+
| Endpoint | Method | Description |
20+
|----------|--------|-------------|
21+
| `/pipeline` | GET | Returns `ok` (plain text) |
22+
| `/baseline11` | GET | Sums query parameters `a` + `b` |
23+
| `/baseline11` | POST | Sums `a` + `b` + a value read from the body |
24+
| `/baseline2` | GET | Sums query parameters `a` + `b` |
25+
| `/json/{count}` | GET | Processes the first `count` dataset items; `?m=` scales each total (default 1) |
26+
| `/upload` | POST | Drains the request body, returns the byte count |
27+
| `/async-db` | GET | `price BETWEEN min AND max` range query (sequential scan) |
28+
| `/crud/items` | GET | Paged listing by category |
29+
| `/crud/items/{id}` | GET | Cached single-item read (`X-Cache: HIT\|MISS`) |
30+
| `/crud/items` | POST | Upsert, returns `201 Created` |
31+
| `/crud/items/{id}` | PUT | Partial update, `404` when the id is unknown |
32+
33+
## Profiles
34+
35+
Declared in `meta.json`: `baseline`, `pipelined`, `limited-conn`, `json`, `upload`,
36+
`async-db`, `crud`, `api-4`, `api-16`.
37+
38+
**Not implemented** — these depend on modules the port does not provide yet, so they are
39+
left out of `meta.json`:
40+
41+
- `json-comp` — response compression (Compression module)
42+
- `json-tls`, `baseline-h2`, `static-h2`, `baseline-h2c`, `json-h2c` — TLS / HTTP-2
43+
- `static` — static file serving (Files module)
44+
- `echo-ws` — WebSockets module
45+
- `fortunes` — HTML templating
46+
- HTTP/3 and gRPC profiles
47+
48+
## Notes
49+
50+
- The single-item CRUD read uses an in-process 200 ms TTL cache-aside (mirrors the C# entry's `MemoryCache`).
51+
- `DATABASE_URL`, `DATASET_PATH` and `DATABASE_MAX_CONN` follow the standard HttpArena contract.
52+
Without `DATABASE_URL`, the database-backed endpoints degrade gracefully (empty results / `404`).
53+
- The Docker build clones CodeGreen from its `port` branch and builds it as a composite build,
54+
so that branch must contain the webservice stack (currently in PR — `dotnet-web-stack/CodeGreen`).
55+
56+
## Build & run locally
57+
58+
The app consumes CodeGreen as a composite build. Point at a local checkout:
59+
60+
```sh
61+
./gradlew shadowJar -PcodegreenDir=/path/to/CodeGreen
62+
DATASET_PATH=../../data/dataset.json java -jar build/libs/fishcake.jar
63+
```
64+
65+
In Docker the CodeGreen checkout is cloned to `./codegreen` automatically (see `Dockerfile`).
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
plugins {
2+
kotlin("jvm") version "2.1.20"
3+
kotlin("plugin.serialization") version "2.1.20"
4+
application
5+
id("com.gradleup.shadow") version "8.3.5"
6+
}
7+
8+
group = "com.httparena.fishcake"
9+
version = "1.0.0"
10+
11+
repositories {
12+
mavenCentral()
13+
}
14+
15+
dependencies {
16+
// CodeGreen modules (substituted from the included build by group:name).
17+
implementation("org.codegreen:internal:0.1.0") // engine:internal — Host
18+
implementation("org.codegreen:api:0.1.0")
19+
implementation("org.codegreen:io:0.1.0")
20+
implementation("org.codegreen:layouting:0.1.0")
21+
implementation("org.codegreen:webservices:0.1.0")
22+
implementation("org.codegreen:reflection:0.1.0")
23+
implementation("org.codegreen:conversion:0.1.0")
24+
25+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
26+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
27+
28+
// Postgres for the async-db / crud profiles, queried directly (as genhttp-11 uses Npgsql).
29+
implementation("org.postgresql:postgresql:42.7.4")
30+
implementation("com.zaxxer:HikariCP:6.2.1")
31+
}
32+
33+
application {
34+
mainClass.set("com.httparena.fishcake.MainKt")
35+
}
36+
37+
kotlin {
38+
jvmToolchain(21)
39+
}
40+
41+
tasks.shadowJar {
42+
archiveFileName.set("fishcake.jar")
43+
mergeServiceFiles()
44+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
kotlin.code.style=official
2+
org.gradle.jvmargs=-Xmx2g
42.7 KB
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4+
networkTimeout=10000
5+
validateDistributionUrl=true
6+
zipStoreBase=GRADLE_USER_HOME
7+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)