Skip to content

Commit 7a404bf

Browse files
authored
Merge pull request #508 from tomas-langer/helidon-framework-tuned
Helidon - Java Microservice Framework, tuned type
2 parents d113556 + 8894fdc commit 7a404bf

94 files changed

Lines changed: 189079 additions & 5 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frameworks/helidon-production/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ The current subscribed benchmark profiles are:
2323
- `static-h2`
2424
- `unary-grpc`
2525
- `unary-grpc-tls`
26+
- `stream-grpc`
27+
- `stream-grpc-tls`
2628
- `echo-ws`
2729

2830
Profiles not currently supported here:
@@ -34,9 +36,9 @@ Profiles not currently supported here:
3436

3537
The benchmark wiring is split by listener:
3638

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

4143
Static content and TLS are configured from `application.yaml`, not
4244
programmatically.

frameworks/helidon-production/meta.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
"echo-ws",
2121
"unary-grpc",
2222
"unary-grpc-tls",
23+
"stream-grpc",
24+
"stream-grpc-tls",
2325
"async-db",
2426
"api-4",
2527
"api-16"

frameworks/helidon-production/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@
9595
<artifactId>slf4j-jdk14</artifactId>
9696
<scope>runtime</scope>
9797
</dependency>
98+
<dependency>
99+
<groupId>org.junit.jupiter</groupId>
100+
<artifactId>junit-jupiter</artifactId>
101+
<scope>test</scope>
102+
</dependency>
98103
</dependencies>
99104

100105
<build>

frameworks/helidon-production/src/main/java/com/httparena/BenchmarkGrpcService.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.httparena;
22

3+
import java.io.UncheckedIOException;
4+
35
import io.helidon.webserver.grpc.GrpcService;
46

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

2224
@Override
2325
public void update(Routing router) {
24-
router.unary("GetSum", this::getSum);
26+
router.unary("GetSum", this::getSum)
27+
.serverStream("StreamSum", this::streamSum);
2528
}
2629

27-
private void getSum(Benchmark.SumRequest request, StreamObserver<Benchmark.SumReply> observer) {
30+
void getSum(Benchmark.SumRequest request, StreamObserver<Benchmark.SumReply> observer) {
2831
complete(observer, Benchmark.SumReply.newBuilder()
2932
.setResult(request.getA() + request.getB())
3033
.build());
3134
}
35+
36+
void streamSum(Benchmark.StreamRequest request, StreamObserver<Benchmark.SumReply> observer) {
37+
try {
38+
int sum = request.getA() + request.getB();
39+
int count = Math.max(request.getCount(), 0);
40+
41+
for (int i = 0; i < count; i++) {
42+
observer.onNext(Benchmark.SumReply.newBuilder()
43+
.setResult(sum + i)
44+
.build());
45+
}
46+
47+
observer.onCompleted();
48+
} catch (UncheckedIOException e) {
49+
// this a connection close, we just ignore it
50+
return;
51+
}
52+
}
3253
}

frameworks/helidon-production/src/main/proto/benchmark.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@ package benchmark;
77

88
service BenchmarkService {
99
rpc GetSum (SumRequest) returns (SumReply);
10+
rpc StreamSum (StreamRequest) returns (stream SumReply);
1011
}
1112

1213
message SumRequest {
1314
int32 a = 1;
1415
int32 b = 2;
1516
}
1617

18+
message StreamRequest {
19+
int32 a = 1;
20+
int32 b = 2;
21+
int32 count = 3;
22+
}
23+
1724
message SumReply {
1825
int32 result = 1;
1926
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.httparena;
2+
3+
import java.io.IOException;
4+
import java.io.UncheckedIOException;
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
import benchmark.Benchmark;
9+
import io.grpc.stub.StreamObserver;
10+
import org.junit.jupiter.api.Test;
11+
12+
import static org.junit.jupiter.api.Assertions.assertEquals;
13+
import static org.junit.jupiter.api.Assertions.assertNull;
14+
15+
class BenchmarkGrpcServiceTest {
16+
@Test
17+
void streamSumEmitsExactSequence() throws Exception {
18+
RecordingObserver observer = new RecordingObserver();
19+
BenchmarkGrpcService service = new BenchmarkGrpcService();
20+
21+
service.streamSum(Benchmark.StreamRequest.newBuilder()
22+
.setA(13)
23+
.setB(42)
24+
.setCount(3)
25+
.build(),
26+
observer);
27+
28+
assertEquals(List.of(55, 56, 57), observer.results);
29+
assertEquals(1, observer.completedCount);
30+
assertNull(observer.error);
31+
}
32+
33+
@Test
34+
void streamSumAllowsEmptyStreamWhenCountIsZero() throws Exception {
35+
RecordingObserver observer = new RecordingObserver();
36+
BenchmarkGrpcService service = new BenchmarkGrpcService();
37+
38+
service.streamSum(Benchmark.StreamRequest.newBuilder()
39+
.setA(13)
40+
.setB(42)
41+
.setCount(0)
42+
.build(),
43+
observer);
44+
45+
assertEquals(List.of(), observer.results);
46+
assertEquals(1, observer.completedCount);
47+
assertNull(observer.error);
48+
}
49+
50+
@Test
51+
void streamSumStopsQuietlyWhenClientDisconnects() throws Exception {
52+
ThrowingObserver observer = new ThrowingObserver();
53+
BenchmarkGrpcService service = new BenchmarkGrpcService();
54+
55+
service.streamSum(Benchmark.StreamRequest.newBuilder()
56+
.setA(13)
57+
.setB(42)
58+
.setCount(3)
59+
.build(),
60+
observer);
61+
62+
assertEquals(List.of(55), observer.results);
63+
assertEquals(0, observer.completedCount);
64+
assertNull(observer.error);
65+
}
66+
67+
private static final class RecordingObserver implements StreamObserver<Benchmark.SumReply> {
68+
private final List<Integer> results = new ArrayList<>();
69+
private int completedCount;
70+
private Throwable error;
71+
72+
@Override
73+
public void onNext(Benchmark.SumReply value) {
74+
results.add(value.getResult());
75+
}
76+
77+
@Override
78+
public void onError(Throwable throwable) {
79+
error = throwable;
80+
}
81+
82+
@Override
83+
public void onCompleted() {
84+
completedCount++;
85+
}
86+
}
87+
88+
private static final class ThrowingObserver implements StreamObserver<Benchmark.SumReply> {
89+
private final List<Integer> results = new ArrayList<>();
90+
private int completedCount;
91+
private Throwable error;
92+
93+
@Override
94+
public void onNext(Benchmark.SumReply value) {
95+
results.add(value.getResult());
96+
throw new UncheckedIOException(new IOException("stream closed"));
97+
}
98+
99+
@Override
100+
public void onError(Throwable throwable) {
101+
error = throwable;
102+
}
103+
104+
@Override
105+
public void onCompleted() {
106+
completedCount++;
107+
}
108+
}
109+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM maven:3.9-eclipse-temurin-25 AS build
2+
WORKDIR /app
3+
COPY pom.xml .
4+
RUN mvn dependency:go-offline -q
5+
COPY src ./src
6+
RUN mvn package -DskipTests -q
7+
8+
FROM eclipse-temurin:25-jre
9+
WORKDIR /app
10+
COPY --from=build /app/target/helidon-httparena-tuned.jar app.jar
11+
COPY --from=build /app/target/libs ./libs
12+
EXPOSE 8080 8081 8443
13+
ENTRYPOINT ["java", "-jar", "app.jar"]

frameworks/helidon-tuned/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
Helidon Tuned
2+
----
3+
4+
# Project
5+
6+
This framework runs Helidon SE 4.4.1 on Níma WebServer as a `tuned`
7+
benchmark entry.
8+
9+
The current subscribed benchmark profiles are:
10+
11+
- `baseline`
12+
- `pipelined`
13+
- `limited-conn`
14+
- `json`
15+
- `json-comp`
16+
- `json-tls`
17+
- `upload`
18+
- `static`
19+
- `async-db`
20+
- `api-4`
21+
- `api-16`
22+
- `baseline-h2`
23+
- `static-h2`
24+
- `unary-grpc`
25+
- `unary-grpc-tls`
26+
- `stream-grpc`
27+
- `stream-grpc-tls`
28+
- `echo-ws`
29+
30+
Profiles not currently supported here:
31+
32+
- HTTP/3: `baseline-h3`, `static-h3`
33+
- `gateway-64`
34+
35+
# Listener layout
36+
37+
The benchmark wiring is split by listener:
38+
39+
- `8080` (`default`): HTTP/1.1 endpoints, cleartext gRPC for `unary-grpc` and `stream-grpc`, and WebSocket
40+
- `8081` (`h1-tls`): HTTP/1.1 + TLS for `json-tls`
41+
- `8443` (`h2-tls`): HTTP/2 + TLS for `baseline-h2`, `static-h2`, `unary-grpc-tls`, and `stream-grpc-tls`
42+
43+
TLS is configured from `application.yaml`. Static content is served
44+
programmatically from `/data/static`, reading from disk on each request while
45+
preferring precompressed `.br` / `.gz` variants and setting
46+
`Vary: Accept-Encoding`.
47+
48+
# Divergence from benchmark guidance
49+
50+
## `async-db` uses JDBC + HikariCP
51+
52+
The benchmark guidance for `async-db` prefers an async PostgreSQL driver.
53+
This Helidon entry currently uses the standard PostgreSQL JDBC driver with
54+
HikariCP.
55+
56+
That means the implementation is benchmark-contract correct, but it does not
57+
follow the async-driver recommendation literally. This is an intentional
58+
tradeoff for the current Helidon/Níma tuned entry.
59+
60+
Helidon WebServer is designed for Java Virtual Threads and optimized for blocking operations.

frameworks/helidon-tuned/meta.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"display_name": "helidon-tuned",
3+
"language": "Java",
4+
"type": "tuned",
5+
"engine": "níma",
6+
"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.",
7+
"repo": "https://github.com/helidon-io/helidon",
8+
"enabled": true,
9+
"tests": [
10+
"baseline",
11+
"pipelined",
12+
"limited-conn",
13+
"json",
14+
"json-comp",
15+
"json-tls",
16+
"upload",
17+
"static",
18+
"baseline-h2",
19+
"static-h2",
20+
"echo-ws",
21+
"unary-grpc",
22+
"unary-grpc-tls",
23+
"stream-grpc",
24+
"stream-grpc-tls",
25+
"async-db",
26+
"api-4",
27+
"api-16"
28+
],
29+
"maintainers": [
30+
"tomas-langer"
31+
]
32+
}

0 commit comments

Comments
 (0)