OOro

Sharding

Route model queries and writes to configured shard connections.

Sharding is a core routing feature. It maps a model query to one of several configured connections through a shard strategy.

Configure shards

db, err := oro.Open(oro.Config{
    Connections: map[string]oro.ConnectionConfig{
        "orders_0": {Driver: mysql.Open(dsn0)},
        "orders_1": {Driver: mysql.Open(dsn1)},
    },
    Shards: map[string]oro.ShardConfig{
        "orders": {
            Connections: []string{"orders_0", "orders_1"},
            Strategy:    oro.ModShard("TenantID"),
        },
    },
})

ModShard(field) hashes an integer-like field by modulo over the configured connection list.

Model sharding

A model opts into a shard group in Define:

type Order struct {
    oro.Model
    TenantID uint64
    Code     string
    Total    uint
}

func (Order) Define(s *oro.SchemaBuilder) {
    s.Table("orders")
    s.Shard("orders", "TenantID")
    s.Field("TenantID").UnsignedBigInt().Index()
    s.Field("Code").String().Unique()
    s.Field("Total").Uint()
}

The field names passed to Shard are Go field names.

Query one shard

Pass shard values explicitly:

orders, err := db.Use[Order]().
    Shard(oro.Map{"TenantID": uint64(10)}).
    Where("Code", "A001").
    Get(ctx)

Writes also require shard values:

created, err := db.Use[Order]().
    Shard(oro.Map{"TenantID": uint64(10)}).
    Create(ctx, &Order{TenantID: 10, Code: "A001", Total: 120})

If a sharded model is queried or written without shard values, Oro returns oro.ErrShardRequired.

Query all shards

Use AllShards() for read-oriented fan-out queries:

orders, err := db.Use[Order]().
    AllShards().
    Where("Status", "paid").
    OrderBy("Code").
    Get(ctx)

AllShards fans the query out to every configured shard connection. For Get, First, and pagination it performs a global merge: rows from all shards are combined, sorted by the query’s ORDER BY, and then LIMIT / OFFSET are applied globally across the merged result. Keep it for reads or controlled maintenance flows.

:::note[Global ordering and paging] All-shard reads now merge and order globally instead of concatenating each shard’s rows. Earlier versions applied LIMIT / OFFSET per shard, so ordering and pagination across shards were incorrect. Because the merge happens in memory, an explicit ORDER BY is required for any ordered all-shard read. First() over AllShards() returns the global-first row and reports oro.ErrOrderRequired when no ORDER BY is set. :::

Cross-shard aggregates

Count over AllShards() is supported and sums the per-shard counts:

total, err := db.Use[Order]().
    AllShards().
    Where("Status", "paid").
    Count(ctx)

Sum, Avg, Min, and Max cannot be computed correctly by merging per-shard values, so over AllShards() they now return oro.ErrUnsupported instead of silently returning a single shard’s result. Compute these at the application layer (or per shard) when you need them across shards.

Transactions

A single transaction cannot span multiple shards. If a write path needs more than one shard, coordinate it at the application layer.

Tenant extension integration

The extensions/tenant package can provide shard values automatically. See Tenant Extension when shard routing is tenant-based.

Edit this page