I’ve deployed 2 variants (Go|Rust) of a gRPC server that implements gRPC Health Checking protocol.
The Health
service implements a unary Check
and a server-streaming Watch
method.
The unary Check
works without issue (other than the issue with reflection per link).
ENDPOINT="healthcheck-dazwilkin.koyeb.app:443" # Golang
ENDPOINT="healthcheck-rust-dazwilkin.koyeb.app:443" # Rust
SERVICE="grpc.health.v1.Health"
METHOD="Check"
grpcurl \
--proto health.proto \
${ENDPOINT} \
${SERVICE}/${METHOD}
{
"status": "SERVING"
}
However, the server-streaming Watch
method fails (on both servers). Neither service is able to stream more than ~8 messages before being terminated. The implementations both sleep (15 seconds) between messages (8*15 = 120 seconds = 2 minutes!?):
METHOD="Watch"
grpcurl \
--proto health.proto \
${ENDPOINT} \
${SERVICE}/${METHOD}
Golang:
{
"status": "SERVING"
}
{
"status": "NOT_SERVING"
}
{
"status": "SERVING"
}
{
}
{
"status": "SERVING"
}
{
"status": "NOT_SERVING"
}
ERROR:
Code: Internal
Message: stream terminated by RST_STREAM with error code: INTERNAL_ERROR
NOTE The Golang implementation randomizes the service status
Rust:
{
"status": "SERVING"
}
{
"status": "SERVING"
}
{
"status": "SERVING"
}
{
"status": "SERVING"
}
{
"status": "SERVING"
}
{
"status": "SERVING"
}
{
"status": "SERVING"
}
{
"status": "SERVING"
}
ERROR:
Code: Internal
Message: stream terminated by RST_STREAM with error code: INTERNAL_ERROR
The Golang service logs no (obvious) errors but the Rust code panics (server.rs:90
shown below) which should not error:
thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: SendError(Ok(HealthCheckResponse { status: Serving }))', src/server.rs:90:18
tokio::spawn(async move {
loop {
println!("[watch] Sending");
tx.send(Ok(HealthCheckResponse {
status: ServingStatus::Serving as i32,
}))
.await
.unwrap(); // line #90
println!("[watch] Sent");
println!("[watch] Sleeping");
sleep(Duration::from_secs(15)).await;
}
});
Both servers are Nano instances but are well-within instance capacity (CPU: ~0%; Memory: ~0.5MB Rust ~3.0MB Golang)