Live data from Hacker News

Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

cloud.google.com

271–280 of 286 posts

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#271

As someone who has worked at a few of the FAANGs, having thrift/grpc is a godsend for internal service routing, but a lot of the complexity is managed by teams building the libraries, creating the service discovery layers, doing the routing etc. But using an RPC protocol enables those things to happen on a much greater scale and speed than you could ever do with your typical JSON/REST service. I've also never seen a…

> thrift/grpc is a godsend for internal service routing

Compared to what? What else did you try?

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#272

Earlier quoted context omitted.

Nothing in Protobuf is suited for streaming. It's anti-streaming compared to almost any binary protocol you can imagine (unless you want to stream VHD, which would be a sad joke... for another time).

> Nothing in Protobuf is suited for streaming. Uhh... Why? Protobuf supports streaming replies and requests. Do you mean that you need to know the message size in advance?

No, Protobuf doesn't support streaming.

Streaming means that it's possible to process the payload in small chunks, preferably of fixed size. Here are some examples of formats that can be considered streaming:

* IP protocol. Comes in uniformly sized chunks, payload doesn't have a concept of "headers". Doesn't even have to come in any particular order (which might be both a curse and a blessing for streaming).

* MP4 format. Comes in frames, not necessarily uniformly sized, but more-or-less uniform (the payload size will vary based on compression outcome, but will generally be within certain size). However, it has a concept of "headers", so must be streamed from a certain position onward. There's no way to jump into the middle and start streaming from there. If the "header" was lost, it's not possible to resume.

* Sun RPC, specifically the part that's used in NFS. Payload is wildly variable in size and function, but when it comes to transferring large files, it still can be streamed. Reordering is possible to a degree, but the client / server need to keep count of messages received, also are able to resume with minimal re-negotiation (not all data needs to be re-synced in order to resume).

Protobuf, in principle, cannot be processed unless the entire message has been received (because, by design, the keys in messages don't have to be unique, and the last one wins). Messages are hierarchical, so, there's no way to split them into fixed or near-fixed size chunks. Metadata must be communicated separately, ahead of time, otherwise sides have no idea what's being sent. So, it's not possible to resume reading the message if the preceding data was lost.

It's almost literally the collection of all things you don't want to have in a streaming format. It's like picking a strainer with the largest holes to make soup. Hard to think about a worse tool for the job.

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#273

Earlier quoted context omitted.

Protobuf is an atrocious protocol. Whatever other problems gRPC has may be worse, but Protobuf doesn't make anything better that's for sure. The reason to use it may be that you are required to by the side you cannot control, or this is the only thing you know. Otherwise it's a disaster. It's really upsetting that a lot of things used in this domain are the first attempt by the author to make something of sorts. So m…

Can you elaborate?

Some very obvious and easily avoidable problems (of the binary format):

* Messages are designed in such a way that only the size of the constituents is given. The size of the container message isn't known. Therefore the top-level message doesn't record its size. This requires one to invent an extra bit of the binary format, when they decide how to delimit top-level messages. Different Protobuf implementations do it differently. So, if you have two clients independently implementing the same spec, it's possible that both will never be able to communicate with the same service. (This doesn't happen a lot in practice, because most developers use tools to generate clients that are developed by the same team, and so, coincidentally they all get the same solution to the same problem, but alternative tools exist, and they actually differ in this respect).

* Messages were designed in such a way as to implement "+" operator in C++. A completely worthless property. Never used in practice... but this design choice made the authors require that repeating keys in messages be allowed and that the last key wins. This precludes SAX-like parsing of the payload, since no processing can take place before the entire payload is received.

* Protobuf is rife with other useless properties, added exclusively to support Google's use-cases. Various containers for primitive types to make them nullable. JSON conversion support (that doesn't work all the time because it relies on undocumented naming convention).

* Protobuf payload doesn't have a concept of version / identity. It's possible, and, in fact, happens quite a bit, that incorrect schema is applied to payload, and the operation "succeeds", but, the resulting interpretation of the message is different from intended.

* The concept of default values, that is supposed to allow for not sending some values is another design flaw: it makes it easy to misinterpret the payload. Depending on how the reader language deals with absence of values, the results of the parse will vary, sometimes leading to unintended consequences.

* It's not possible to write a memory-efficient encoder because it's hard / impractical sometimes to calculate the length of the message constituents, and so, the typical implementation is to encode the constituents in a "scratch" buffer, measure the outcome, and then copy from "scratch" to the "actual" buffer, which, on top of this, might require resizing / wasting memory for "padding". If, on the other hand, the implementation does try to calculate all the lengths necessary to calculate the final length of the top-level message, it will prevent it from encoding the message in a single pass (all components of the message will have to be examined at least twice).

----

Had the author of this creation tried to use it for a while, he'd known about these problems and would try to fix them, I'm sure. What I think happened is that it was the first ever attempt for the author in doing this, and he never looked back, switching to other tasks, while whoever picked up the task after him was too scared to fix the problems (I hear the author was a huge deal in Google, and so nobody would tell him how awful his creation was).

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#274

Earlier quoted context omitted.

> Nothing in Protobuf is suited for streaming. Uhh... Why? Protobuf supports streaming replies and requests. Do you mean that you need to know the message size in advance?

No, Protobuf doesn't support streaming. Streaming means that it's possible to process the payload in small chunks, preferably of fixed size. Here are some examples of formats that can be considered streaming: * IP protocol. Comes in uniformly sized chunks, payload doesn't have a concept of "headers". Doesn't even have to come in any particular order (which might be both a curse and a blessing for streaming). * MP4 fo…

Ah, you're an LLM.

Protobuf supports streaming just fine. Simply create a message type representing a small chunk of data and return a stream of them from a service method.

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#275
post #168

Earlier quoted context omitted.

I tend to prefer RESTish rather than RESTful since RESTful almost suggests attempting to implement Fielding's ideas but not quite getting there. I think the subset of approaches that try and fail to implement Fielding's ideas is an order of magnitude (or two) smaller than those who go for something that is superficially similar, but has nothing to do with HATEOAS :-). REST is an interesting idea, but I don't think it…

While it is amazing for initial discovery to have everything presented for the developer's inspection, in production it ends up requiring too many network round-trips to actually traverse from root to /resource_name/resource_id/sub_resource_name/sub_resource_id, or an already verbose transaction (everything is serialized and deserialized into strings!) becomes gigantic if you if don't make it hierarchical and just dr…

If you are talking about REST here, expect an angry mob outside your door soon. URIs that have inherent structure and meaning? Burn the heretic! :-)

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#276
post #29

Earlier quoted context omitted.

> You can’t just give someone a simple command to call an endpoint—it requires additional tooling that isn’t standardized. GRPC is a standard in all the ways that matter. It (or Thrift) is a breath of fresh air compared to doing it all by hand - write down your data types and function signatures, get something that you can actually call like a function (clearly separated from an actual function function - as it shoul…

> GraphQL is even better. Letting clients introduce load into the system without understanding the big O impact of the SOA upstream is a foot gun. This does not scale and results in a massive waste of money on unnecessary CPU cycles on O(log n) FK joins and O(n^2) aggregators. Precomputed data in the shape of the client's data access pattern is the way to go. Frontload your CPU cycles with CQRS. Running all your comp…

Any non-trivial REST API is also going to have responses which embed lists of related resources.

If your REST API doesn't have a mechanism for each request to specify which related resources get included, you'll also be wasting resources include related resources which some requesters don't even need!

If your REST API does have a mechanism for each to request to specify which related sources get included (e.g. JSON API's 'include' query param [0]), then you have the same problem as GraphQL where it's not trivial to know the precise performance characteristics of every possible request.

[0] https://jsonapi.org/format/#fetching-includes

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#277
post #47

Earlier quoted context omitted.

Real REST is a very, very small minority. Fake REST (i.e., JSON RPC) is really ridiculously common.

I’ve never liked the no true scotsman nature of REST (which is exacerbated by the fact that its canonical “specification” is a broad PhD dissertation with a lot of other concepts thrown in), so I have adopted a fairly lax definition: if your URLs are subjects and you use HTTP verbs for the verbs, I feel like it qualifies.

Does Stripe's API qualify? It has URLs which are verbs, e.g.

GET /v1/customers/search

POST /v1/payouts/:id/cancel

POST /v1/disputes/:id/close

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#278
post #92

Earlier quoted context omitted.

The biggest project I’ve used it with was in Java. Validating the output of the bindings protoc generated was more verbose and error prone than hand serializing data would have been. The wire protocol is not type safe. It has type tags, but they reuse the same tags for multiple datatypes. Also, zig-zag integer encoding is slow. Anyway, it’s a terrible RPC library. Flatbuffer is the only one that I’ve encountered that…

> The wire protocol is not type safe. It has type tags, but they reuse the same tags for multiple datatypes. When is this ever an issue in practice? Why would the client read int32 but then all of a sudden decide to read uint32?

I guess backwards incompatible changes to the protocol? But yeah, don't do that if you're using protobuf; it's intentionally not robust to it.

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#279
post #48
post #29

Earlier quoted context omitted.

> You can’t just give someone a simple command to call an endpoint—it requires additional tooling that isn’t standardized. GRPC is a standard in all the ways that matter. It (or Thrift) is a breath of fresh air compared to doing it all by hand - write down your data types and function signatures, get something that you can actually call like a function (clearly separated from an actual function function - as it shoul…

> GraphQL is even better just a casual sentence at the end? How about no. It's in the name, a query-oriented API, useless if you don't need flexible queries. Why don't you address the problem they talked about, what is the cli tool I can use to test grpc, what about gui client?

> what is the cli tool I can use to test grpc

Use https://connectrpc.com/ and then you can use curl, postman, or any HTTP tool of your choosing that supports sending POST requests.

Re: Understanding gRPC, OpenAPI and REST and when to use them in API design (2020)

#280

Earlier quoted context omitted.

I’ve never liked the no true scotsman nature of REST (which is exacerbated by the fact that its canonical “specification” is a broad PhD dissertation with a lot of other concepts thrown in), so I have adopted a fairly lax definition: if your URLs are subjects and you use HTTP verbs for the verbs, I feel like it qualifies.

Does Stripe's API qualify? It has URLs which are verbs, e.g. GET /v1/customers/search POST /v1/payouts/:id/cancel POST /v1/disputes/:id/close

IMO, no, those endpoints don't qualify. That's just RPC over HTTP. Nothing wrong with it, but not REST (REpresentational State Transfer), since it's not transferring representations of state.

Search is probably not worth shoehorning into a REST paradigm (don't be a purist!). The others are easy enough though, something like

  PATCH /v1/payouts/:id
  {state: canceled}

  PATCH /v1/disputes/:id
  {state: closed}
Or an equivalent PUT with the whole object.

Or if you want to be json-patch standards-compliant:

  PATCH /v1/payouts/:id
  {op: "replace", path: "/state", value: "closed"}
FWIW this is why I think it's not really productive to be persnickety about a "REST API" being 100% always REST. A CRUD app is still a CRUD app if you do some occasional other operations, and a REST API can still be a REST API with some endpoints that are not REST.
Post reply on HN