Skip to content
Open
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
9 changes: 8 additions & 1 deletion frameworks/helidon-production/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The current subscribed benchmark profiles are:
- `latency-10k`
- `pipelined`
- `limited-conn`
- `async`
- `json-comp`
- `json-tls`
- `static-tls`
Expand All @@ -30,7 +31,7 @@ The current subscribed benchmark profiles are:

Profiles not currently supported here:

- application profiles: `async`, `fortunes`
- application profiles: `fortunes`
- HTTP/3: `baseline-h3`, `static-h3`
- composed deployments: `gateway-64`, `gateway-h3`, `production-stack`

Expand Down Expand Up @@ -58,4 +59,10 @@ That means the implementation is benchmark-contract correct, but it does not
follow the async-driver recommendation literally. This is an intentional
tradeoff for the current Helidon/Níma production entry.

## `async` uses virtual threads

Helidon WebServer is designed for Java Virtual Threads and optimized for blocking operations.

The `async` delay handler uses `Thread.sleep` on Helidon's request virtual
thread. The virtual thread is suspended for the requested delay without
occupying its carrier thread.
1 change: 1 addition & 0 deletions frameworks/helidon-production/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"baseline", "latency-1m", "latency-10k",
"pipelined",
"limited-conn",
"async",
"json-comp",
"json-tls",
"static-tls",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

class BaselineHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_TEXT_PLAIN);

UriQuery query = req.query();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

class BaselinePostHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_TEXT_PLAIN);

UriQuery query = req.query();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_JSON;

class DbHandler implements Handler {
Expand Down Expand Up @@ -47,16 +46,13 @@ class DbHandler implements Handler {

@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);

if (!databaseConfigured) {
// if not configured, fail (maybe local invocation, or manual command line invocation)
res.status(Status.INTERNAL_SERVER_ERROR_500)
.send();
return;
}

res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_JSON);

HikariDataSource dataSource = database();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.httparena;

import java.nio.charset.StandardCharsets;

import io.helidon.webserver.http.Handler;
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

final class DelayHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) throws InterruptedException {
int delay = Integer.parseInt(req.path().pathParameters().get("ms"));

Thread.sleep(delay);

res.header(CONTENT_TYPE_TEXT_PLAIN);
res.send(Integer.toString(delay).getBytes(StandardCharsets.US_ASCII));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;

class EchoHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header("Content-Type", "application/octet-stream");

// Collected: the response needs a Content-Length, and a chunked
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_JSON;

class JsonHandler implements Handler {
Expand All @@ -24,7 +23,6 @@ class JsonHandler implements Handler {

@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_JSON);

int requestedCount = req.path().pathParameters().first("count")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package com.httparena;

import io.helidon.config.Config;
import io.helidon.http.Header;
import io.helidon.http.HeaderNames;
import io.helidon.http.HeaderValues;
import io.helidon.logging.common.LogConfig;
import io.helidon.webserver.WebServer;
import io.helidon.webserver.grpc.GrpcRouting;
import io.helidon.webserver.websocket.WsRouting;

public final class Main {
static final Header SERVER_HEADER = HeaderValues.createCached(HeaderNames.SERVER, "helidon");

static {
LogConfig.initClass();
}
Expand All @@ -37,6 +32,7 @@ public static void main(String[] args) throws Exception {
builder.routing(httpRouting -> httpRouting
.get("/pipeline", new PipelineHandler())
.get("/baseline11", baselineHandler)
.get("/delay/{ms}", new DelayHandler())
.get("/json/{count}", jsonHandler)
.get("/json", jsonHandler)
.post("/baseline11", new BaselinePostHandler())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

class PipelineHandler implements Handler {
Expand All @@ -18,7 +17,6 @@ class PipelineHandler implements Handler {

@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_TEXT_PLAIN);
res.header(CONTENT_LENGTH);
res.send(RESPONSE);
Expand Down
12 changes: 11 additions & 1 deletion frameworks/helidon-tuned/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The current subscribed benchmark profiles are:
- `latency-10k`
- `pipelined`
- `limited-conn`
- `async`
- `json-comp`
- `json-tls`
- `static-tls`
Expand All @@ -30,7 +31,7 @@ The current subscribed benchmark profiles are:

Profiles not currently supported here:

- application profiles: `async`, `fortunes`
- application profiles: `fortunes`
- HTTP/3: `baseline-h3`, `static-h3`
- composed deployments: `gateway-64`, `gateway-h3`, `production-stack`

Expand All @@ -56,6 +57,9 @@ HTTP/2 framing, pseudo-header, and connection-header validation remains
enabled. This benchmark-specific setting applies only to `helidon-tuned`;
`helidon-production` retains the standard validation configuration.

The `h1-tls` listener also enables `TCP_NODELAY` for the `json-tls`,
`static-tls`, and `8gbit` profiles.

# Divergence from benchmark guidance

## `async-db` uses JDBC + HikariCP
Expand All @@ -68,4 +72,10 @@ That means the implementation is benchmark-contract correct, but it does not
follow the async-driver recommendation literally. This is an intentional
tradeoff for the current Helidon/Níma tuned entry.

## `async` uses virtual threads

Helidon WebServer is designed for Java Virtual Threads and optimized for blocking operations.

The `async` delay handler uses `Thread.sleep` on Helidon's request virtual
thread. The virtual thread is suspended for the requested delay without
occupying its carrier thread.
1 change: 1 addition & 0 deletions frameworks/helidon-tuned/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"baseline", "latency-1m", "latency-10k",
"pipelined",
"limited-conn",
"async",
"json-comp",
"json-tls",
"static-tls",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

class BaselineHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_TEXT_PLAIN);

UriQuery query = req.query();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

class BaselinePostHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_TEXT_PLAIN);

UriQuery query = req.query();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_JSON;

class DbHandler implements Handler {
Expand Down Expand Up @@ -47,16 +46,13 @@ class DbHandler implements Handler {

@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);

if (!databaseConfigured) {
// if not configured, fail (maybe local invocation, or manual command line invocation)
res.status(Status.INTERNAL_SERVER_ERROR_500)
.send();
return;
}

res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_JSON);

HikariDataSource dataSource = database();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.httparena;

import java.nio.charset.StandardCharsets;

import io.helidon.webserver.http.Handler;
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

final class DelayHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) throws InterruptedException {
int delay = Integer.parseInt(req.path().pathParameters().get("ms"));

Thread.sleep(delay);

res.header(CONTENT_TYPE_TEXT_PLAIN);
res.send(Integer.toString(delay).getBytes(StandardCharsets.US_ASCII));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;

class EchoHandler implements Handler {
@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header("Content-Type", "application/octet-stream");

// Collected: the response needs a Content-Length, and a chunked
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_JSON;

class JsonHandler implements Handler {
Expand All @@ -37,7 +36,6 @@ class JsonHandler implements Handler {

@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_JSON);

int requestedCount = req.path().pathParameters().first("count")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package com.httparena;

import io.helidon.config.Config;
import io.helidon.http.Header;
import io.helidon.http.HeaderNames;
import io.helidon.http.HeaderValues;
import io.helidon.logging.common.LogConfig;
import io.helidon.webserver.WebServer;
import io.helidon.webserver.grpc.GrpcRouting;
import io.helidon.webserver.websocket.WsRouting;

public final class Main {
static final Header SERVER_HEADER = HeaderValues.createCached(HeaderNames.SERVER, "helidon");

static {
LogConfig.initClass();
}
Expand All @@ -38,6 +33,7 @@ public static void main(String[] args) throws Exception {
builder.routing(httpRouting -> httpRouting
.get("/pipeline", new PipelineHandler())
.get("/baseline11", baselineHandler)
.get("/delay/{ms}", new DelayHandler())
.get("/json/{count}", jsonHandler)
.get("/json", jsonHandler)
.get("/static/{filename}", staticHandler)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;

import static com.httparena.Main.SERVER_HEADER;
import static io.helidon.http.HeaderValues.CONTENT_TYPE_TEXT_PLAIN;

class PipelineHandler implements Handler {
Expand All @@ -18,7 +17,6 @@ class PipelineHandler implements Handler {

@Override
public void handle(ServerRequest req, ServerResponse res) {
res.header(SERVER_HEADER);
res.header(CONTENT_TYPE_TEXT_PLAIN);
res.header(CONTENT_LENGTH);
res.send(RESPONSE);
Expand Down
Loading