github gofr-dev/gofr v1.49.0

latest release: pkg/gofr/datasource/dbresolver/v0.1.0
3 hours ago

Release v1.49.0

🚀 New Features

🔹 DB Resolver for MySQL Read/Write Splitting

GoFr introduces a powerful, production-ready DBResolver module that enables automatic read/write splitting between a primary database and multiple replicas.

Key Highlights:

  • Automatic routing based on HTTP methods:

    • Writes (POST, PUT, PATCH, DELETE) → Primary
    • Reads (GET, HEAD, OPTIONS) → Replicas
  • Route-based overrides using PrimaryRoutes for strongly consistent paths

  • Pluggable read-balancing strategies:

    • Round-robin
    • Random
  • Built-in circuit breaker for tracking replica failures & recovery

  • Full integration through dbresolver.InitDBResolver()

  • Unlocks horizontal scaling for read-heavy services

  • Significantly reduces read latency

  • Ensures high resilience with automatic failover to primary when replicas are down

Additional Details:

Installation

go get gofr.dev/pkg/gofr/datasource/dbresolver@latest

Initializing DBResolver

import (
    "gofr.dev/pkg/gofr"
    "gofr.dev/pkg/gofr/datasource/dbresolver"
)

func main() {
    a := gofr.New()

    err := dbresolver.InitDBResolver(a, &dbresolver.Config{
        Strategy:     dbresolver.StrategyRoundRobin,
        ReadFallback: true,     // fallback to primary if all replicas fail
        MaxFailures:  3,        // mark replica down after 3 failures
        TimeoutSec:   30,       // cooldown before retrying
        PrimaryRoutes: []string{
            "/admin",
            "/api/payments/*",
        },
        Replicas: []dbresolver.ReplicaCredential{
            {Host: "localhost:3307", User: "replica_user1", Password: "pass1"},
            {Host: "replica2.example.com:3308", User: "replica_user2", Password: "pass2"},
            {Host: "replica3.example.com:3309", User: "replica_user3", Password: "pass3"},
        },
    })

    if err != nil {
        a.Logger().Errorf("failed to initialize db resolver: %v", err)
    }

    a.Run()
}

Read Endpoint (Auto → Replica)

a.GET("/customers", func(c *gofr.Context) (any, error) {
    var out []Customer
    c.SQL.Select(c, &out, "SELECT id, name FROM customers")
    return out, nil
})

Write Endpoint (Auto → Primary)

a.POST("/customers", func(c *gofr.Context) (any, error) {
    var cust Customer
    c.Bind(&cust)

    _, err := c.SQL.Exec("INSERT INTO customers (name) VALUES (?)", cust.Name)
    return cust, err
})

Forced Primary Route

a.GET("/admin/customers", func(c *gofr.Context) (any, error) {
    var out []Customer
    c.SQL.Select(c, &out, "SELECT id, name FROM customers")
    return out, nil
})

🔹 XML Responses (response.XML)

GoFr now supports raw XML responses without JSON encoding by using response.XML.

Key Highlights:

  • Writes raw XML bytes directly to the client
  • Default Content-Type = application/xml
  • Perfect for legacy and enterprise XML-based systems

Example:

app.GET("/legacy/xml", func(ctx *gofr.Context) (any, error) {
    content := []byte(`<Response status="ok"><Message>Hello</Message></Response>`)
    return response.XML{Content: content}, nil
})

XML Output

<Response status="ok"><Message>Hello</Message></Response>

📦 Improvements

🔹 Responder Improvements

  • Special response types (Raw, File, Template, XML, Redirect) now follow a unified handling flow
  • Avoids incorrect 206 Partial Content responses
  • Ensures default JSON Content-Type

🔹 DBResolver Pooling Controls

Replica pools now scale relative to primary pools. Defaults:

  • Primary: DB_MAX_IDLE_CONNECTION=2 → Replicas default to 8
  • Primary: DB_MAX_OPEN_CONNECTION=20 → Replicas default to 40

Optional overrides:

DB_REPLICA_MAX_IDLE_CAP=100
DB_REPLICA_MIN_IDLE=5
DB_REPLICA_DEFAULT_IDLE=15
DB_REPLICA_MAX_OPEN_CAP=500
DB_REPLICA_MIN_OPEN=20
DB_REPLICA_DEFAULT_OPEN=150

🐛 Bug Fixes & Small Changes

🔹 Authentication Timing Attack Protection

  • API Key and Basic Auth now use consistent time comparisons

Refer : GoFr Official Documentation
https://gofr.dev/docs

Don't miss a new gofr release

NewReleases is sending notifications on new releases.