Mercurial > gemma
comparison auth/inmemory.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 | |
children | 3349bfc2a047 |
comparison
equal
deleted
inserted
replaced
147:5ca6f436d0b7 | 148:0116aae1071b |
---|---|
1 package auth | |
2 | |
3 import ( | |
4 "database/sql" | |
5 "time" | |
6 ) | |
7 | |
8 type InMemoryConnectionPool struct { | |
9 conns map[string]*Connection | |
10 cmds chan func(*InMemoryConnectionPool) | |
11 } | |
12 | |
13 func NewInMemoryConnectionPool() *InMemoryConnectionPool { | |
14 cp := &InMemoryConnectionPool{ | |
15 conns: make(map[string]*Connection), | |
16 cmds: make(chan func(*InMemoryConnectionPool)), | |
17 } | |
18 go cp.run() | |
19 return cp | |
20 } | |
21 | |
22 func (cp *InMemoryConnectionPool) run() { | |
23 for { | |
24 select { | |
25 case cmd := <-cp.cmds: | |
26 cmd(cp) | |
27 case <-time.After(time.Minute): | |
28 cp.cleanDB() | |
29 case <-time.After(time.Minute * 5): | |
30 cp.cleanToken() | |
31 } | |
32 } | |
33 } | |
34 | |
35 func (cp *InMemoryConnectionPool) cleanDB() { | |
36 valid := time.Now().Add(-maxDBIdle) | |
37 for _, con := range cp.conns { | |
38 if con.refCount <= 0 && con.last().Before(valid) { | |
39 con.close() | |
40 } | |
41 } | |
42 } | |
43 | |
44 func (cp *InMemoryConnectionPool) cleanToken() { | |
45 now := time.Now() | |
46 for token, con := range cp.conns { | |
47 expires := time.Unix(con.session.ExpiresAt, 0) | |
48 if expires.Before(now) { | |
49 // TODO: Be more graceful here? | |
50 con.close() | |
51 delete(cp.conns, token) | |
52 } | |
53 } | |
54 } | |
55 | |
56 func (cp *InMemoryConnectionPool) Delete(token string) bool { | |
57 res := make(chan bool) | |
58 cp.cmds <- func(cp *InMemoryConnectionPool) { | |
59 conn, found := cp.conns[token] | |
60 if !found { | |
61 res <- false | |
62 return | |
63 } | |
64 conn.close() | |
65 delete(cp.conns, token) | |
66 res <- true | |
67 } | |
68 return <-res | |
69 } | |
70 | |
71 func (cp *InMemoryConnectionPool) Add(token string, session *Session) *Connection { | |
72 res := make(chan *Connection) | |
73 | |
74 cp.cmds <- func(cp *InMemoryConnectionPool) { | |
75 con := cp.conns[token] | |
76 if con == nil { | |
77 con = &Connection{} | |
78 cp.conns[token] = con | |
79 } | |
80 con.set(session) | |
81 res <- con | |
82 } | |
83 | |
84 con := <-res | |
85 return con | |
86 } | |
87 | |
88 func (cp *InMemoryConnectionPool) Renew(token string) (string, error) { | |
89 | |
90 type result struct { | |
91 newToken string | |
92 err error | |
93 } | |
94 | |
95 resCh := make(chan result) | |
96 | |
97 cp.cmds <- func(cp *InMemoryConnectionPool) { | |
98 con := cp.conns[token] | |
99 if con == nil { | |
100 resCh <- result{err: ErrNoSuchToken} | |
101 } else { | |
102 delete(cp.conns, token) | |
103 newToken := GenerateSessionKey() | |
104 // TODO: Ensure that this is not racy! | |
105 con.session.ExpiresAt = time.Now().Add(maxTokenValid).Unix() | |
106 cp.conns[newToken] = con | |
107 resCh <- result{newToken: newToken} | |
108 } | |
109 } | |
110 | |
111 r := <-resCh | |
112 return r.newToken, r.err | |
113 } | |
114 | |
115 func (cp *InMemoryConnectionPool) trim(conn *Connection) { | |
116 | |
117 conn.refCount-- | |
118 | |
119 for { | |
120 least := time.Now() | |
121 var count int | |
122 var oldest *Connection | |
123 | |
124 for _, con := range cp.conns { | |
125 if con.db != nil && con.refCount <= 0 { | |
126 if last := con.last(); last.Before(least) { | |
127 least = last | |
128 oldest = con | |
129 } | |
130 count++ | |
131 } | |
132 } | |
133 if count <= maxOpen { | |
134 break | |
135 } | |
136 oldest.close() | |
137 } | |
138 } | |
139 | |
140 func (cp *InMemoryConnectionPool) Do(token string, fn func(*sql.DB) error) error { | |
141 | |
142 type result struct { | |
143 con *Connection | |
144 err error | |
145 } | |
146 | |
147 res := make(chan result) | |
148 | |
149 cp.cmds <- func(cp *InMemoryConnectionPool) { | |
150 con := cp.conns[token] | |
151 if con == nil { | |
152 res <- result{err: ErrNoSuchToken} | |
153 return | |
154 } | |
155 con.touch() | |
156 if con.db != nil { | |
157 con.refCount++ | |
158 res <- result{con: con} | |
159 return | |
160 } | |
161 | |
162 session := con.session | |
163 db, err := opendb(session.User, session.Password) | |
164 if err != nil { | |
165 res <- result{err: err} | |
166 return | |
167 } | |
168 con.db = db | |
169 con.refCount++ | |
170 res <- result{con: con} | |
171 } | |
172 | |
173 r := <-res | |
174 | |
175 if r.err != nil { | |
176 return r.err | |
177 } | |
178 | |
179 defer func() { | |
180 cp.cmds <- func(cp *InMemoryConnectionPool) { | |
181 cp.trim(r.con) | |
182 } | |
183 }() | |
184 | |
185 return fn(r.con.db) | |
186 } | |
187 | |
188 func (cp *InMemoryConnectionPool) Session(token string) *Session { | |
189 res := make(chan *Session) | |
190 cp.cmds <- func(cp *InMemoryConnectionPool) { | |
191 con := cp.conns[token] | |
192 if con == nil { | |
193 res <- nil | |
194 } else { | |
195 con.touch() | |
196 res <- con.session | |
197 } | |
198 } | |
199 return <-res | |
200 } |