feat: Add new grpc health check#483
Conversation
This PR adds the ability to use grpc health checks following the grpc standard.
…wer cognitive complexity
|
F1bonacc1
left a comment
There was a problem hiding this comment.
Hi @deedubs - thanks for putting this together, this is a real gap and the implementation is clean. Before going deeper on the details though, I want to raise one architectural question that I think changes how this should be structured.
Should the gRPC checker live in F1bonacc1/go-health (https://github.com/F1bonacc1/go-health) instead?
Looking at how the existing probes are wired:
http_getdelegates tof1bonacc1/go-health/v2/checkers.NewHTTP- the actual checker lives upstreamexecis local to process-compose because it depends on command.ShellConfig, env, etc.grpcis local, but thegrpcCheckertype has no process-compose-specific dependencies - it's a pure dial +grpc.health.v1.Health/Checkimplementation
go-health already ships pre-built checkers for HTTP, SQL, Redis, Mongo, Memcache, disk, reachable - gRPC fits that pattern much more naturally than it fits here. Some concrete benefits to splitting it:
- Dependency placement - google.golang.org/grpc is a sizable tree. Putting it in
go-healthmeans library consumers opt into it; in process-compose every user pays the cost regardless of whether they use gRPC probes. - Consistency - future maintainers won't have to wonder why two of three probes delegate to
go-healthand one doesn't. - Reusability -
go-healthhas its own users who'd benefit. - Smaller surface here - this PR shrinks to roughly the
GrpcProbeconfig struct,validateAndSetGrpcDefaults, and agetGrpcCheckerthat callscheckers.NewGRPC(...)- maybe ~30 lines plus the YAML/JSON wiring.
Would you be open to splitting this into two PRs?
F1bonacc1/go-health: add checkers/grpc.go (or checkers/grpc/) exposing NewGRPC(*GRPCConfig) (ICheckable, error). TLS support, connection lifecycle, etc. get designed there in the right context.F1bonacc1/process-compose: bump go-health and add the config + delegation.
Happy to give detailed feedback on the current diff (schema/docs updates, go mod tidy for the // indirect grpc dep, TLS, port validation, connection-per-probe) once we settle on the structural direction - didn't want to dive into those if the architecture itself is going to shift.
Either way, thanks again for the contribution - solving the "gRPC service with no HTTP endpoint" case natively is a clear win.
| g.NumPort, _ = strconv.Atoi(g.Port) | ||
| } | ||
| if g.NumPort < 1 || g.NumPort > 65535 { | ||
| g.NumPort = 0 |
There was a problem hiding this comment.
This will fail silently with an unclear message to the user.
It looks the same as how it is handled for HTTP, but in fact, in HTTP, no port is a valid case as it falls back to the scheme default:
if h.NumPort == 0 {
urlStr = fmt.Sprintf("%s://%s%s", h.Scheme, h.Host, h.Path)
}| golang.org/x/sync v0.20.0 // indirect | ||
| golang.org/x/text v0.36.0 // indirect | ||
| golang.org/x/tools v0.44.0 // indirect | ||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect |
There was a problem hiding this comment.
This is a direct dependency.
Did you run go mod tidy?
| } | ||
| defer conn.Close() | ||
|
|
||
| client := healthpb.NewHealthClient(conn) |
There was a problem hiding this comment.
Do you think we can preserve the connection and not recreate it on every status tick?
| healthServer.SetServingStatus("my.service", healthpb.HealthCheckResponse_SERVING) | ||
| healthpb.RegisterHealthServer(srv, healthServer) | ||
|
|
||
| go srv.Serve(lis) |
There was a problem hiding this comment.
Please check the err here.
Also, there is a chance the test will start running before the server serves. Consider a WaitGroup here
| healthServer.SetServingStatus("", healthpb.HealthCheckResponse_NOT_SERVING) | ||
| healthpb.RegisterHealthServer(srv, healthServer) | ||
|
|
||
| go srv.Serve(lis) |



We are looking to use process-compose to run grpc processes that don't have http endpoints for health checking.
While we do have a workable solution in using
grpcurl1 it didn't feel like a big lift to add this natively.This PR adds the ability to use grpc health checks following the grpc standard. It attempts to follow a similar api to that of F1bonacc1/go-health
Footnotes
Example using exec.command: grpcurl -plaintext -connect-timeout 2 localhost:9990 grpc.health.v1.Health/Check ↩