# Load Balancing Strategies in Fabrica-Kit: Master and Reader Modes Explained

> Explore Fabrica-Kit load balancing strategies: Master for sticky writes and Reader for read-only traffic. Learn about weighted-round-robin with route-table stickiness.

- Repository: [Pantheon/fabrica-kit](https://github.com/go-pantheon/fabrica-kit)
- Tags: deep-dive
- Published: 2026-03-02

---

**Fabrica-Kit provides two built-in gRPC load balancing strategies—Master (`TypeMaster`) for sticky write operations and Reader (`TypeReader`) for distributed read-only traffic—both implemented using a weighted-round-robin (WRR) algorithm with optional route-table stickiness.**

Fabrica-Kit is a Go microservice toolkit that offers sophisticated client-side load balancing for gRPC connections. Understanding the available load balancing strategies in fabrica-kit is essential for architects building distributed systems that require consistent write routing to master nodes while scaling read operations across replica pools.

## Overview of Load Balancing Strategies

The `router/balancer` package defines two distinct balancer types in [`router/balancer/register.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/register.go): `TypeMaster` and `TypeReader`. Both strategies leverage the same core `weightBalancer` implementation found in [`router/balancer/balancer.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/balancer.go), but differ significantly in their routing table behavior and consistency guarantees.

## Master Strategy (TypeMaster)

### Purpose and Behavior

The **Master** strategy directs all requests to a single master node, optimized for write-oriented operations that require strong consistency. It implements sticky routing using a `MasterRouteTable` to ensure that requests for the same object ID (OID) and color consistently reach the same backend instance, preventing write conflicts or ordering issues.

### Implementation Details

In [`router/balancer/balancer.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/balancer.go), the `weightBalancer` first attempts to resolve the request's OID and color via the `MasterRouteTable`. If a mapping exists, the request routes to that exact address. Otherwise, the WRR algorithm selects a node and atomically stores the mapping for subsequent calls, establishing persistent stickiness for that object.

## Reader Strategy (TypeReader)

### Purpose and Behavior

The **Reader** strategy distributes read-only traffic across a pool of replica nodes, maximizing throughput for query-heavy workloads. It uses a `ReadOnlyRouteTable` for optional sticky routing but operates without the mandatory write-back behavior found in the Master strategy, making it suitable for stateless read scaling.

### Implementation Details

When `weightBalancer` operates in Reader mode, it consults the `ReadOnlyRouteTable` for an existing OID-to-address mapping. If none exists or the route table is unavailable, it simply selects a node using the weighted-round-robin algorithm. Unlike Master mode, no route-table write-back occurs, allowing for lighter coordination on read paths.

## Core Weighted-Round-Robin Implementation

Both strategies rely on the `weightBalancer` struct in [`router/balancer/balancer.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/balancer.go), which implements nginx-style weighted round-robin selection. The balancer:

1. **Selects** a node based on configured weights using the WRR algorithm, ensuring higher-weighted nodes receive proportionally more traffic.
2. **Consults** route tables (`routetable.ReadOnlyRouteTable` or `routetable.MasterRouteTable`) to enforce sticky routing per OID/color when provided.
3. **Registers** the appropriate balancer builder at runtime via `balancer.RegisterMasterBalancer` or `balancer.RegisterReadOnlyBalancer` in [`router/balancer/register.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/register.go).

## Using Load Balancers in Your Code

To utilize these strategies, use the `conn.NewConn` function from [`router/conn/conn.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/conn/conn.go), which automatically registers the appropriate balancer and injects the WRR-based picker:

```go
import (
    "github.com/go-pantheon/fabrica-kit/router/balancer"
    "github.com/go-pantheon/fabrica-kit/router/routetable"
    "github.com/go-pantheon/fabrica-kit/router/conn"
    "github.com/go-kratos/kratos/v2/registry"
    "github.com/go-kratos/kratos/v2/log"
)

// Create a client for master (write) operations
func NewMasterClient(service string, rt routetable.MasterRouteTable, disc registry.Discovery, logger log.Logger) (*conn.Conn, error) {
    return conn.NewConn(service, balancer.TypeMaster, logger, rt, disc)
}

// Create a client for reader (read-only) operations
func NewReaderClient(service string, rt routetable.ReadOnlyRouteTable, disc registry.Discovery, logger log.Logger) (*conn.Conn, error) {
    return conn.NewConn(service, balancer.TypeReader, logger, rt, disc)
}

```

The `balancer.Type` parameter determines whether the connection uses Master or Reader semantics, while the route table parameters enable sticky routing based on object IDs.

## Summary

- Fabrica-Kit provides **two load balancing strategies**: `TypeMaster` for write operations and `TypeReader` for read-only traffic.
- Both strategies use **weighted-round-robin (WRR)** selection via the shared `weightBalancer` implementation in [`router/balancer/balancer.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/balancer.go).
- **Master strategy** enforces sticky routing with write-back to `MasterRouteTable`, ensuring consistent master node selection for specific object IDs.
- **Reader strategy** distributes load across replicas using `ReadOnlyRouteTable` for optional stickiness without write-back behavior.
- Use `conn.NewConn` from [`router/conn/conn.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/conn/conn.go) to instantiate clients with the desired strategy and route table configuration.

## Frequently Asked Questions

### What is the difference between Master and Reader load balancing in Fabrica-Kit?

The **Master** strategy (`balancer.TypeMaster`) routes all requests to a single master node and uses sticky routing with a `MasterRouteTable` to ensure write consistency for specific object IDs. The **Reader** strategy (`balancer.TypeReader`) distributes read-only traffic across multiple replica nodes using weighted-round-robin, with optional stickiness via `ReadOnlyRouteTable` but without the mandatory route-table write-back behavior found in Master mode.

### How does the weighted-round-robin algorithm work in Fabrica-Kit?

Fabrica-Kit implements nginx-style weighted round-robin in the `weightBalancer` struct located in [`router/balancer/balancer.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/balancer.go). The algorithm selects backend nodes proportionally to their configured weights, ensuring higher-weighted nodes receive more traffic. Both Master and Reader strategies leverage this same core implementation, differing only in how they interact with route tables for sticky routing decisions.

### Can I use Fabrica-Kit load balancing without sticky routing?

Yes, while both strategies support route tables for sticky routing, you can operate without them. For the **Reader** strategy, if no `ReadOnlyRouteTable` is provided or if no mapping exists for a given object ID, the balancer falls back to pure weighted-round-robin selection across all available nodes. However, the **Master** strategy is designed specifically for sticky write routing, so using it without a route table defeats its primary purpose of ensuring consistent master node selection.

### Where are the load balancer types defined in the Fabrica-Kit source code?

The balancer types are defined as constants in [`router/balancer/register.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/register.go), specifically `balancer.TypeMaster` and `balancer.TypeReader`. The core selection logic resides in [`router/balancer/balancer.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/balancer.go) within the `weightBalancer` struct, while the builder pattern implementation is found in [`router/balancer/balancerbuilder.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/balancer/balancerbuilder.go). The public API for creating connections with these balancers is exposed through `conn.NewConn` in [`router/conn/conn.go`](https://github.com/go-pantheon/fabrica-kit/blob/main/router/conn/conn.go).