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

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: TypeMaster and TypeReader. Both strategies leverage the same core weightBalancer implementation found in 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, 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, 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.

Using Load Balancers in Your Code

To utilize these strategies, use the conn.NewConn function from router/conn/conn.go, which automatically registers the appropriate balancer and injects the WRR-based picker:

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.
  • 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 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. 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, specifically balancer.TypeMaster and balancer.TypeReader. The core selection logic resides in router/balancer/balancer.go within the weightBalancer struct, while the builder pattern implementation is found in router/balancer/balancerbuilder.go. The public API for creating connections with these balancers is exposed through conn.NewConn in router/conn/conn.go.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →