Go Protobuf
Michael Stapelberg, Zürich Gophers 2025-April-2
Agenda
5-minute refresher on Protobuf
01
Newly released: The Opaque API
Migrating millions of lines of code
02
03
5-minute refresher on Protobuf (Protocol Buffers)
edition = "2023"; // successor to proto2/3
package log;
message LogEntry {
string backend_server = 1;
uint32 request_size = 2;
string ip_address = 3;
}
5-minute refresher on Protobuf (Protocol Buffers)
The Protobuf workflow:
5-minute protobuf refresher: Logs Analysis Pipeline decodes a message
for {
b := … // read from network or disk�
var entry logpb.LogEntry // protoc made this!
err := proto.Unmarshal(b, &entry)
if err != nil { … }
� requestsByIP[entry.GetIpAddress()]++
}
// Store aggregated statistics report…
5-minute protobuf refresher: Why not JSON?
Latest development:�Opaque API launch�in December 2024
Opaque API: What’s changing?
Let’s recap the Protobuf workflow:
The Opaque API is a new
version of the generated code
Background: Generated code (old Open Struct API)
package logpb // generated by protoc
type LogEntry struct {
BackendServer *string
RequestSize *uint32
IPAddress *string
// …internal fields elided…
}
func (l *LogEntry) GetBackendServer() string { … }
func (l *LogEntry) GetRequestSize() uint32 { … }
func (l *LogEntry) GetIPAddress() string { … }
Background: Field presence
Background: Field presence over the years
The Opaque API uncouples the�Generated Code API from the�in-memory representation!
Generated code (new Opaque API)
package logpb // generated by protoc, Opaque API
type LogEntry struct {
xxx_hidden_BackendServer *string // not exported
xxx_hidden_RequestSize uint32 // not exported
xxx_hidden_IPAddress *string // not exported
// …internal fields elided…
}
func (l *LogEntry) GetBackendServer() string { … }
func (l *LogEntry) HasBackendServer() bool { … }
func (l *LogEntry) SetBackendServer(string) { … }
func (l *LogEntry) ClearBackendServer() { … }
Opaque API: Why? Less memory usage!
│ Open Struct API │ Opaque API │
│ allocs/op │ allocs/op vs base │
Prod#1 360.3k ± 0% 360.3k ± 0% + 0.00% (p=0.002 n=6)
Search#1 1413.7k ± 0% 762.3k ± 0% -46.08% (p=0.002 n=6)
Search#2 314.8k ± 0% 132.4k ± 0% -57.95% (p=0.002 n=6)
Opaque API: Why? Lazy decoding!
Opaque API: Why? Safety! More optimizations!
Migrating well over 100 million�lines of Go code
Migration: the open2opaque tool
Migration: the open2opaque tool (2)
Migration: the open2opaque tool (3)
Migration: scaling it up!
Migration: scaling it up!
Migration: scaling it up!
Opaque API
Thank you for�your attention!
→ protobuf.dev �→ go.dev/blog