More robust and straight forward pool abstraction
This commit is contained in:
parent
737a24befb
commit
33b922f181
106
rdb.go
106
rdb.go
|
@ -4,13 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.bit5.ru/backend/colog"
|
"git.bit5.ru/backend/colog"
|
||||||
"git.bit5.ru/backend/redigo/redis"
|
"git.bit5.ru/backend/redigo/redis"
|
||||||
"git.bit5.ru/backend/redsync"
|
"git.bit5.ru/backend/redsync"
|
||||||
"git.bit5.ru/backend/res_tracker"
|
"git.bit5.ru/backend/res_tracker"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,25 +18,53 @@ const (
|
||||||
ExpireRDMutexSec = 30
|
ExpireRDMutexSec = 30
|
||||||
)
|
)
|
||||||
|
|
||||||
var pools sync.Map
|
type Settings struct {
|
||||||
|
|
||||||
type RdSettings struct {
|
|
||||||
Host, Prefix string
|
Host, Prefix string
|
||||||
Port int
|
//will be shown in CLIENT LIST
|
||||||
Db int
|
ClientName string
|
||||||
LogLevel int
|
Port int
|
||||||
|
Db int
|
||||||
|
LogLevel int
|
||||||
|
MaxIdle int
|
||||||
|
IdleTimeoutSec int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RdSettings) ConnStr() string {
|
func (s *Settings) ConnStr() string {
|
||||||
return s.Host + ":" + strconv.Itoa(s.Port)
|
return s.Host + ":" + strconv.Itoa(s.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Pool struct {
|
||||||
|
S Settings
|
||||||
|
RP *redis.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenPool(s Settings, logger *colog.CoLog) *Pool {
|
||||||
|
p := &Pool{S: s, RP: newRedisPool(s, logger)}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) Close() {
|
||||||
|
p.RP.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pool) Get() redis.Conn {
|
||||||
|
conn := p.RP.Get()
|
||||||
|
|
||||||
|
if res_tracker.IsOn() {
|
||||||
|
//let's wrap our connection with the special tracked wrapper
|
||||||
|
conn = &tracked{conn}
|
||||||
|
res_tracker.Track(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: redis connection logs all operations
|
// NOTE: redis connection logs all operations
|
||||||
type rdb struct {
|
type rdb struct {
|
||||||
orig redis.Conn
|
orig redis.Conn
|
||||||
name string
|
name string
|
||||||
logger *colog.CoLog
|
logger *colog.CoLog
|
||||||
s RdSettings
|
s Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *rdb) Close() error {
|
func (rd *rdb) Close() error {
|
||||||
|
@ -93,28 +121,22 @@ func (rd *rdb) Receive() (interface{}, error) {
|
||||||
return rd.orig.Receive()
|
return rd.orig.Receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
func redisPool(logger *colog.CoLog, s RdSettings, clientName string) *redis.Pool {
|
func newRedisPool(s Settings, logger *colog.CoLog) *redis.Pool {
|
||||||
//TODO: why not simply using s as a key for pools.Load(..)?
|
|
||||||
key := s.Host + ":" + strconv.Itoa(s.Port) + ":" + strconv.Itoa(s.Db) + ":" + s.Prefix
|
maxIdle := s.MaxIdle
|
||||||
v, ok := pools.Load(key)
|
if maxIdle == 0 {
|
||||||
if ok {
|
maxIdle = 16
|
||||||
return v.(*redis.Pool)
|
|
||||||
} else {
|
|
||||||
rdpool := NewRedisPool(logger, s, clientName)
|
|
||||||
pools.Store(key, rdpool)
|
|
||||||
return rdpool
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func NewRedisPool(logger *colog.CoLog, s RdSettings, clientName string) *redis.Pool {
|
idleTimeoutSec := s.IdleTimeoutSec
|
||||||
return NewRedisPoolEx(logger, s, 16, 240*time.Second, clientName)
|
if idleTimeoutSec == 0 {
|
||||||
}
|
idleTimeoutSec = 240
|
||||||
|
}
|
||||||
|
|
||||||
func NewRedisPoolEx(logger *colog.CoLog, s RdSettings, maxIdle int, idleTimeout time.Duration, clientName string) *redis.Pool {
|
|
||||||
return &redis.Pool{
|
return &redis.Pool{
|
||||||
Wait: false,
|
Wait: false,
|
||||||
MaxIdle: maxIdle,
|
MaxIdle: maxIdle,
|
||||||
IdleTimeout: idleTimeout,
|
IdleTimeout: time.Second * time.Duration(idleTimeoutSec),
|
||||||
Dial: func() (redis.Conn, error) {
|
Dial: func() (redis.Conn, error) {
|
||||||
orig, err := redis.Dial("tcp", s.ConnStr())
|
orig, err := redis.Dial("tcp", s.ConnStr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -129,8 +151,8 @@ func NewRedisPoolEx(logger *colog.CoLog, s RdSettings, maxIdle int, idleTimeout
|
||||||
|
|
||||||
// Сохраняем название клиента для диагностики соединений с помощью команды CLIENT LIST.
|
// Сохраняем название клиента для диагностики соединений с помощью команды CLIENT LIST.
|
||||||
// https://redis.io/commands/client-list/
|
// https://redis.io/commands/client-list/
|
||||||
if len(clientName) > 0 {
|
if len(s.ClientName) > 0 {
|
||||||
_, err = c.Do("CLIENT", "SETNAME", clientName)
|
_, err = c.Do("CLIENT", "SETNAME", s.ClientName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -150,37 +172,27 @@ func NewRedisPoolEx(logger *colog.CoLog, s RdSettings, maxIdle int, idleTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRedisConn(logger *colog.CoLog, s RdSettings, clientName string) redis.Conn {
|
func GetRedisMutex(pool *Pool, name string, ttlSec int) *redsync.Mutex {
|
||||||
conn := redisPool(logger, s, clientName).Get()
|
mx, _ := redsync.NewMutexWithPool(name, []*redis.Pool{pool.RP})
|
||||||
|
|
||||||
if res_tracker.IsOn() {
|
|
||||||
//let's wrap our connection with the special tracked wrapper
|
|
||||||
conn = tracked{conn}
|
|
||||||
res_tracker.Track(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetRedisMutex(logger *colog.CoLog, s RdSettings, name string, ttlSec int) *redsync.Mutex {
|
|
||||||
mx, _ := redsync.NewMutexWithPool(name, []*redis.Pool{redisPool(logger, s, "")})
|
|
||||||
mx.Expiry = time.Duration(ttlSec) * time.Second
|
mx.Expiry = time.Duration(ttlSec) * time.Second
|
||||||
return mx
|
return mx
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRedisMutexAutoTTL(logger *colog.CoLog, s RdSettings, name string) *redsync.Mutex {
|
func GetRedisMutexAutoTTL(pool *Pool, name string) *redsync.Mutex {
|
||||||
return GetRedisMutex(logger, s, name, ExpireRDMutexSec)
|
return GetRedisMutex(pool, name, ExpireRDMutexSec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveRedisMutex(logger *colog.CoLog, s RdSettings, name string) error {
|
func RemoveRedisMutex(pool *Pool, name string) error {
|
||||||
rd := redisPool(logger, s, "").Get()
|
rd := pool.Get()
|
||||||
|
defer rd.Close()
|
||||||
|
|
||||||
_, err := rd.Do("DEL", name)
|
_, err := rd.Do("DEL", name)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExistsRedisMutex(logger *colog.CoLog, s RdSettings, name string) (bool, error) {
|
func ExistsRedisMutex(pool *Pool, name string) (bool, error) {
|
||||||
rd := redisPool(logger, s, "").Get()
|
rd := pool.Get()
|
||||||
|
defer rd.Close()
|
||||||
|
|
||||||
exists, err := redis.Bool(rd.Do("EXISTS", name))
|
exists, err := redis.Bool(rd.Do("EXISTS", name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
20
rdb_test.go
20
rdb_test.go
|
@ -6,12 +6,11 @@ import (
|
||||||
|
|
||||||
"git.bit5.ru/backend/colog"
|
"git.bit5.ru/backend/colog"
|
||||||
"git.bit5.ru/backend/rdb"
|
"git.bit5.ru/backend/rdb"
|
||||||
"git.bit5.ru/backend/redigo/redis"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPool() *redis.Pool {
|
func getPool() *rdb.Pool {
|
||||||
pool := rdb.NewRedisPool(getLogger(), rdb.RdSettings{Host: "localhost", Port: 6379, Db: 10}, "test")
|
pool := rdb.OpenPool(rdb.Settings{Host: "localhost", Port: 6379, Db: 10, ClientName: "test"}, getLogger())
|
||||||
return pool
|
return pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ func getLogger() *colog.CoLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBadConnection(t *testing.T) {
|
func TestBadConnection(t *testing.T) {
|
||||||
pool := rdb.NewRedisPool(getLogger(), rdb.RdSettings{Host: "dummy", Port: 80}, "test")
|
pool := rdb.OpenPool(rdb.Settings{Host: "dummy", Port: 80, ClientName: "test"}, getLogger())
|
||||||
conn := pool.Get()
|
conn := pool.Get()
|
||||||
assert.NotNil(t, conn)
|
assert.NotNil(t, conn)
|
||||||
_, err := conn.Do("PING")
|
_, err := conn.Do("PING")
|
||||||
|
@ -33,6 +32,7 @@ func TestBadConnection(t *testing.T) {
|
||||||
|
|
||||||
func TestGetConn(t *testing.T) {
|
func TestGetConn(t *testing.T) {
|
||||||
pool := getPool()
|
pool := getPool()
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
conn := pool.Get()
|
conn := pool.Get()
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
@ -42,9 +42,10 @@ func TestGetConn(t *testing.T) {
|
||||||
|
|
||||||
func TestCloseConn(t *testing.T) {
|
func TestCloseConn(t *testing.T) {
|
||||||
pool := getPool()
|
pool := getPool()
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
conn := pool.Get()
|
conn := pool.Get()
|
||||||
assert.EqualValues(t, 0, pool.IdleCount())
|
assert.EqualValues(t, 0, pool.RP.IdleCount())
|
||||||
|
|
||||||
_, err := conn.Do("PING")
|
_, err := conn.Do("PING")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
@ -52,19 +53,20 @@ func TestCloseConn(t *testing.T) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
_, err = conn.Do("PING")
|
_, err = conn.Do("PING")
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.EqualValues(t, 1, pool.IdleCount())
|
assert.EqualValues(t, 1, pool.RP.IdleCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoubleCloseConnIsOk(t *testing.T) {
|
func TestDoubleCloseConnIsOk(t *testing.T) {
|
||||||
pool := getPool()
|
pool := getPool()
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
conn := pool.Get()
|
conn := pool.Get()
|
||||||
_, err := conn.Do("PING")
|
_, err := conn.Do("PING")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.EqualValues(t, 0, pool.IdleCount())
|
assert.EqualValues(t, 0, pool.RP.IdleCount())
|
||||||
conn.Close()
|
conn.Close()
|
||||||
assert.EqualValues(t, 1, pool.IdleCount())
|
assert.EqualValues(t, 1, pool.RP.IdleCount())
|
||||||
conn.Close()
|
conn.Close()
|
||||||
assert.EqualValues(t, 1, pool.IdleCount())
|
assert.EqualValues(t, 1, pool.RP.IdleCount())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue