Paul's Programming Notes PostsRSSGithub

Thrift Is More Difficult To Use Than HTTP

The microservices at my work implement both HTTP endpoints and Apache Thrift RPC endpoints, with Thrift carrying the internal communication between services. External access goes through an API gateway that needs HTTP anyway. I keep losing hours to a Thrift problem I could have solved in minutes over HTTP.

New services don’t get Thrift support at all anymore. They’re documented with Swagger and validated with JSON schema instead, and the tests check requests and responses against the spec.

What makes it harder to live with than HTTP:

  • An exception you didn’t declare in the IDL reaches the client as TApplicationException: Internal error and nothing else. The generated processor catches it, logs the traceback on the server, and sends back that one opaque message, so every debugging session starts with going to find the server log.
  • With an HTTP endpoint I can mock things out with a library like responses. Nothing equivalent exists for Thrift in Python yet, so testing is a lot more work.
  • Version mismatches are very difficult to debug, especially when someone changes the type of an existing field or adds a field to the end of a definition.
  • Updating one endpoint means updating three things: the Thrift definitions, the definitions on the client, and the definitions on the server.
  • The Python tooling for running a Thrift service is nowhere near as mature as it is for HTTP services.
  • A new developer has definitely used HTTP and probably hasn’t used Thrift. The business intelligence people don’t touch it at all, the barrier to entry is too high.
  • Javascript and iOS support isn’t great, though you probably shouldn’t be exposing Thrift services to the public internet anyway.

Thrift does buy real things. It’s strongly typed, the definitions give you one place to look at all of your models, it validates them for you, and the leaner transport puts less over the wire.

That last one matters less than it sounds. Gzipped JSON is already pretty compact and the default Thrift transports don’t compress at all, so you’re saving a few bytes in exchange for everything above.

If you’re only using Python, marshmallow covers the validation, or you can pair JSON schema with something like warlock to build objects from it. If you do stay on Thrift, thriftpy is a big quality of life improvement over the built-in client because it reads the definitions directly instead of making you generate code from them.

Whether the complexity is worth the performance depends on your scale. Uber runs Thrift across a thousand services and Matt Ranney still summed it up as “Thrift is OK, but generated code is bad” in What I Wish I Had Known Before Scaling Uber to 1000 Services, which is the same complaint that makes thriftpy worth using. For a small team it’s a lot of work and learning to end up somewhere HTTP already is.