-
Notifications
You must be signed in to change notification settings - Fork 0
hidb_en.md
The hidb package provides the database abstraction layer and connection manager for the HypGo framework, supporting multiple database engines, read-write separation (Master-Replica), connection pooling, transaction management, Redis caching, and an extensible plugin system.
-
Multi-Database Support: Abstracts MySQL / PostgreSQL driver differences through the
Dialectinterface, using Bun ORM as the query builder layer. -
Read-Write Separation: Write operations always route to Master, read operations are distributed to Replicas via
ReplicaPoolusing Round-Robin, with automatic fallback to Master when no Replicas are available. -
Lock-Free Round-Robin: Uses
atomic.Pointer+atomic.Uint64for a completely lock-free read path (copy-on-write), zero mutex contention at 100k+ QPS. -
Connection Pool Management: Supports
MaxIdleConns/MaxOpenConns/ConnMaxLifetime(default 30 minutes) configuration, with Master and each Replica managing connection pools independently. -
Transaction Management: Provides both native
sql.Txand Bun ORMbun.Txtransaction interfaces. -
Redis Integration: Built-in
go-redis/v9client, unified lifecycle management with SQL databases. -
Plugin System: Supports dynamic registration and loading of non-SQL databases (e.g., Cassandra) through the
DatabasePlugininterface. - Graceful Shutdown: Shuts down in order: Replica -> Master -> Redis -> Plugins, with accumulated errors reported together.
-
Backward Compatible:
HypDB()/SQL()always return Master; existing code requires no changes.
package main
import (
"context"
"log"
"github.com/maoxiaoyue/hypgo/pkg/hidb"
"github.com/maoxiaoyue/hypgo/pkg/hidb/pg"
)
func main() {
// 1. Create database manager (PostgreSQL)
db, err := hidb.NewWithInterface(
appConfig.Database, // Implements config.DatabaseConfigInterface
hidb.WithDialect(pg.New()), // Specify SQL dialect
)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 2. Health check
if err := db.HealthCheck(context.Background()); err != nil {
log.Fatal(err)
}
// 3. Read operations -> Replica (falls back to Master when no Replicas)
readDB := db.ReadHypDB()
var users []User
readDB.NewSelect().Model(&users).Scan(context.Background())
// 4. Write operations -> Always uses Master
writeDB := db.WriteHypDB()
writeDB.NewInsert().Model(&newUser).Exec(context.Background())
}Enable read-write separation by implementing the config.ReplicaConfigProvider interface:
type ReplicaConfigProvider interface {
GetReplicas() []ReplicaConfig
}
type ReplicaConfig struct {
DSN string // Replica connection string
MaxIdleConns int // Maximum idle connections
MaxOpenConns int // Maximum connections
}| Method | Target | Description |
|---|---|---|
ReadHypDB() |
Replica (ORM) | Round-Robin distribution, falls back to Master |
ReadSQL() |
Replica (Raw SQL) | Round-Robin distribution, falls back to Master |
WriteHypDB() |
Master (ORM) | Always uses Master |
WriteSQL() |
Master (Raw SQL) | Always uses Master |
HypDB() |
Master (ORM) | Backward compatible, equivalent to WriteHypDB()
|
SQL() |
Master (Raw SQL) | Backward compatible, equivalent to WriteSQL()
|
// Read path is completely lock-free: atomic.Pointer + atomic.Uint64
// Write path uses copy-on-write: Mutex protection + create new slice + atomic.Store
replicas := *rp.replicas.Load() // Zero lock contention
idx := rp.counter.Add(1) - 1
replica := replicas[idx % uint64(len(replicas))]Master and all Replicas uniformly set ConnMaxLifetime = 30 minutes, preventing long-lived connections from holding stale state (e.g., DNS changes, password rotation).
db.HasReplicas() // bool -- Whether Replicas are configured
db.ReplicaCount() // int -- Number of Replicaserr := db.Transaction(ctx, func(tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, "INSERT INTO users (name) VALUES (?)", "Alice")
if err != nil {
return err // Auto Rollback
}
return nil // Auto Commit
})err := db.HypDBTransaction(ctx, func(ctx context.Context, tx bun.Tx) error {
_, err := tx.NewInsert().Model(&user).Exec(ctx)
if err != nil {
return err // Auto Rollback
}
return nil // Auto Commit
})HiDB includes a complete Redis wrapper, also compatible with KeyDB (Redis protocol compatible). Beyond the raw go-redis/v9 client, it provides high-level methods covering all common data structures.
database:
driver: "redis" # Or used alongside SQL
redis:
addr: "localhost:6379" # KeyDB uses the same settings
password: "your_password"
db: 0type RedisConfigInterface interface {
GetAddr() string
GetPassword() string
GetDB() int
}ctx := context.Background()
// Set (with TTL, 0 = no expiry)
db.RedisSet(ctx, "user:1:name", "Alice", 10*time.Minute)
// Get
name, err := db.RedisGet(ctx, "user:1:name")
if hidb.RedisIsNil(err) {
// Key does not exist
}
// Delete
db.RedisDel(ctx, "user:1:name", "user:2:name")
// Check existence
count, _ := db.RedisExists(ctx, "user:1:name")
// TTL management
db.RedisExpire(ctx, "session:abc", 30*time.Minute)
ttl, _ := db.RedisTTL(ctx, "session:abc")Store and retrieve Go structs directly with automatic JSON serialization/deserialization:
type GameState struct {
Players []string `json:"players"`
Round int `json:"round"`
Status string `json:"status"`
}
// Store
state := GameState{Players: []string{"p1", "p2"}, Round: 1, Status: "playing"}
db.RedisSetJSON(ctx, "game:123:state", state, time.Hour)
// Retrieve
var loaded GameState
db.RedisGetJSON(ctx, "game:123:state", &loaded)// Set multiple fields
db.RedisHSet(ctx, "user:1", "name", "Alice", "email", "alice@test.com")
// Get single field
name, _ := db.RedisHGet(ctx, "user:1", "name")
// Get all fields
all, _ := db.RedisHGetAll(ctx, "user:1")
// all = map[string]string{"name": "Alice", "email": "alice@test.com"}
// Delete field
db.RedisHDel(ctx, "user:1", "email")// Push
db.RedisLPush(ctx, "queue:tasks", "task1", "task2")
db.RedisRPush(ctx, "queue:tasks", "task3")
// Pop
task, _ := db.RedisLPop(ctx, "queue:tasks")
// Range query
tasks, _ := db.RedisLRange(ctx, "queue:tasks", 0, -1)
// Length
length, _ := db.RedisLLen(ctx, "queue:tasks")// Add members
db.RedisSAdd(ctx, "room:1:players", "player1", "player2")
// Check membership
isMember, _ := db.RedisSIsMember(ctx, "room:1:players", "player1")
// Get all members
members, _ := db.RedisSMembers(ctx, "room:1:players")
// Remove member
db.RedisSRem(ctx, "room:1:players", "player1")import "github.com/redis/go-redis/v9"
// Add scores
db.RedisZAdd(ctx, "leaderboard", redis.Z{Score: 100, Member: "player1"})
// Get ranking (with scores)
results, _ := db.RedisZRangeWithScores(ctx, "leaderboard", 0, 9) // Top 10
// Query score
score, _ := db.RedisZScore(ctx, "leaderboard", "player1")
// Remove
db.RedisZRem(ctx, "leaderboard", "player1")// Counter
db.RedisIncr(ctx, "api:requests:count")
db.RedisDecr(ctx, "stock:item:42")
db.RedisIncrBy(ctx, "user:1:score", 50)// Attempt to acquire lock (30 second auto-release)
acquired, _ := db.RedisSetNX(ctx, "lock:game:123", "owner-uuid", 30*time.Second)
if acquired {
defer db.RedisDel(ctx, "lock:game:123")
// Execute critical section...
}// Publish
db.RedisPublish(ctx, "game:events", "player_joined")
// Subscribe (caller must Close)
sub, _ := db.RedisSubscribe(ctx, "game:events")
defer sub.Close()
for msg := range sub.Channel() {
fmt.Printf("Channel: %s, Payload: %s\n", msg.Channel, msg.Payload)
}// Regular pipeline (non-transactional)
pipe, _ := db.RedisPipeline()
pipe.Set(ctx, "key1", "val1", 0)
pipe.Set(ctx, "key2", "val2", 0)
pipe.Incr(ctx, "counter")
cmds, err := pipe.Exec(ctx)
// Transactional pipeline (MULTI/EXEC)
txPipe, _ := db.RedisTxPipeline()
txPipe.Set(ctx, "key1", "val1", 0)
txPipe.Set(ctx, "key2", "val2", 0)
cmds, err = txPipe.Exec(ctx)// Non-blocking key scan
var cursor uint64
for {
keys, nextCursor, _ := db.RedisScan(ctx, cursor, "user:*", 100)
for _, key := range keys {
fmt.Println(key)
}
cursor = nextCursor
if cursor == 0 {
break
}
}When you need go-redis features not covered by the wrapper:
client := db.Redis() // *redis.Client
// Use the full go-redis/v9 API directlyKeyDB uses the same protocol as Redis; just change the connection address:
database:
driver: "redis"
redis:
addr: "keydb-server:6379" # Point to KeyDB
password: "password"
db: 0KeyDB advantages: multi-threaded architecture, FLASH storage support, Active Replication -- all work transparently with no code changes required.
type Dialect interface {
DriverName() string // "mysql" or "postgres"
BunDialect() schema.Dialect // Bun ORM dialect instance
}MySQL / TiDB:
import "github.com/maoxiaoyue/hypgo/pkg/hidb/mysql"
db, err := hidb.NewWithInterface(cfg, hidb.WithDialect(mysql.New()))PostgreSQL:
import "github.com/maoxiaoyue/hypgo/pkg/hidb/pg"
db, err := hidb.NewWithInterface(cfg, hidb.WithDialect(pg.New()))type DatabasePlugin interface {
Name() string
Init(config map[string]interface{}) error
Connect() error
Close() error
Ping(ctx context.Context) error
}// Register plugin
cassandraPlugin := cassandra.NewPlugin()
db.RegisterPlugin(cassandraPlugin)
// Dynamic loading (Init + Connect)
db.LoadPlugin("cassandra", map[string]interface{}{
"hosts": []string{"127.0.0.1"},
"keyspace": "my_keyspace",
})
// Get plugin
if plugin, ok := db.GetPlugin("cassandra"); ok {
plugin.Ping(ctx)
}pkg/hidb/cassandra provides a complete Cassandra 5.0+ / ScyllaDB driver wrapper, including a Fluent DDL builder, Model mapping, CRUD builder, Vector/ANN queries, Migration, RBAC, Schema Introspection, and more.
See hidb.cassandra for full documentation.
// Connection status
db.IsConnected() // bool
// Full health check (Master + Replicas + Redis + Plugins)
err := db.HealthCheck(ctx)
// Database driver type
db.Type() // "mysql", "postgres", "redis", etc.
// Graceful shutdown (in order: Replicas -> Master -> Redis -> Plugins)
err := db.Close()When the configuration object does not implement config.DatabaseConfigInterface, New() auto-adapts via reflection:
// Legacy struct (uses reflection adapter)
db, err := hidb.New(legacyConfig, hidb.WithDialect(pg.New()))
// Recommended: implement DatabaseConfigInterface
db, err := hidb.NewWithInterface(modernConfig, hidb.WithDialect(pg.New()))DatabaseConfigAdapter extracts Driver, DSN, MaxIdleConns, MaxOpenConns, Redis, and other fields via reflection, ensuring existing code requires no changes.
pkg/hidb/
├── hidb.go # Core: Database, Dialect, Option, Plugin system,
│ # Transaction management, Health check, Graceful shutdown
├── redis.go # Redis/KeyDB high-level wrapper (KV, Hash, List, Set, ZSet, Pub/Sub, Pipeline)
├── redis_test.go # Redis nil guard + RedisIsNil tests
├── readwrite.go # ReplicaPool: Round-Robin load balancing, Read-write separation
├── readwrite_test.go # 11 tests: Round-Robin, concurrency, fallback, shutdown
├── mysql/
│ └── mysql.go # MySQL / TiDB dialect implementation
├── pg/
│ └── pg.go # PostgreSQL dialect implementation
└── cassandra/
└── cassandra.go # Cassandra plugin implementation
| Package | Purpose |
|---|---|
database/sql |
Go standard SQL interface |
github.com/uptrace/bun |
ORM query builder |
github.com/redis/go-redis/v9 |
Redis client |
github.com/go-sql-driver/mysql |
MySQL driver |
github.com/lib/pq |
PostgreSQL driver |
github.com/gocql/gocql |
Cassandra driver |
由 台灣卯小月 用 ❤️ 製作 · MIT License And Wiki is written by Claude
設計文件
套件
- config — 設定
- context — 請求上下文
- router — 路由器
- server — 伺服器
- middleware — 中介層
- websocket — WebSocket
- hidb — 資料庫 ORM
- hidb/cassandra — Cassandra
- logger — 日誌
- json — JSON 處理
- grpc — gRPC
AI 協作工具鏈
- schema — Schema-first 路由
- manifest — 專案 Manifest
- contract — Contract Testing
- errors — Typed Error Catalog
- migrate — Migration Diff
- scaffold — 智慧 Scaffold
- airules — AI Rules
CLI 命令
- hyp 總覽
- hyp new
- hyp api
- hyp run
- hyp restart
- hyp generate
- hyp migrate
- hyp context
- hyp ai-rules
- hyp chkcomment
- hyp impact
- hyp docker
- hyp health
- hyp version
- hyp difflog
Design Docs
Packages
- config — Configuration
- context — Request Context
- router — Router
- server — Server
- middleware — Middleware
- websocket — WebSocket
- hidb — Database ORM
- hidb/cassandra - Cassandra 5.0
- logger — Logger
- json — JSON
- grpc — gRPC
AI Collaboration Toolchain
- schema — Schema-first Routing
- manifest — Project Manifest
- contract — Contract Testing
- errors — Typed Error Catalog
- migrate — Migration Diff
- scaffold — Smart Scaffold
- airules — AI Rules
CLI Commands