Documentation
¶
Overview ¶
Package pgmesh provides type-safe replica routing and virtual sharding for query wrappers generated by the pgmesh process plugin.
Index ¶
- Constants
- Variables
- func VShardRange(from, to uint64) []uint64
- type Builder
- func (b *Builder[R, W, SK]) Build() (*Mesh[R, W, SK], error)
- func (b *Builder[R, W, SK]) Link(vshard uint64, rs *ReplicaSet[R, W]) *Builder[R, W, SK]
- func (b *Builder[R, W, SK]) WithHasher(hasher ShardHasher[SK]) *Builder[R, W, SK]
- func (b *Builder[R, W, SK]) WithLogger(logger *slog.Logger) *Builder[R, W, SK]
- func (b *Builder[R, W, SK]) WithMeterProvider(provider metric.MeterProvider) *Builder[R, W, SK]
- func (b *Builder[R, W, SK]) WithTracerProvider(provider trace.TracerProvider) *Builder[R, W, SK]
- type Connection
- type IntShardKey
- type Mesh
- type Mirrorable
- type Node
- type Options
- type QueryKind
- type QueryTrace
- type ReplicaSet
- type ReplicaSetSpec
- type RouteMode
- type Shard
- type ShardHasher
- type Shards
- type VShardMapping
Examples ¶
Constants ¶
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.
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 ¶
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 ¶
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]) Link ¶
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 ¶
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 ¶
AllShards returns one entry per physical replica set in first-vshard order.
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.
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 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.
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 ¶
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.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
sqlc-gen-store
command
|
|
|
examples
|
|
|
01-single-database
command
|
|
|
02-read-write-split
command
|
|
|
03-sharded-read-write
command
|
|
|
04-mirrors-and-transactions
command
|
|
|
integration
|
|
|
Package sqlcplugin generates pgmesh query wrappers from sqlc metadata.
|
Package sqlcplugin generates pgmesh query wrappers from sqlc metadata. |