Skip to content

Commit

Permalink
✨ feat: added resolver databases based on configs #5
Browse files Browse the repository at this point in the history
  • Loading branch information
arisnguyen215 committed Dec 10, 2023
1 parent 180e8b2 commit 7d80adb
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 0 deletions.
90 changes: 90 additions & 0 deletions db.resolver.go
Original file line number Diff line number Diff line change
@@ -1 +1,91 @@
package dbresolver

import (
"database/sql"
"fmt"
"sync"
"time"

"github.com/sivaosorg/govm/dbx"
"github.com/sivaosorg/govm/logger"
"github.com/sivaosorg/govm/mysql"
"github.com/sivaosorg/govm/postgres"
"github.com/sivaosorg/mysqlconn"
"github.com/sivaosorg/postgresconn"
)

// NewPostgresConnector creates a new PostgresConnector instance.
func NewPostgresConnector(config postgres.PostgresConfig) *PostgresConnector {
return &PostgresConnector{Config: config}
}

// NewMySQLConnector creates a new MySQLConnector instance.
func NewMySQLConnector(config mysql.MysqlConfig) *MySQLConnector {
return &MySQLConnector{Config: config}
}

func (p *PostgresConnector) Connect() (*sql.DB, dbx.Dbx) {
psql, s := postgresconn.NewClient(p.Config)
if s.IsConnected {
return psql.GetConn().DB, s
}
return nil, s
}

func (m *MySQLConnector) Connect() (*sql.DB, dbx.Dbx) {
msql, s := mysqlconn.NewClient(m.Config)
return msql.GetConn(), s
}

// NewMultiTenantDBResolver creates a new MultiTenantDBResolver instance.
func NewMultiTenantDBResolver() *MultiTenantDBResolver {
return &MultiTenantDBResolver{
connectors: make(map[string]DBConnector),
once: make(map[string]*sync.Once),
}
}

// AddConnector adds a new database connector for a specific tenant.
func (r *MultiTenantDBResolver) AddConnector(tenantId string, connector DBConnector) *MultiTenantDBResolver {
mu.Lock()
defer mu.Unlock()
r.connectors[tenantId] = connector
r.once[tenantId] = &sync.Once{}
return r
}

// GetConnector returns a database connection for a specific tenant.
func (r *MultiTenantDBResolver) GetConnector(tenantId string) (*sql.DB, dbx.Dbx) {
mu.RLock()
connector, ok := r.connectors[tenantId]
once := r.once[tenantId]
mu.RUnlock()

if !ok {
mu.Lock()
defer mu.Unlock()
// Check again to avoid race condition
if connector, ok = r.connectors[tenantId]; !ok {
connector = NewPostgresConnector(*postgres.GetPostgresConfigSample()) // Fallback to default config
r.AddConnector(tenantId, connector)
}
}

// This will be executed only once for the first connection
once.Do(func() {
start := time.Now()
db, s := connector.Connect()
if !s.IsConnected {
logger.Errorf(fmt.Sprintf("Error initializing database connection for tenant %s (executed in %v): %s", tenantId, time.Since(start), s.Message), s.Error)
}
dbs[tenantId] = struct {
C *sql.DB
S dbx.Dbx
}{
C: db,
S: s,
}
})
conn, _ := dbs[tenantId]
return conn.C, conn.S
}
22 changes: 22 additions & 0 deletions db.resolver_conf.go
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
package dbresolver

import (
"database/sql"
"sync"

"github.com/sivaosorg/govm/dbx"
)

var (
dbs = make(map[string]struct {
C *sql.DB
S dbx.Dbx
})
mu sync.RWMutex
defaultConfig = dbConfig{
Host: "127.0.0.1",
Port: 5432,
User: "default-db-user",
Password: "default-db-password",
Database: "default-db-name",
}
)
39 changes: 39 additions & 0 deletions db.resolver_model.go
Original file line number Diff line number Diff line change
@@ -1 +1,40 @@
package dbresolver

import (
"database/sql"
"sync"

"github.com/sivaosorg/govm/dbx"
"github.com/sivaosorg/govm/mysql"
"github.com/sivaosorg/govm/postgres"
)

// dbConfig represents the common configuration for both Postgres and MySQL.
type dbConfig struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"-"`
Database string `json:"database"`
}

// DBConnector is an interface for creating and managing database connections.
type DBConnector interface {
Connect() (*sql.DB, dbx.Dbx)
}

// PostgresConnector implements the DBConnector interface for PostgreSQL.
type PostgresConnector struct {
Config postgres.PostgresConfig `json:"psql_conf"`
}

// MySQLConnector implements the DBConnector interface for MySQL.
type MySQLConnector struct {
Config mysql.MysqlConfig `json:"msql_conf"`
}

// MultiTenantDBResolver manages database connections for multiple tenants.
type MultiTenantDBResolver struct {
connectors map[string]DBConnector
once map[string]*sync.Once
}
44 changes: 44 additions & 0 deletions example/resolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package example

import (
dbresolver "github.com/sivaosorg/db.resolver"
"github.com/sivaosorg/govm/logger"
"github.com/sivaosorg/govm/postgres"
)

func main() {
config_1 := postgres.GetPostgresConfigSample().
SetEnabled(true).
SetDatabase("your_db").
SetPort(6666).
SetPassword("@@@@@@@@").
SetUsername("XXXX").
SetDebugMode(false)

config_2 := postgres.GetPostgresConfigSample().
SetEnabled(true).
SetDatabase("your_db").
SetPort(6666).
SetPassword("@@@@@@@@").
SetUsername("XXXX").
SetDebugMode(false)

connector1 := dbresolver.NewPostgresConnector(*config_1)
connector2 := dbresolver.NewPostgresConnector(*config_2)

resolver := dbresolver.NewMultiTenantDBResolver()

resolver.AddConnector("psql_node1", connector1).AddConnector("psql_node2", connector2)

_, s1 := resolver.GetConnector("psql_node1")

logger.Infof("Conn status node1 = %v", s1.Json())

_, s2 := resolver.GetConnector("psql_node2")

logger.Infof("Conn status node2 = %v", s2.Json())

_, s := resolver.GetConnector("psql_node1")

logger.Infof("Conn status node1 retake = %v", s.Json())
}
22 changes: 22 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
module github.com/sivaosorg/db.resolver

go 1.20

require (
github.com/sivaosorg/govm v1.2.8
github.com/sivaosorg/mysqlconn v1.0.5
github.com/sivaosorg/postgresconn v1.0.7
)

require (
github.com/fatih/color v1.16.0 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
golang.org/x/sys v0.15.0 // indirect
gopkg.in/guregu/null.v3 v3.5.0 // indirect
)
57 changes: 57 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sivaosorg/govm v1.2.8 h1:SyVwq7PNUKQQNqI9PbjZ6zY9b33lks75y/qzAzxDNyI=
github.com/sivaosorg/govm v1.2.8/go.mod h1:rXfPCNGc4ddPf1+VRX8Ytw/5xqehfPRrCr53Oi+cwpw=
github.com/sivaosorg/mysqlconn v1.0.5 h1:VHVfk6d6NQaMNsaLhojyzxF3PV+TA0I4wekIF5fU9QI=
github.com/sivaosorg/mysqlconn v1.0.5/go.mod h1:oohoHcVTYlLw/ABOYgLRF/f4F2sQjbEOLPooxGyT9O8=
github.com/sivaosorg/postgresconn v1.0.7 h1:36O2bVZdFzOqFPboyDZSp7BpZODrCRjj88KELG50ehQ=
github.com/sivaosorg/postgresconn v1.0.7/go.mod h1:FfrmwVY0FjdFzU/fZJn5dvGyNBwa4l5HrIMQxIhDrgE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/guregu/null.v3 v3.5.0 h1:xTcasT8ETfMcUHn0zTvIYtQud/9Mx5dJqD554SZct0o=
gopkg.in/guregu/null.v3 v3.5.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 7d80adb

Please sign in to comment.