Working with Protobufs

CRDB uses gRPC to send messages across processes, which serializes structured data with protocol buffers. If you’re unfamiliar with gRPC and protobufs, click those links to learn more. Also note that cockroachdb uses a fork of the gogoproto protobuf extension.

You will find the checked in, language agnostic, protbuf files in our codebase by grepping for .proto files which define our proto messages. Further, you will encounter read only, generated protobuf .pb.go files that will get compiled into the cockroach binary. In the cockroachdb repo at least, running ./dev gen protobuf or ./dev build short will regenerate these files.

When working with protobufs, please follow these general guidelines, in addition to the crdb specific ones below.

  • Don’t use the required the field. The required field raises a decoding error if the field is not present in the message, which restricts the backward and forward compatibility of a proto message. For cockroach specifically, sending messages between two crdb nodes with different binaries becomes treacherous.

  • Use the gogoproto.nullable option so the compiled field type is the type itself instead of a pointer. We use this option because we want our generated proto structs to look and feel like regular go structs-- specifically, we follow the convention that the absence of the value is represented as the 0 value of the type, instead of a null pointer. Note that by default go primitive types are compiled to the type itself. There are two exceptions to this recommendation:

    • If you really need to distinguish between the zero value vs absent, then don’t set this option. Further, if you want a pointer to a primitive type, set gogoproto.nullable=false.

    • If the type of the field is very large, i.e. another message type with lots and lots of fields, and if we'll be passing instances of the containing type around often. Then the one time alloc and just copying addr to pass it can actually be cheaper. But this too is pretty rare (I imagine it might hold for Descriptor and RequestUnion but not many others).

  • Don’t use the optional field. As described here, it allows the user to distinguish between the zero value and if the field is absent. If you need this, work with gogoproto.nullable invocation so the compiled field is nullable.

  • Avoid types.Any and json types like the plague. They are very hard to decode while inspecting them in a debug zip.

 

Gotchas

  • For protobufs on performance critical paths, beware that empty fields can have non zero space overhead when marshalled. To ensure these fields are truly empty in the encoded message, use Radu’s fancy omitEmpty field.

 

Copyright (C) Cockroach Labs.
Attention: This documentation is provided on an "as is" basis, without warranties or conditions of any kind, either express or implied, including, without limitation, any warranties or conditions of title, non-infringement, merchantability, or fitness for a particular purpose.