Mercurial > gemma
diff auth/connection.go @ 148:0116aae1071b
Made ConnectionPool an interface and use current in-memory implementation.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 02 Jul 2018 11:00:19 +0200 |
parents | 0c56c56a1c44 |
children | 3349bfc2a047 |
line wrap: on
line diff
--- a/auth/connection.go Mon Jul 02 10:34:56 2018 +0200 +++ b/auth/connection.go Mon Jul 02 11:00:19 2018 +0200 @@ -10,7 +10,15 @@ var ErrNoSuchToken = errors.New("No such token") -var ConnPool = NewConnectionPool() +type ConnectionPool interface { + Delete(token string) bool + Add(token string, session *Session) *Connection + Renew(token string) (string, error) + Do(token string, fn func(*sql.DB) error) error + Session(token string) *Session +} + +var ConnPool ConnectionPool = NewInMemoryConnectionPool() const ( maxOpen = 16 @@ -53,197 +61,3 @@ c.db = nil } } - -type ConnectionPool struct { - conns map[string]*Connection - cmds chan func(*ConnectionPool) -} - -func NewConnectionPool() *ConnectionPool { - cp := &ConnectionPool{ - conns: make(map[string]*Connection), - cmds: make(chan func(*ConnectionPool)), - } - go cp.run() - return cp -} - -func (cp *ConnectionPool) run() { - for { - select { - case cmd := <-cp.cmds: - cmd(cp) - case <-time.After(time.Minute): - cp.cleanDB() - case <-time.After(time.Minute * 5): - cp.cleanToken() - } - } -} - -func (cp *ConnectionPool) cleanDB() { - valid := time.Now().Add(-maxDBIdle) - for _, con := range cp.conns { - if con.refCount <= 0 && con.last().Before(valid) { - con.close() - } - } -} - -func (cp *ConnectionPool) cleanToken() { - now := time.Now() - for token, con := range cp.conns { - expires := time.Unix(con.session.ExpiresAt, 0) - if expires.Before(now) { - // TODO: Be more graceful here? - con.close() - delete(cp.conns, token) - } - } -} - -func (cp *ConnectionPool) Delete(token string) bool { - res := make(chan bool) - cp.cmds <- func(cp *ConnectionPool) { - conn, found := cp.conns[token] - if !found { - res <- false - return - } - conn.close() - delete(cp.conns, token) - res <- true - } - return <-res -} - -func (cp *ConnectionPool) Add(token string, session *Session) *Connection { - res := make(chan *Connection) - - cp.cmds <- func(cp *ConnectionPool) { - con := cp.conns[token] - if con == nil { - con = &Connection{} - cp.conns[token] = con - } - con.set(session) - res <- con - } - - con := <-res - return con -} - -func (cp ConnectionPool) Renew(token string) (string, error) { - - type result struct { - newToken string - err error - } - - resCh := make(chan result) - - cp.cmds <- func(cp *ConnectionPool) { - con := cp.conns[token] - if con == nil { - resCh <- result{err: ErrNoSuchToken} - } else { - delete(cp.conns, token) - newToken := GenerateSessionKey() - // TODO: Ensure that this is not racy! - con.session.ExpiresAt = time.Now().Add(maxTokenValid).Unix() - cp.conns[newToken] = con - resCh <- result{newToken: newToken} - } - } - - r := <-resCh - return r.newToken, r.err -} - -func (cp *ConnectionPool) trim(conn *Connection) { - - conn.refCount-- - - for { - least := time.Now() - var count int - var oldest *Connection - - for _, con := range cp.conns { - if con.db != nil && con.refCount <= 0 { - if last := con.last(); last.Before(least) { - least = last - oldest = con - } - count++ - } - } - if count <= maxOpen { - break - } - oldest.close() - } -} - -func (cp *ConnectionPool) Do(token string, fn func(*sql.DB) error) error { - - type result struct { - con *Connection - err error - } - - res := make(chan result) - - cp.cmds <- func(cp *ConnectionPool) { - con := cp.conns[token] - if con == nil { - res <- result{err: ErrNoSuchToken} - return - } - con.touch() - if con.db != nil { - con.refCount++ - res <- result{con: con} - return - } - - session := con.session - db, err := opendb(session.User, session.Password) - if err != nil { - res <- result{err: err} - return - } - con.db = db - con.refCount++ - res <- result{con: con} - } - - r := <-res - - if r.err != nil { - return r.err - } - - defer func() { - cp.cmds <- func(cp *ConnectionPool) { - cp.trim(r.con) - } - }() - - return fn(r.con.db) -} - -func (cp *ConnectionPool) Session(token string) *Session { - res := make(chan *Session) - cp.cmds <- func(cp *ConnectionPool) { - con := cp.conns[token] - if con == nil { - res <- nil - } else { - con.touch() - res <- con.session - } - } - return <-res -}