diff --git a/lib/bouncer/backends/v0/accept.go b/lib/bouncer/backends/v0/accept.go index 49c2866d24d90c5a8e653ca43ac5c79b41dea06c..e9ff29b4cf7b71dee6fd935d3740ce70acdd74bc 100644 --- a/lib/bouncer/backends/v0/accept.go +++ b/lib/bouncer/backends/v0/accept.go @@ -223,7 +223,7 @@ func startup1(server zap.ReadWriter, parameterStatus map[string]string) (done bo return false, nil default: err = ErrUnexpectedPacket - return false, err + return } } diff --git a/lib/gat/configs/pgbouncer/config.go b/lib/gat/configs/pgbouncer/config.go index aa464449a8a561704e0661e5e810e32d921174b1..b52d3a50df8487fe00375521de2bbb043dec19b5 100644 --- a/lib/gat/configs/pgbouncer/config.go +++ b/lib/gat/configs/pgbouncer/config.go @@ -2,14 +2,17 @@ package pgbouncer import ( "errors" + "log" "net" + "os" "strconv" "time" "pggat2/lib/gat" "pggat2/lib/gat/pools/session" "pggat2/lib/gat/pools/transaction" - "pggat2/lib/util/ini" + ini2 "pggat2/lib/util/encoding/ini" + "pggat2/lib/util/encoding/userlist" ) type PoolMode string @@ -248,19 +251,32 @@ var Default = Config{ } func Load() (Config, error) { - conf, err := ini.ReadFile("pgbouncer.ini") + conf, err := ini2.ReadFile("pgbouncer.ini") if err != nil { return Config{}, err } var c = Default - err = ini.Unmarshal(conf, &c) + err = ini2.Unmarshal(conf, &c) return c, err } func (T *Config) ListenAndServe(pooler *gat.Pooler) error { + var authFile map[string]string + if T.PgBouncer.AuthFile != "" { + file, err := os.ReadFile(T.PgBouncer.AuthFile) + if err != nil { + return err + } + + authFile, err = userlist.Unmarshal(file) + if err != nil { + return err + } + } + for name, user := range T.Users { - u := gat.NewUser("pw") // TODO(garet) passwords + u := gat.NewUser(authFile[name]) // TODO(garet) passwords pooler.AddUser(name, u) for dbname, db := range T.Databases { @@ -305,12 +321,27 @@ func (T *Config) ListenAndServe(pooler *gat.Pooler) error { // connect over unix socket // TODO(garet) } else { + var address string + if db.Port == 0 { + address = net.JoinHostPort(db.Host, "5432") + } else { + address = net.JoinHostPort(db.Host, strconv.Itoa(db.Port)) + } + + var password string + if db.Password == "" { + // lookup password + password = authFile[name] + } else { + password = db.Password + } + // connect over tcp recipe := gat.TCPRecipe{ Database: db.DBName, - Address: db.Host, + Address: address, User: name, - Password: "pw", + Password: password, MinConnections: db.MinPoolSize, MaxConnections: db.MaxDBConnections, StartupParameters: startupParameters, @@ -327,7 +358,9 @@ func (T *Config) ListenAndServe(pooler *gat.Pooler) error { } } - return pooler.ListenAndServe( - net.JoinHostPort(T.PgBouncer.ListenAddr, strconv.Itoa(T.PgBouncer.ListenPort)), - ) + listen := net.JoinHostPort(T.PgBouncer.ListenAddr, strconv.Itoa(T.PgBouncer.ListenPort)) + + log.Println("listening on", listen) + + return pooler.ListenAndServe(listen) } diff --git a/lib/gat/pool.go b/lib/gat/pool.go index d72bf5b8632e9a76a7ac8b5fc7fba79e47e8b57d..faed75e1e7e728ab30e2454aa402b8fd01791da1 100644 --- a/lib/gat/pool.go +++ b/lib/gat/pool.go @@ -57,29 +57,31 @@ func NewPool(raw RawPool, idleTimeout time.Duration) *Pool { } }() - go func() { - for { - var wait time.Duration + if idleTimeout != 0 { + go func() { + for { + var wait time.Duration + + now := time.Now() + idle := pool.IdleSince() + for now.Sub(idle) > idleTimeout { + if idle == (time.Time{}) { + break + } + pool.ScaleDown(1) + idle = pool.IdleSince() + } - now := time.Now() - idle := pool.IdleSince() - for now.Sub(idle) > idleTimeout { if idle == (time.Time{}) { - break + wait = idleTimeout + } else { + wait = now.Sub(idle.Add(idleTimeout)) } - pool.ScaleDown(1) - idle = pool.IdleSince() - } - if idle == (time.Time{}) { - wait = idleTimeout - } else { - wait = now.Sub(idle.Add(idleTimeout)) + time.Sleep(wait) } - - time.Sleep(wait) - } - }() + }() + } return pool } diff --git a/lib/util/ini/readfile.go b/lib/util/encoding/ini/readfile.go similarity index 100% rename from lib/util/ini/readfile.go rename to lib/util/encoding/ini/readfile.go diff --git a/lib/util/ini/unmarshal.go b/lib/util/encoding/ini/unmarshal.go similarity index 100% rename from lib/util/ini/unmarshal.go rename to lib/util/encoding/ini/unmarshal.go diff --git a/lib/util/ini/unmarshal_test.go b/lib/util/encoding/ini/unmarshal_test.go similarity index 100% rename from lib/util/ini/unmarshal_test.go rename to lib/util/encoding/ini/unmarshal_test.go diff --git a/lib/util/encoding/userlist/unmarshal.go b/lib/util/encoding/userlist/unmarshal.go new file mode 100644 index 0000000000000000000000000000000000000000..94444d2be6a497be824a474ecbd23f44bfb6d8b6 --- /dev/null +++ b/lib/util/encoding/userlist/unmarshal.go @@ -0,0 +1,42 @@ +package userlist + +import ( + "bytes" + "errors" + "strconv" +) + +func Unmarshal(data []byte) (map[string]string, error) { + var res = make(map[string]string) + + var line []byte + for { + line, data, _ = bytes.Cut(data, []byte{'\n'}) + line = bytes.TrimSpace(line) + if len(line) == 0 { + if len(data) == 0 { + break + } + + continue + } + + fields := bytes.Fields(line) + if len(fields) < 2 { + return nil, errors.New("expected \"key\" \"value\"") + } + + key, err := strconv.Unquote(string(fields[0])) + if err != nil { + return nil, err + } + value, err := strconv.Unquote(string(fields[1])) + if err != nil { + return nil, err + } + + res[key] = value + } + + return res, nil +} diff --git a/pgbouncer.ini b/pgbouncer.ini new file mode 100644 index 0000000000000000000000000000000000000000..5ce00217d8bb9e16f5c1024424ed8b24cb1dfd4c --- /dev/null +++ b/pgbouncer.ini @@ -0,0 +1,10 @@ +[pgbouncer] +mode = transaction +auth_file = userlist.txt + +[users] +postgres = + +[databases] +uniswap = host=localhost +postgres = host=localhost diff --git a/userlist.txt b/userlist.txt new file mode 100644 index 0000000000000000000000000000000000000000..7ceb0c53575ddb050eb244e225b2544f137c2eaf --- /dev/null +++ b/userlist.txt @@ -0,0 +1 @@ +"postgres" "password"