Hacker News new | ask | show | jobs
by reidbuzby 974 days ago
Hey everyone, I've recently created an open source library on top of grpc-kotlin and grpc-java that allows you to propagate a context across microservice boundaries throughout an entire request lifetime. The existing io.grpc.Context (https://grpc.github.io/grpc-java/javadoc/io/grpc/Context.htm...) only propagates a context across API boundaries within the same container and does not cross microservice boundaries.

It's called KonigKontext, check it out here: https://github.com/konigsoftware/konig-kontext!

Example use cases include:

Propagating security principals, or user credentials and identifiers throughout an entire request lifetime across all of your microservices. Propogating distributed tracing information. Set a request trace id upon receiving a request and later access that id in any downstream microservice. However, Konig Kontext is built to support any type of context value, so it can be extended to fit any specific use cases as well.

Let me know what you think!

2 comments

Why not just add this stuff to the request itself?
It avoids polluting your request classes with side channel details that your service may not even be concerned with at all; it makes for nice abstractions.

It allows for the opaque propagation of side channel information, where intermediates don't need to know what's there.

side-channel-with-well-defined-propagation-rules is pretty useful.

Adding hidden data to the request seems counter to the desire for a schema driven rpc. I guess it's a matter of taste whether some data is pollution or simply explicit. You're sending data that the service may or may not be concerned with whether its in the header or the body or some other side channel.

Maybe my reaction is a question of purity. I don't really see why one would think a request body should be in the schema but we should leave other data out of the schema. Wouldn't every single argument point to consistency?

I like gRPC and what it gives you. I personally would like that same explicit schema and type safety to apply to my tracing as well. Its interesting to me that others would draw a line.

Debug/tracing data is a great use case for context, for example. If propagating trace information has to be done manually, then you significantly weaken the utility of cross-service tracing. You'll always be questioning if lack of a trace means it didn't happen, or if it means someone forgot to pass along the trace context.

This applies especially to grpc, where everything is optional.

To be clear, I prefer explicit over implicit. But that doesn't always scale well to large orgs.

I think what's throwing me off is that the examples aren't just transparent things like debug/tracing. They mention credentials and user ids, which the app code can observe and rely on.

The combination of out-of-band/out-of-schema data with APIs to set and get from app code seems like a bad match.

I'd prefer if it was completely invisible to app code and existed only in interceptors.

While I agree, gRPC is based on protobufs, which are incredibly weakly typed. They specifically include mechanisms for passing through mysterious (not in the schema and of unknown type) data intact.

So, I’d argue the OP is using gRPC correctly, and I’d be using it wrong if I hadn’t given up on it long ago. I appreciate things like static type checking, and services that err on the side of rejecting unparsable requests in order to avoid data corruption. Protobufs/gRPC require heroic effort on the part of the RPC handler implementation if you want those things. In particular, it is easier to implement your own serialization format than typecheck the results returned by the APIs protoc emits.

You do get the same explicit schema and type safety with this library. The context values can be typed using a protobuf message: https://github.com/konigsoftware/konig-kontext#protobuf-mess..., or any other type you'd like. Although you are right, it is not explicitly in the request schema but that's kinda the point.
Because every single handler in a long chain of requests will have to explicitly support and propagate this data. The whole idea of a request context is to let you plumb low-cost metadata transparently through a call tree, and decouple your code from that metadata.
Very well articulated and agree.

It’s one of those things you eventually wish you had.

Ditto whats been said before. It can be very cumbersome to update multiple request/response types with new data especially if an intermediary service has no use for the data and is simply passing it through.
The header is fixed as konig-kontext-grpc-context [1]. Does that mean that you can only propagate one gRPC context object? Would it be possible to specify your own header string for the key, and have the interceptors support multiple keys? That sounds safer since the header name could both imply both the serialization and content-type, and Protobuf type_uri, etc. And sounds more useful since developers wouldn't need a catch-all type.

[1] https://github.com/konigsoftware/konig-kontext/blob/84faa627...

Support for multiple keys is in the works! See this issue for updates: https://github.com/konigsoftware/konig-kontext/issues/8