Skip to content

Orientation

This is the on-ramp. If you’re an experienced engineer new to Go, start here: we’ll learn Go by reading a real production codebase rather than working through toy examples. The running example throughout this guide is multigres, a distributed-systems project written in Go, and this page covers what it is, how its code is laid out, and — most importantly — how to read unfamiliar Go fast enough to find your way around a large codebase.

The “how to read Go” material here is language-general; it pays off in any Go project you ever touch.

Multigres is Vitess for Postgres: a set of small Go services that sit in front of real PostgreSQL servers and add horizontal scaling, connection pooling, and automated failover. An application speaks the ordinary PostgreSQL wire protocol; it never knows it is talking to a proxy. Behind the proxy, the system decides which Postgres to run a query on, reuses connections, and — when a Postgres dies — elects a new leader and reconnects, all without the client noticing.

It is a single Go module, github.com/multigres/multigres, on Go 1.25. All Go code lives under go/. The project is early-stage; the wire protocol and gRPC contracts are its stable spine.

The mental model: service topology and the request path

Section titled “The mental model: service topology and the request path”

There are five long-running services (implementations under go/services/) plus operator and test binaries. The latency-sensitive path is just two hops, and only the first hop is gRPC — the second is a real pooled SQL connection:

Request path
Rendering diagram…

Everything else is off the query path:

ServiceRoleOn the query path?
multigatewayStateless proxy: speaks PG wire + gRPC, routes queriesYes (hop 1)
multipoolerConnection pooling; serves queries; talks to pgctld via gRPCYes (hop 2)
pgctldPostgreSQL control daemon — lifecycle only (start/stop/restart)No (control plane)
multiorchConsensus + failover orchestrationNo (control plane)
multiadminAdmin service, HTTP + gRPC endpointsNo (admin)

Two binaries under go/cmd/ are not services: multigres is the operator CLI, and portpoolserver is a cross-process port allocator used only by integration tests.

The cluster is organized into cells (availability zones), with metadata in a topology store (etcd in production). For the full cell-aware topology, the leader/primary terminology, and the exact query trace, see the deep dive: Architecture & Request Flow.

The repo root holds the contracts and config; everything Go lives under go/.

PathWhat lives here
proto/gRPC/protobuf contracts (.proto). Source of truth for service surfaces; generates into go/pb/. Key files: multipoolerservice.proto, pgctldservice.proto, multiorchservice.proto, multigatewayservice.proto, clustermetadata.proto, query.proto.
config/Per-component YAMLs (e.g. multigateway.yaml, multipooler.yaml, pgctld.yaml, multiorch.yaml) — small files holding things like http-port and log-level. They are one source in a precedence chain (flags > env > config file > defaults); cluster-wide metadata lives in the topology store, not here.
MakefileSelf-documenting build/dev driver (make help).
go/All Go source — see below.

Inside go/:

PathWhat lives hereDependency rule
go/cmd/7 binaries (the 5 services + multigres CLI + portpoolserver). Each is a tiny main.go.Can depend on anything.
go/services/The 5 long-running services’ implementations (multiadmin, multigateway, multiorch, multipooler, pgctld).Cannot depend on cmd/ or other services.
go/common/Shared code: mterrors, pgprotocol, sqltypes, parser, queryservice, topoclient, consensus, constants, etc.Cannot depend on cmd/ or services/.
go/tools/Generic, project-agnostic helpers (timers, retry, …).Cannot depend on any repo code outside tools/.
go/pb/Generated protobuf Go (// Code generated). Read-only.
go/observability/Metric catalog.
go/provisioner/Cluster provisioning (local + provisioner.go).
go/test/End-to-end tests and test utilities (endtoend, utils).

The dependency direction is strict and worth internalizing — it tells you which way imports may point:

Allowed dependency directions
Rendering diagram…

In words: cmd/ may depend on anything; services/ may not depend on cmd/ or on other services; common/ may not depend on cmd/ or services/; and tools/ may not depend on anything outside tools/.

Go has almost no metaprogramming, so the code says what it does. The leverage is in knowing where to start and how the pieces are wired.

github.com/multigres/multigres/go/services/multigateway is the directory go/services/multigateway. Strip the module prefix (github.com/multigres/multigres) and you have a path relative to the repo root. This mapping is exact and always holds.

Every binary is a tiny go/cmd/<svc>/main.go that delegates immediately. There are two shapes.

Service binaries build a cobra command, then run an Init / RunDefault pair. From go/cmd/multigateway/main.go:

go/cmd/multigateway/main.go
func run(ctx context.Context, mg *multigateway.MultiGateway) error {
if err := mg.Init(ctx); err != nil {
return err
}
return mg.RunDefault()
}

The package doc-comment and the cobra Short string are the project’s own one-line descriptions — read them first. (multigateway’s Short: “Multigateway is a stateless proxy responsible for accepting requests from applications and routing them to the appropriate multipooler server(s) for query execution. It speaks both the PostgreSQL Protocol and a gRPC protocol.”)

The operator CLI has a different shape — go/cmd/multigres/main.go just dispatches to subcommands (which live under go/cmd/multigres/command/):

go/cmd/multigres/main.go
func main() {
root := command.GetRootCommand()
if err := root.Execute(); err != nil {
os.Exit(1)
}
}

The request path is glued together by interfaces, not concrete types. When you hit one — engine.IExecute, queryservice.QueryServicegrep for its name to find the definition and the concrete implementation(s):

Terminal window
grep -rn 'IExecute' go/

This is the single most useful habit for reading Go service code: an interface defines the seam, and the implementation lives in some other package you find by grepping the name. See Interfaces & composition for why Go is structured this way.

These are general Go techniques:

  • go doc <import-path> prints a package’s doc-comment and exported symbols. go doc <import-path>.Symbol drills into one type or function. Faster than opening the file when you just want the contract.
  • gopls is the Go language server (jump-to-definition, find-references). If your editor has Go support, it is running gopls.

The guide is organized into five tracks. The language and project tracks are the spine; the tooling and reference tracks support them.

The language track presupposes nothing but general programming experience; the project track presupposes the whole language track. Within each track, read in order — the numbering encodes a real dependency chain, not just a sequence.

A few workflow facts worth knowing before you build anything:

  • Build with make build; regenerate code with make proto / make parser; do both plus binaries with make build-all.
  • This codebase runs its tests through a dev wrapper rather than calling go test directly — integration tests auto-build first. See the testing workflow.