dskit

A modular Go toolkit of production-hardened building blocks for distributed systems, extracted from Grafana Labs' own database projects.

Library
Go
vv0.0.0-20260905140254-b4a98cb2c130
582stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
80/100Excellent
Development Activity100
Maintenance52
Community84
Maturity56
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
77/100Good
Architecture85
Code Quality88
Innovation78
Learning Curve55

dskit is a collection of Go packages that Grafana Labs pulled out of its own distributed systems — Mimir, Loki, Tempo, and Pyroscope — so that the plumbing every horizontally-scaled service needs didn’t have to be rewritten four times over. Instead of a single opinionated framework, it is a grab-bag of narrowly-scoped packages: a consistent-hashing ring for sharding work across nodes, a common key-value abstraction with Consul, Etcd, and Memberlist backends, a Guava-style service lifecycle model for coordinating startup and shutdown across components, gRPC client/server middleware for metrics and logging, exponential backoff, request hedging, and tenant/multi-tenancy helpers.

Because every package originated as internal infrastructure for real, high-throughput systems rather than a green-field API design exercise, the abstractions are shaped by what actually broke in production — retry storms, split-brain ring state, noisy gRPC logging — rather than by what looked clean on a whiteboard. Teams building their own sharded, multi-tenant, or highly-available Go services can pull in just the ring, just the KV client, or just the service manager without adopting a whole framework.

The project is maintained as living infrastructure: it ships from the same commit history that powers Grafana’s production databases, uses conventional-commit PR titles instead of a hand-written changelog, and tracks the two latest Go minor releases for compatibility.

What You Get

  • Consistent-hashing ring (ring/) — a distributed hash ring implementation for sharding ownership of keys/tokens across a fleet of nodes, with heartbeat-based membership and configurable replication factor.
  • Pluggable KV store client (kv/) — a single Client interface with Consul, Etcd, and Memberlist implementations plus a multi wrapper for migrating between backends without changing call sites.
  • Service lifecycle model (services/) — a Guava-inspired Service interface (New → Starting → Running → Stopping → Terminated/Failed) plus a Manager for coordinating groups of dependent services.
  • gRPC client/server middleware (grpcclient/, grpcutil/, middleware/) — reusable interceptors for metrics, structured logging, tracing propagation, and HTTP-over-gRPC error mapping (httpgrpc/).
  • Resilience primitives (backoff/, hedging/, limiter/, gate/) — exponential backoff for retries, request hedging to cut tail latency, and concurrency/rate limiting helpers.
  • Multi-tenancy helpers (tenant/, user/) — extracting and propagating tenant/org IDs through request contexts and gRPC metadata, the same mechanism Mimir and Loki use for tenant isolation.

Common Use Cases

  • Building a sharded microservice — use ring/ to distribute ownership of partitions, shards, or tenants across a fleet of stateless workers with automatic rebalancing on scale-up/down.
  • Coordinating service startup order — use services/ to model a component (a cache warmer, a background compactor) as a Service and let a Manager sequence dependent starts/stops instead of hand-rolling goroutine coordination.
  • Swapping coordination backends — use kv/ to write ring or config-watching code once against the Client interface, then choose Consul, Etcd, or gossip-based Memberlist at deploy time via config.
  • Instrumenting gRPC services — drop in grpcclient/middleware interceptors to get consistent Prometheus metrics and structured logs across every gRPC call without writing interceptor boilerplate per service.
  • Multi-tenant SaaS backends — use tenant/ to extract a tenant ID from inbound requests and propagate it through internal RPCs, mirroring how Grafana Cloud isolates tenant data in Mimir/Loki.

Under The Hood

Architecture The repository is organized as roughly forty independent, single-purpose packages (ring/, kv/, services/, grpcclient/, tenant/, etc.) rather than a layered framework — there is no central entry point or app object; a consumer imports only the packages it needs. The one cross-cutting abstraction is the services.Service state machine (services/basic_service.go), which several other packages (the ring, KV clients) build on to expose consistent start/stop/health semantics. The kv/client.go Config/StoreConfig pair follows a composition pattern: a single Client interface is backed by swappable consul, etcd, or memberlist implementations selected at runtime via a store config string, so call sites never branch on backend. Because packages are deliberately decoupled, changing one (e.g. the ring’s token-generation strategy) has a narrow, auditable blast radius rather than rippling through a shared core.

Tech Stack Pure Go (99% of the codebase, go.mod requires Go 1.25+ with a 1.27 toolchain), with no runtime beyond the standard library and its dependencies: go-kit/log for structured logging, prometheus/client_golang for metrics, hashicorp/consul, go.etcd.io/etcd/client, and hashicorp/memberlist for the three KV backends, gogo/protobuf and google.golang.org/grpc for RPC, and OpenTelemetry/Jaeger packages for tracing. Build and release automation runs through a Makefile and GitHub Actions (test-build.yml), with Renovate configured for automated dependency updates.

Code Quality Test coverage is extensive — roughly one test file per implementation file across the tree (over 150 _test.go files), using the standard library testing package alongside stretchr/testify for assertions and mocks. Core packages like services/ include both unit tests and runnable examples (services/example_test.go). Linting is enforced via golangci-lint (.golangci.yml) with revive, misspell, and loggercheck enabled alongside gofmt/goimports formatters and a scoped errcheck exclusion list, and CI runs on every push and pull request via test-build.yml, with a dedicated flaky-test-detection workflow that reruns the suite twenty times on demand. Error handling favors explicit returned errors over panics, consistent with idiomatic Go and the codebase’s origin as production infrastructure.

API Design The public surface favors small, composable interfaces over large all-in-one structs — kv.Client, services.Service, and ring.ReadRing are each narrow enough to mock or reimplement independently. Constructors follow a consistent New* + Config + RegisterFlagsWithPrefix pattern throughout (seen in kv/client.go and mirrored in the ring and gRPC packages), so configuring any subsystem from a CLI flag set feels the same regardless of which package it comes from. Documentation is comment-driven (package-level doc comments plus per-function GoDoc) rather than a separate docs site, which keeps API reference accurate but raises the initial learning curve for newcomers who need to read source alongside the README to understand how packages compose.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search