pgmesh

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 13 Imported by: 0

README

pgmesh

Test Lint Generated Vulnerability Integration

pgmesh is a standalone sqlc companion for PostgreSQL and pgx/v5. Its Go package is also named pgmesh. A process plugin generates read/write-separated query wrappers, while the runtime routes those wrappers across virtual shards, primary databases, read replicas, and synchronous dual-write mirrors for staged shard expansion.

Documentation

Installation

Add the runtime module to an application with:

go get github.com/clnv/pgmesh

Import it using the package name pgmesh:

import "github.com/clnv/pgmesh"

Generated layers

For each sqlc package, the plugin generates:

  • ReadQuerier and ReadQueries, containing only read queries.
  • WriteQuerier and WriteQueries, containing only writes and mirror fan-out.
  • StoreQuerier and StoreQueries, combining both views for a primary.
  • NewStoreNode, pairing a read-only view with the primary-capable view.
  • ShardResolver[SK] and ShardedQueries[SK] when at least one query declares a shard route.

This split makes Shard.Read() return a type that cannot execute writes. Shard.Write() returns StoreQueries, because primary reads and transactional reads must use the primary as well.

Examples

The module includes executable examples in several forms:

  • examples is a progressive suite covering a plain single database, primary/replica splitting, multi-database sharding, synchronous mirrors, and shard-pinned transactions.
  • example_test.go demonstrates direct NewBuilder usage, round-robin replicas, primary fallback, deterministic physical-shard enumeration, declarative CreateMesh, and synchronous mirrors.
  • integration/fixture/example_test.go exercises the actual generated ShardedQueries API, including replica reads, forced-primary reads, primary writes, and mirror fan-out.
  • examples/sqlc is the shared schema, annotated query set, and sqlc.yaml process-plugin configuration.

Run the executable documentation examples with:

go test -run '^Example' ./...

The complete just verify workflow also runs every standalone program in examples against the local PostgreSQL topology.

Query annotations

Every query must put its kind immediately after the sqlc name annotation:

-- name: ListUsers :many
-- kind: read
SELECT * FROM users;

-- name: CreateUser :one
-- kind: write
INSERT INTO users (id, tenant_id) VALUES ($1, $2) RETURNING *;

A single-shard query may put a named shard route immediately after its kind:

-- name: GetConversation :one
-- kind: read
-- shard: p2p(user_id, peer_id)
SELECT * FROM conversations
WHERE user_id = $1 AND peer_id = $2;

The route name becomes an exported resolver method, and operands refer to SQL parameter names. The example generates:

type ShardResolver[SK any] interface {
    P2P(userID int64, peerID int64) SK
}

The application implements this interface, so normalization, hashing domains, and composite shard-key construction remain application decisions. The plugin resolves the operands to either scalar Go parameters or fields on sqlc's params struct, independent of query_parameter_limit.

Metadata must appear in this order: name, kind, optional shard, then ordinary documentation. Missing, malformed, misplaced, or type-conflicting metadata fails generation. Queries without shard remain available through node-level wrappers and are omitted from ShardedQueries.

Automatic routing is deliberately unavailable for :copyfrom and :batch*. Partition those inputs by shard and invoke node-level wrappers. The plugin does not implement scatter-gather or cross-shard result merging.

sqlc configuration

Build the process plugin and register it in sqlc.yaml:

go build -o bin/sqlc-gen-store ./cmd/sqlc-gen-store
version: "2"
plugins:
  - name: "pgmesh"
    process:
      cmd: "path/to/sqlc-gen-store"

sql:
  - engine: "postgresql"
    schema: "schema.sql"
    queries: "queries.sql"
    gen:
      go:
        package: "db"
        out: "db"
        sql_package: "pgx/v5"
        emit_interface: true
        query_parameter_limit: 1
        emit_params_struct_pointers: true
        emit_result_struct_pointers: true
        emit_pointers_for_null_types: true
    codegen:
      - plugin: "pgmesh"
        out: "db"
        options:
          package: "db"
          output_file_name: "zz_generated_store.go"
          type: "StoreQueries"
          constructor: "NewStoreQueries"
          sql_package: "pgx/v5"
          query_parameter_limit: 1
          emit_params_struct_pointers: true
          emit_result_struct_pointers: true
          emit_pointers_for_null_types: true

The plugin supports the sqlc commands :one, :many, :exec, :execrows, :execresult, :copyfrom, :batchexec, :batchone, and :batchmany at the node layer. It accepts sqlc-compatible rename, override, pointer, package, constructor, and output naming options. sql_package must be pgx/v5 and skip_with_tx is rejected. runtime_import_path can override the default github.com/clnv/pgmesh import for forks.

When the wrapper is generated into a package separate from sqlc's Go output, set internal_import_path and, optionally, internal_import_alias. The plugin then qualifies sqlc-generated models, params structs, batch result types, enums, constructors, and interfaces through that import. The checked-in fixture generates and compiles both same-package and separate-package layouts.

Building a topology

Open each DSN in a node factory and use the generated node constructor:

func createNode(
    ctx context.Context,
    dsn string,
) (pgmesh.Node[*db.ReadQueries, *db.StoreQueries], error) {
    pool, err := pgxpool.New(ctx, dsn)
    if err != nil {
        return pgmesh.Node[*db.ReadQueries, *db.StoreQueries]{}, err
    }
    return db.NewStoreNode(pool), nil
}

mesh, err := pgmesh.CreateMesh(ctx, &pgmesh.Options[
    *db.ReadQueries,
    *db.StoreQueries,
    ShardKey,
]{
    ReplicaSets: []pgmesh.ReplicaSetSpec{
        {
            Name:     "shard-a",
            Primary:  pgmesh.Connection{DSN: primaryDSN},
            Replicas: []pgmesh.Connection{{DSN: replicaDSN}},
        },
    },
    Shards: pgmesh.Shards{
        NumVShards: 1024,
        Mappings: []pgmesh.VShardMapping{
            {
                VShards:        pgmesh.VShardRange(0, 1024),
                MainReplicaSet: "shard-a",
            },
        },
    },
    CreateNode:  createNode,
    ShardHasher: shardHasher,
})

Topology construction validates every virtual shard, replica-set reference, mirror reference, name, DSN, factory, and hasher. Mesh.Shard also rejects a hasher result outside the configured virtual-shard range. AllShards returns physical shards deterministically in first-vshard order.

Mirror order is part of the topology: mappings for the same main replica set must list the same mirrors in the same order, because the first non-ignored mirror error is returned.

Direct construction is available through NewNode, NewReplicaSet, and NewBuilder when topology does not come from DSNs.

Routed queries and consistency

Construct the generated facade with the mesh and application resolver:

queries := db.NewShardedQueries(mesh, resolver)

// Read replica by default; primary fallback when no replicas exist.
user, err := queries.GetUser(ctx, arg)

// Strong read from the selected shard's primary.
user, err = queries.GetUser(ctx, arg, db.ReadFromPrimary())

// Reads and writes in a transaction use its selected primary.
user, err = queries.GetUser(ctx, arg, db.WithTx(tx))

Writes always execute against the selected primary. A transaction-bound wrapper deliberately drops mirrors, avoiding cross-database transactions.

OpenTelemetry

Generated routed queries emit OpenTelemetry spans, operation counts, and duration histograms. They use the global providers by default, or explicit providers can be supplied through Options.TracerProvider and Options.MeterProvider (or their Builder equivalents). The query context is propagated to the selected database wrapper, allowing pgx instrumentation to create child spans and metric exemplars to link back to traces.

See Enable OpenTelemetry for setup and the metric and attribute contract.

Structured debug logging

Set Options.Logger or call Builder.WithLogger to emit one structured slog record for each completed routed query. Records use Debug level and include the query, selected route, duration, mirror count, and error outcome. A nil logger disables logging.

See Enable structured logging for configuration and the complete record contract.

Write mirrors for shard expansion

VShardMapping.MirrorReplicaSets is primarily a staged resharding mechanism. Keep the old database as MainReplicaSet, add the future database as a mirror, and dual-write live changes while historical rows are backfilled and verified. After reconciliation, switch MainReplicaSet to the new database. The old database can temporarily become the new database's mirror to preserve a rollback window.

Generated mirrored write wrappers:

  1. Execute the primary and return immediately on primary failure.
  2. Execute mirrors sequentially after primary success.
  3. Always retain result values returned by the primary.
  4. Ignore sql.ErrNoRows from mirrors, useful for idempotent migrations.
  5. Return the first other mirror error by default.

The old and new writes are ordered but not atomic: mirror failure does not roll back a successful primary write. pgmesh also does not backfill or reconcile data. Transaction-bound calls, :batch* result objects, direct pgx writes, and older application versions bypass mirror fan-out; :copyfrom writes are mirrored normally. Cover those paths with an outbox, CDC, or explicit replay before cutover.

Avoid ignore_mirror_error: true during migration unless another durable repair path captures failures. Follow the full old-to-new cutover guide before switching traffic.

Development and verification

Development commands use just. Run just without arguments to list the available recipes. Recipes are organized under just/ by responsibility:

  • generation.just generates the integration fixture and example package.
  • testing.just runs tests, vet, and golangci-lint.
  • integration.just manages the local PostgreSQL topology and smoke tests.
  • toolings.just installs pinned sqlc and golangci-lint binaries under bin/.

The main verification commands are:

Command Purpose
just test Run all unit and example tests.
just lint Run the pinned linter with .golangci.yaml.
just verify-unit Regenerate checked-in code, then run tests, vet, and lint.
just integration Start PostgreSQL, run integration and example smoke tests, then clean up.
just verify Run both the non-Docker and Docker-backed verification suites.

The checked-in integration fixture is generated by sqlc v1.31.1 and compiled as part of the module tests. Table-driven unit tests cover topology validation, hashing, virtual-shard ranges, annotation parsing, route conflicts, sqlc command shapes, and generated option combinations.

Separate GitHub Actions workflows check formatting and module-file drift, build every package, run tests with the race detector and coverage, lint, scan known vulnerabilities, verify generated code, and execute the full Docker-backed suite. Failed integration runs retain Docker logs for seven days; test runs retain the coverage profile for seven days.

Local PostgreSQL integration

integration/docker-compose.yaml starts five isolated PostgreSQL 18 databases:

Endpoint Default port Purpose
shard0-primary 25432 Primary for virtual shard 0
shard0-replica0 25433 First read endpoint for shard 0
shard0-replica1 25434 Second read endpoint for shard 0
shard1-primary 25435 Primary and read fallback for shard 1
shard0-mirror 25436 Synchronous write mirror for shard 0

The replica containers are intentionally independent databases rather than a streaming-replication cluster. Tests seed distinct marker rows into each one so they can prove exactly which endpoint handled each generated read. The suite then validates round-robin replica reads, primary fallback, forced-primary reads, virtual-shard write routing, synchronous mirrors, real PostgreSQL mirror errors, transaction pinning, mirror suppression in transactions, and manually partitioned COPY FROM fan-out.

Run only the Docker-backed suite, including automatic startup, health waits, race detection, and cleanup:

just integration

Run the complete local validation—generation, table-driven unit tests, vet, lint, and the five-database integration suite—with:

just verify

For debugging, the lifecycle can be controlled separately:

just integration-up
just integration-test
just integration-down

Ports can be changed with PGMESH_SHARD0_PRIMARY_PORT, PGMESH_SHARD0_REPLICA0_PORT, PGMESH_SHARD0_REPLICA1_PORT, PGMESH_SHARD1_PRIMARY_PORT, and PGMESH_SHARD0_MIRROR_PORT. The test also accepts full DSN overrides using the corresponding _DSN variables.

Equivalent isolated non-Docker commands are:

just generate-fixture
go test ./...
go vet ./...
just lint

Documentation

Overview

Package pgmesh provides type-safe replica routing and virtual sharding for query wrappers generated by the pgmesh process plugin.

Index

Examples

Constants

View Source
const (
	// MetricQueryCount is the counter for completed routed queries.
	MetricQueryCount = "pgmesh.query.count"
	// MetricQueryDuration is the histogram of routed query durations in seconds.
	MetricQueryDuration = "pgmesh.query.duration"
)

OpenTelemetry metric instrument names emitted for routed queries.

View Source
const (
	// AttributeQueryName identifies the generated query method.
	AttributeQueryName = "pgmesh.query.name"
	// AttributeQueryKind identifies whether a routed query is a read or write.
	AttributeQueryKind = "pgmesh.query.kind"
	// AttributeQueryError reports whether a routed query returned an error.
	AttributeQueryError = "pgmesh.query.error"
	// AttributeVShard identifies the selected virtual shard.
	AttributeVShard = "pgmesh.route.vshard"
	// AttributeReplicaSet identifies the selected physical replica set.
	AttributeReplicaSet = "pgmesh.route.replica_set"
	// AttributeRouteMode identifies the database path selected for a query.
	AttributeRouteMode = "pgmesh.route.mode"
	// AttributeWriteMirrorCount reports the number of configured write mirrors.
	AttributeWriteMirrorCount = "pgmesh.route.write_mirror_count"
)

OpenTelemetry attribute keys recorded on routed query telemetry.

Variables

View Source
var (
	// ErrNoReplicaSets indicates that a topology contains no replica sets.
	ErrNoReplicaSets = errors.New("pgmesh: at least one replica set is required")
	// ErrEmptyReplicaSetName indicates that a replica set has no name.
	ErrEmptyReplicaSetName = errors.New("pgmesh: replica set name must not be empty")
	// ErrDuplicateReplicaSet indicates that a topology reuses a replica set name.
	ErrDuplicateReplicaSet = errors.New("pgmesh: duplicate replica set")
	// ErrEmptyDSN indicates that a database connection has no DSN.
	ErrEmptyDSN = errors.New("pgmesh: connection DSN must not be empty")
	// ErrNoVShards indicates that a topology contains no virtual shards.
	ErrNoVShards = errors.New("pgmesh: at least one virtual shard is required")
	// ErrDuplicateVShard indicates that a virtual shard has already been linked.
	ErrDuplicateVShard = errors.New("pgmesh: virtual shard is already linked")
	// ErrMissingVShard indicates that a virtual shard has not been linked.
	ErrMissingVShard = errors.New("pgmesh: virtual shard is not linked")
	// ErrVShardOutOfRange indicates that a virtual shard index is outside the topology.
	ErrVShardOutOfRange = errors.New("pgmesh: virtual shard is out of range")
	// ErrNoShardHasher indicates that no shard-key hasher was configured.
	ErrNoShardHasher = errors.New("pgmesh: shard hasher is required")
	// ErrNoNodeFactory indicates that no database node factory was configured.
	ErrNoNodeFactory = errors.New("pgmesh: node factory is required")
	// ErrUnknownReplicaSet indicates that a shard mapping names an undefined replica set.
	ErrUnknownReplicaSet = errors.New("pgmesh: unknown replica set")
	// ErrNilReplicaSet indicates that a builder was given a nil replica set.
	ErrNilReplicaSet = errors.New("pgmesh: replica set must not be nil")
	// ErrMirrorConfiguration indicates that write-mirror mappings are inconsistent.
	ErrMirrorConfiguration = errors.New("pgmesh: inconsistent mirror configuration")
)

Functions

func VShardRange

func VShardRange(from, to uint64) []uint64

VShardRange returns the half-open virtual shard range [from, to).

Types

type Builder

type Builder[R any, W Mirrorable[W], SK any] struct {
	// contains filtered or unexported fields
}

Builder incrementally assembles and validates an immutable Mesh topology.

func NewBuilder

func NewBuilder[R any, W Mirrorable[W], SK any](numVShards uint64) *Builder[R, W, SK]

NewBuilder creates a builder with numVShards unlinked virtual shards.

Example
package main

import (
	"fmt"

	"github.com/clnv/pgmesh"
)

type exampleReadQueries struct {
	node string
}

type exampleStoreQueries struct {
	node    string
	mirrors []*exampleStoreQueries
}

func (q *exampleStoreQueries) WithMirrors(mirrors ...*exampleStoreQueries) *exampleStoreQueries {
	return &exampleStoreQueries{
		node:    q.node,
		mirrors: append(append([]*exampleStoreQueries(nil), q.mirrors...), mirrors...),
	}
}

func (q *exampleStoreQueries) Put(value string) []string {
	writes := []string{q.node + ":" + value}
	for _, mirror := range q.mirrors {
		writes = append(writes, mirror.node+":"+value)
	}
	return writes
}

func exampleNode(name string) pgmesh.Node[*exampleReadQueries, *exampleStoreQueries] {
	return pgmesh.NewNode(
		&exampleReadQueries{node: name},
		&exampleStoreQueries{node: name, mirrors: nil},
	)
}

func main() {
	shard0 := pgmesh.NewReplicaSet(
		"shard-0",
		exampleNode("shard0-primary"),
		[]pgmesh.Node[*exampleReadQueries, *exampleStoreQueries]{
			exampleNode("shard0-replica0"),
			exampleNode("shard0-replica1"),
		},
	)
	shard1 := pgmesh.NewReplicaSet("shard-1", exampleNode("shard1-primary"), nil)

	mesh, err := pgmesh.NewBuilder[*exampleReadQueries, *exampleStoreQueries, uint64](2).
		WithHasher(pgmesh.ModularShardHashFor[uint64](2)).
		Link(0, shard0).
		Link(1, shard1).
		Build()
	if err != nil {
		panic(err)
	}

	routed, err := mesh.Shard(2)
	if err != nil {
		panic(err)
	}
	fmt.Println(routed.Name(), routed.VShardIndex())
	fmt.Println(routed.Read().node)
	fmt.Println(routed.Read().node)
	fmt.Println(routed.Write().Put("message"))

	fallback, err := mesh.Shard(3)
	if err != nil {
		panic(err)
	}
	fmt.Println(fallback.Read().node)

	for _, shard := range mesh.AllShards() {
		fmt.Println(shard.Name())
	}

}
Output:
shard-0 0
shard0-replica0
shard0-replica1
[shard0-primary:message]
shard1-primary
shard-0
shard-1

func (*Builder[R, W, SK]) Build

func (b *Builder[R, W, SK]) Build() (*Mesh[R, W, SK], error)

Build validates the topology and returns an immutable mesh.

func (b *Builder[R, W, SK]) Link(vshard uint64, rs *ReplicaSet[R, W]) *Builder[R, W, SK]

Link records validation failures and returns the builder so topology setup remains fluent without panics. Build returns the first recorded error.

func (*Builder[R, W, SK]) WithHasher

func (b *Builder[R, W, SK]) WithHasher(hasher ShardHasher[SK]) *Builder[R, W, SK]

WithHasher configures the mapping from shard keys to virtual shard indexes.

func (*Builder[R, W, SK]) WithLogger

func (b *Builder[R, W, SK]) WithLogger(logger *slog.Logger) *Builder[R, W, SK]

WithLogger configures optional structured logging for routed queries. Completed queries are logged at Debug level. A nil logger disables logging.

func (*Builder[R, W, SK]) WithMeterProvider

func (b *Builder[R, W, SK]) WithMeterProvider(provider metric.MeterProvider) *Builder[R, W, SK]

WithMeterProvider configures the provider used for routed query metrics. A nil provider uses the global OpenTelemetry meter provider.

func (*Builder[R, W, SK]) WithTracerProvider

func (b *Builder[R, W, SK]) WithTracerProvider(provider trace.TracerProvider) *Builder[R, W, SK]

WithTracerProvider configures the provider used for routed query spans. A nil provider uses the global OpenTelemetry tracer provider.

type Connection

type Connection struct {
	// DSN is the PostgreSQL data source name passed to Options.CreateNode.
	DSN string
}

Connection identifies a database node by its connection string.

type IntShardKey

type IntShardKey interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

IntShardKey is the set of integer types supported by ModularShardHashFor.

type Mesh

type Mesh[R any, W Mirrorable[W], SK any] struct {
	// contains filtered or unexported fields
}

Mesh routes logical shard keys through virtual shards to physical replica sets. Its topology is immutable after construction and safe for concurrent use.

func CreateMesh

func CreateMesh[R any, W Mirrorable[W], SK any](
	ctx context.Context,
	opts *Options[R, W, SK],
) (*Mesh[R, W, SK], error)

CreateMesh validates opts, opens its database nodes, and builds an immutable mesh.

Example
package main

import (
	"context"
	"fmt"

	"github.com/clnv/pgmesh"
)

type exampleReadQueries struct {
	node string
}

type exampleStoreQueries struct {
	node    string
	mirrors []*exampleStoreQueries
}

func (q *exampleStoreQueries) WithMirrors(mirrors ...*exampleStoreQueries) *exampleStoreQueries {
	return &exampleStoreQueries{
		node:    q.node,
		mirrors: append(append([]*exampleStoreQueries(nil), q.mirrors...), mirrors...),
	}
}

func (q *exampleStoreQueries) Put(value string) []string {
	writes := []string{q.node + ":" + value}
	for _, mirror := range q.mirrors {
		writes = append(writes, mirror.node+":"+value)
	}
	return writes
}

func exampleNode(name string) pgmesh.Node[*exampleReadQueries, *exampleStoreQueries] {
	return pgmesh.NewNode(
		&exampleReadQueries{node: name},
		&exampleStoreQueries{node: name, mirrors: nil},
	)
}

func main() {
	mesh, err := pgmesh.CreateMesh(context.Background(), &pgmesh.Options[
		*exampleReadQueries,
		*exampleStoreQueries,
		uint64,
	]{
		ReplicaSets: []pgmesh.ReplicaSetSpec{
			{
				Name:     "east",
				Primary:  pgmesh.Connection{DSN: "east-primary"},
				Replicas: []pgmesh.Connection{{DSN: "east-replica"}},
			},
			{Name: "west", Primary: pgmesh.Connection{DSN: "west-primary"}},
			{Name: "archive", Primary: pgmesh.Connection{DSN: "archive-primary"}},
		},
		Shards: pgmesh.Shards{
			NumVShards: 4,
			Mappings: []pgmesh.VShardMapping{
				{
					VShards:           []uint64{0, 2},
					MainReplicaSet:    "east",
					MirrorReplicaSets: []string{"archive"},
				},
				{VShards: []uint64{1, 3}, MainReplicaSet: "west"},
			},
		},
		CreateNode: func(_ context.Context, dsn string) (
			pgmesh.Node[*exampleReadQueries, *exampleStoreQueries],
			error,
		) {
			return exampleNode(dsn), nil
		},
		ShardHasher: pgmesh.ModularShardHashFor[uint64](4),
	})
	if err != nil {
		panic(err)
	}

	routed, err := mesh.Shard(6)
	if err != nil {
		panic(err)
	}
	fmt.Println(routed.Name(), routed.VShardIndex())
	fmt.Println(routed.Read().node)
	fmt.Println(routed.Write().Put("event"))

}
Output:
east 2
east-replica
[east-primary:event archive-primary:event]

func (*Mesh[R, W, SK]) AllShards

func (m *Mesh[R, W, SK]) AllShards() []*Shard[R, W]

AllShards returns one entry per physical replica set in first-vshard order.

func (*Mesh[R, W, SK]) Shard

func (m *Mesh[R, W, SK]) Shard(key SK) (*Shard[R, W], error)

Shard resolves key to its virtual shard and physical replica set.

func (*Mesh[R, W, SK]) StartQueryTrace

func (m *Mesh[R, W, SK]) StartQueryTrace(
	ctx context.Context,
	queryName string,
	kind QueryKind,
) (context.Context, *QueryTrace)

StartQueryTrace starts telemetry for a routed query and returns the span context so database instrumentation can create child spans.

type Mirrorable

type Mirrorable[W any] interface {
	// WithMirrors returns a copy that also writes to the supplied mirrors.
	WithMirrors(...W) W
}

Mirrorable is implemented by generated primary-capable query wrappers. WithMirrors must return a new value and leave the receiver unchanged.

type Node

type Node[R any, W Mirrorable[W]] struct {
	// contains filtered or unexported fields
}

Node contains the read-only and primary-capable views of one database connection. ReplicaSet exposes only Reader for replicas and Writer for the primary, preventing writes from accidentally being routed to replicas.

func NewNode

func NewNode[R any, W Mirrorable[W]](reader R, writer W) Node[R, W]

NewNode creates a database node from its read-only and primary-capable views.

func (Node[R, W]) Reader

func (n Node[R, W]) Reader() R

Reader returns the node's read-only query view.

func (Node[R, W]) Writer

func (n Node[R, W]) Writer() W

Writer returns the node's primary-capable query view.

type Options

type Options[R any, W Mirrorable[W], SK any] struct {
	// ReplicaSets define the physical database nodes in the topology.
	ReplicaSets []ReplicaSetSpec
	// Shards defines virtual shard placement and write mirrors.
	Shards Shards

	// CreateNode opens the node identified by a DSN.
	CreateNode func(context.Context, string) (Node[R, W], error)
	// ShardHasher maps application shard keys to virtual shard indexes.
	ShardHasher ShardHasher[SK]
	// TracerProvider records routed query spans; nil uses the global provider.
	TracerProvider trace.TracerProvider
	// MeterProvider records routed query metrics; nil uses the global provider.
	MeterProvider metric.MeterProvider
	// Logger receives routed query debug logs; nil disables logging.
	Logger *slog.Logger
}

Options configures declarative mesh construction.

type QueryKind

type QueryKind string

QueryKind classifies a routed query as a read or write.

const (
	// QueryKindRead identifies a read query.
	QueryKindRead QueryKind = "read"
	// QueryKindWrite identifies a write query.
	QueryKindWrite QueryKind = "write"
)

Query kinds recorded by generated routed query methods.

type QueryTrace

type QueryTrace struct {
	// contains filtered or unexported fields
}

QueryTrace tracks telemetry for one routed query.

func (*QueryTrace) End

func (t *QueryTrace) End(err error)

End records metrics and a debug log, records err if present, then ends the routed query span.

func (*QueryTrace) SetRoute

func (t *QueryTrace) SetRoute(
	vshard uint64,
	replicaSet string,
	mode RouteMode,
	writeMirrorCount int,
)

SetRoute records the selected virtual shard, replica set, route mode, and synchronous mirror count.

type ReplicaSet

type ReplicaSet[R any, W Mirrorable[W]] struct {
	// contains filtered or unexported fields
}

ReplicaSet represents one physical shard. Reads are balanced across replica readers, while writes always use the primary writer and its configured synchronous mirrors.

func NewReplicaSet

func NewReplicaSet[R any, W Mirrorable[W]](
	name string,
	primary Node[R, W],
	replicas []Node[R, W],
) *ReplicaSet[R, W]

NewReplicaSet creates a physical replica set. If replicas is empty, reads fall back to the primary node.

func (*ReplicaSet[R, W]) Name

func (s *ReplicaSet[R, W]) Name() string

Name returns the replica set's topology name.

func (*ReplicaSet[R, W]) Read

func (s *ReplicaSet[R, W]) Read() R

Read returns the next read view selected by round-robin balancing.

func (*ReplicaSet[R, W]) WithWriteMirrors

func (s *ReplicaSet[R, W]) WithWriteMirrors(writes ...W) *ReplicaSet[R, W]

WithWriteMirrors returns a copy with writes appended to its synchronous mirrors.

func (*ReplicaSet[R, W]) Write

func (s *ReplicaSet[R, W]) Write() W

Write returns the primary write view configured with synchronous mirrors.

func (*ReplicaSet[R, W]) WriteMirrorCount

func (s *ReplicaSet[R, W]) WriteMirrorCount() int

WriteMirrorCount returns the number of synchronous write mirrors.

type ReplicaSetSpec

type ReplicaSetSpec struct {
	// Name uniquely identifies the replica set within a topology.
	Name string
	// Primary is the replica set's writable database node.
	Primary Connection
	// Replicas are read-only nodes used for round-robin reads.
	Replicas []Connection
}

ReplicaSetSpec describes a primary database and its read replicas.

type RouteMode

type RouteMode string

RouteMode describes the database path selected for a routed query.

const (
	// RouteModeRead indicates a read routed through the replica load balancer.
	RouteModeRead RouteMode = "read"
	// RouteModePrimary indicates a read or write routed directly to the primary.
	RouteModePrimary RouteMode = "primary"
	// RouteModeTransaction indicates a query executed on an explicit transaction.
	RouteModeTransaction RouteMode = "transaction"
)

Route modes recorded after a query resolves to a shard.

type Shard

type Shard[R any, W Mirrorable[W]] struct {
	*ReplicaSet[R, W]
	// contains filtered or unexported fields
}

Shard is a routed virtual shard linked to a physical replica set.

func (*Shard[R, W]) VShardIndex

func (s *Shard[R, W]) VShardIndex() uint64

VShardIndex returns the virtual shard index used to select this shard.

type ShardHasher

type ShardHasher[SK any] interface {
	// Hash returns the virtual shard index for key.
	Hash(SK) uint64
}

ShardHasher maps an application shard key to a virtual shard index.

func ConstantShardHashFor

func ConstantShardHashFor[SK any](vshard uint64) ShardHasher[SK]

ConstantShardHashFor returns a hasher that always selects vshard.

func ModularShardHashFor

func ModularShardHashFor[SK IntShardKey](numVShards uint64) ShardHasher[SK]

ModularShardHashFor returns a hasher that maps integer keys modulo numVShards. It panics if numVShards is zero.

type Shards

type Shards struct {
	// NumVShards is the total number of virtual shards in the topology.
	NumVShards uint64
	// Mappings assign every virtual shard to a physical replica set.
	Mappings []VShardMapping
}

Shards describes the virtual-shard topology and its physical mappings.

type VShardMapping

type VShardMapping struct {
	// VShards are the virtual shard indexes covered by this mapping.
	VShards []uint64
	// MainReplicaSet names the replica set that serves reads and primary writes.
	MainReplicaSet string
	// MirrorReplicaSets name replica sets that synchronously receive writes.
	MirrorReplicaSets []string
}

VShardMapping assigns virtual shards to a main replica set and write mirrors.

Directories

Path Synopsis
cmd
sqlc-gen-store command
examples
integration
Package sqlcplugin generates pgmesh query wrappers from sqlc metadata.
Package sqlcplugin generates pgmesh query wrappers from sqlc metadata.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL