Open-Source Wikis

/

etcd

/

How to contribute

/

Patterns and conventions

etcd-io/etcd

Patterns and conventions

How etcd's Go code is organized, named, and exercised. Use this page as a quick reference when reading or writing code in the repository.

Module boundaries

The repo is a Go workspace (go.work) of independently-versioned modules. Each module has its own go.mod, go.sum, LICENSE, OWNERS, and a .gomodguard.yaml declaring which other modules and external dependencies it may import. make verify-gomodguard enforces this.

The high-level dependency direction is:

api -> (no etcd modules)
client/pkg -> (no etcd modules)
client/v3 -> api, client/pkg
pkg -> client/pkg, api
server -> api, client/pkg, client/v3 (limited), pkg
cache -> client/v3
etcdctl -> client/v3, pkg, etcdutl (snapshot only)
etcdutl -> server, api, client/pkg, pkg
tests -> everything (it's the integration boundary)

If your change introduces a new edge between modules, it almost certainly needs a .gomodguard.yaml update.

Versioning suffixes

Most internal Go packages still use /v3 import paths reflecting the major version of the public API. Even modules that have moved to per-module tagging keep the import suffix for backwards compatibility, e.g. go.etcd.io/etcd/server/v3/etcdserver.

Logging

  • Always use *zap.Logger. The zero-value is forbidden — every constructor takes a logger or grabs one via etcd.GetLogger().
  • nil loggers should be handled defensively for tests that don't pass one (see e.g. server/etcdserver/zap_raft.go).
  • Structured fields: prefer zap.String("key", ...) over fmt.Sprintf interpolation.
  • Logs that fire on every request should use Debug. Info is reserved for state transitions (start, stop, leader change, snapshot finished).

Errors

  • Sentinel errors are exported from each package (e.g. mvcc.ErrCompacted, lease.ErrLeaseNotFound, auth.ErrPermissionDenied).
  • gRPC-facing errors are translated to google.golang.org/grpc/status codes in server/etcdserver/api/v3rpc/util.go and api/v3/v3rpc/rpctypes/.
  • Use errors.Is/errors.As. Wrapping with fmt.Errorf("...: %w", err) is the norm for new code.

Concurrency

  • The server's serial sections are protected by named mutexes (r.Lock(), s.applyW.Lock(), etc.) — read the file-level comments.
  • go.etcd.io/etcd/pkg/v3/wait is the canonical "request id → response channel" map (used heavily in EtcdServer.processInternalRaftRequestOnce).
  • Long-running goroutines should listen on s.stopping / s.done and call s.goAttach to be tracked by the server's wait group.

Tracing

go.etcd.io/etcd/pkg/v3/traceutil provides a lightweight per-request trace structure that records named steps with timestamps. Production code attaches steps in hot paths (v3_server.go, apply/); the trace is dumped if total duration exceeds traceThreshold (100 ms).

OpenTelemetry tracing is opt-in (see debugging.md).

Feature gates

pkg/featuregate/ mirrors Kubernetes' feature-gate registry. Each gate has a name, a default, and a stability level (Alpha / Beta / GA). Gates are declared in server/features/etcd_features.go and surfaced via the --feature-gates flag (server/embed/config.go, ServerFeatureGateFlagName).

When adding a new behavior toggle, prefer a feature gate over a bespoke --experimental-* flag.

Protobuf

  • Source .proto files live under api/.
  • Regenerate with scripts/genproto.sh (and verify with scripts/verify_genproto.sh).
  • Generated files end in .pb.go, _grpc.pb.go, .pb.gw.go. Do not edit them by hand.
  • New gRPC methods need an (google.api.http) annotation if they should be reachable via the REST gateway.

Testing

  • Tests use Go's standard testing package; no third-party assertion library globally — though github.com/stretchr/testify is widely used.
  • Cluster fixtures are in tests/framework/integration/cluster.go (multi-member in-process) and tests/framework/e2e/ (subprocess).
  • Common workflow: write the test against the framework's Cluster interface (tests/framework/interfaces/), then it runs in both integration and e2e modes via tests/common/.

File header

Every Go file starts with the etcd Apache 2.0 header (./.header). make verify-lint will fail if the header is missing or wrong.

Naming

  • Files: lowercase_with_underscores.go is acceptable but lowercasewithoutseparators.go is more common.
  • Packages: short, lowercase, no separators. mvcc, lease, rafthttp, etcdmain.
  • Functions: PascalCase exported, camelCase unexported, idiomatic Go.
  • Mutex names: a comment above the mutex describing what it protects is mandatory in core packages.

Documentation comments

go.etcd.io/etcd/... is published on https://godocs.io. Any exported symbol must have a doc comment. The verify-lint step uses golangci-lint's revive rule (see tools/.golangci.yaml).

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – etcd wiki | Factory