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
6 changes: 4 additions & 2 deletions frameworks/helidon-production/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ The current subscribed benchmark profiles are:
- `static-h2`
- `unary-grpc`
- `unary-grpc-tls`
- `stream-grpc`
- `stream-grpc-tls`
- `echo-ws`

Profiles not currently supported here:
Expand All @@ -34,9 +36,9 @@ Profiles not currently supported here:

The benchmark wiring is split by listener:

- `8080` (`default`): HTTP/1.1 endpoints, cleartext gRPC, and WebSocket
- `8080` (`default`): HTTP/1.1 endpoints, cleartext gRPC for `unary-grpc` and `stream-grpc`, and WebSocket
- `8081` (`h1-tls`): HTTP/1.1 + TLS for `json-tls`
- `8443` (`h2-tls`): HTTP/2 + TLS for `baseline-h2`, `static-h2`, and TLS gRPC
- `8443` (`h2-tls`): HTTP/2 + TLS for `baseline-h2`, `static-h2`, `unary-grpc-tls`, and `stream-grpc-tls`

Static content and TLS are configured from `application.yaml`, not
programmatically.
Expand Down
2 changes: 2 additions & 0 deletions frameworks/helidon-production/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"echo-ws",
"unary-grpc",
"unary-grpc-tls",
"stream-grpc",
"stream-grpc-tls",
"async-db",
"api-4",
"api-16"
Expand Down
5 changes: 5 additions & 0 deletions frameworks/helidon-production/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@
<artifactId>slf4j-jdk14</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.httparena;

import java.io.UncheckedIOException;

import io.helidon.webserver.grpc.GrpcService;

import benchmark.Benchmark;
Expand All @@ -21,12 +23,31 @@ public Descriptors.FileDescriptor proto() {

@Override
public void update(Routing router) {
router.unary("GetSum", this::getSum);
router.unary("GetSum", this::getSum)
.serverStream("StreamSum", this::streamSum);
}

private void getSum(Benchmark.SumRequest request, StreamObserver<Benchmark.SumReply> observer) {
void getSum(Benchmark.SumRequest request, StreamObserver<Benchmark.SumReply> observer) {
complete(observer, Benchmark.SumReply.newBuilder()
.setResult(request.getA() + request.getB())
.build());
}

void streamSum(Benchmark.StreamRequest request, StreamObserver<Benchmark.SumReply> observer) {
try {
int sum = request.getA() + request.getB();
int count = Math.max(request.getCount(), 0);

for (int i = 0; i < count; i++) {
observer.onNext(Benchmark.SumReply.newBuilder()
.setResult(sum + i)
.build());
}

observer.onCompleted();
} catch (UncheckedIOException e) {
// this a connection close, we just ignore it
return;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ package benchmark;

service BenchmarkService {
rpc GetSum (SumRequest) returns (SumReply);
rpc StreamSum (StreamRequest) returns (stream SumReply);
}

message SumRequest {
int32 a = 1;
int32 b = 2;
}

message StreamRequest {
int32 a = 1;
int32 b = 2;
int32 count = 3;
}

message SumReply {
int32 result = 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.httparena;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;

import benchmark.Benchmark;
import io.grpc.stub.StreamObserver;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

class BenchmarkGrpcServiceTest {
@Test
void streamSumEmitsExactSequence() throws Exception {
RecordingObserver observer = new RecordingObserver();
BenchmarkGrpcService service = new BenchmarkGrpcService();

service.streamSum(Benchmark.StreamRequest.newBuilder()
.setA(13)
.setB(42)
.setCount(3)
.build(),
observer);

assertEquals(List.of(55, 56, 57), observer.results);
assertEquals(1, observer.completedCount);
assertNull(observer.error);
}

@Test
void streamSumAllowsEmptyStreamWhenCountIsZero() throws Exception {
RecordingObserver observer = new RecordingObserver();
BenchmarkGrpcService service = new BenchmarkGrpcService();

service.streamSum(Benchmark.StreamRequest.newBuilder()
.setA(13)
.setB(42)
.setCount(0)
.build(),
observer);

assertEquals(List.of(), observer.results);
assertEquals(1, observer.completedCount);
assertNull(observer.error);
}

@Test
void streamSumStopsQuietlyWhenClientDisconnects() throws Exception {
ThrowingObserver observer = new ThrowingObserver();
BenchmarkGrpcService service = new BenchmarkGrpcService();

service.streamSum(Benchmark.StreamRequest.newBuilder()
.setA(13)
.setB(42)
.setCount(3)
.build(),
observer);

assertEquals(List.of(55), observer.results);
assertEquals(0, observer.completedCount);
assertNull(observer.error);
}

private static final class RecordingObserver implements StreamObserver<Benchmark.SumReply> {
private final List<Integer> results = new ArrayList<>();
private int completedCount;
private Throwable error;

@Override
public void onNext(Benchmark.SumReply value) {
results.add(value.getResult());
}

@Override
public void onError(Throwable throwable) {
error = throwable;
}

@Override
public void onCompleted() {
completedCount++;
}
}

private static final class ThrowingObserver implements StreamObserver<Benchmark.SumReply> {
private final List<Integer> results = new ArrayList<>();
private int completedCount;
private Throwable error;

@Override
public void onNext(Benchmark.SumReply value) {
results.add(value.getResult());
throw new UncheckedIOException(new IOException("stream closed"));
}

@Override
public void onError(Throwable throwable) {
error = throwable;
}

@Override
public void onCompleted() {
completedCount++;
}
}
}
13 changes: 13 additions & 0 deletions frameworks/helidon-tuned/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM maven:3.9-eclipse-temurin-25 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -q
COPY src ./src
RUN mvn package -DskipTests -q

FROM eclipse-temurin:25-jre
WORKDIR /app
COPY --from=build /app/target/helidon-httparena-tuned.jar app.jar
COPY --from=build /app/target/libs ./libs
EXPOSE 8080 8081 8443
ENTRYPOINT ["java", "-jar", "app.jar"]
60 changes: 60 additions & 0 deletions frameworks/helidon-tuned/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
Helidon Tuned
----

# Project

This framework runs Helidon SE 4.4.1 on Níma WebServer as a `tuned`
benchmark entry.

The current subscribed benchmark profiles are:

- `baseline`
- `pipelined`
- `limited-conn`
- `json`
- `json-comp`
- `json-tls`
- `upload`
- `static`
- `async-db`
- `api-4`
- `api-16`
- `baseline-h2`
- `static-h2`
- `unary-grpc`
- `unary-grpc-tls`
- `stream-grpc`
- `stream-grpc-tls`
- `echo-ws`

Profiles not currently supported here:

- HTTP/3: `baseline-h3`, `static-h3`
- `gateway-64`

# Listener layout

The benchmark wiring is split by listener:

- `8080` (`default`): HTTP/1.1 endpoints, cleartext gRPC for `unary-grpc` and `stream-grpc`, and WebSocket
- `8081` (`h1-tls`): HTTP/1.1 + TLS for `json-tls`
- `8443` (`h2-tls`): HTTP/2 + TLS for `baseline-h2`, `static-h2`, `unary-grpc-tls`, and `stream-grpc-tls`

TLS is configured from `application.yaml`. Static content is served
programmatically from `/data/static`, reading from disk on each request while
preferring precompressed `.br` / `.gz` variants and setting
`Vary: Accept-Encoding`.

# Divergence from benchmark guidance

## `async-db` uses JDBC + HikariCP

The benchmark guidance for `async-db` prefers an async PostgreSQL driver.
This Helidon entry currently uses the standard PostgreSQL JDBC driver with
HikariCP.

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.

Helidon WebServer is designed for Java Virtual Threads and optimized for blocking operations.
32 changes: 32 additions & 0 deletions frameworks/helidon-tuned/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"display_name": "helidon-tuned",
"language": "Java",
"type": "tuned",
"engine": "níma",
"description": "Helidon SE 4.4.1 on Níma WebServer with Java 25, tuned socket options, precompressed static content, HTTP/2, gRPC, and WebSocket support.",
"repo": "https://github.com/helidon-io/helidon",
"enabled": true,
"tests": [
"baseline",
"pipelined",
"limited-conn",
"json",
"json-comp",
"json-tls",
"upload",
"static",
"baseline-h2",
"static-h2",
"echo-ws",
"unary-grpc",
"unary-grpc-tls",
"stream-grpc",
"stream-grpc-tls",
"async-db",
"api-4",
"api-16"
],
"maintainers": [
"tomas-langer"
]
}
Loading