|  | package tinylb | 
|  |  | 
|  | import ( | 
|  | "net" | 
|  | "sort" | 
|  | "sync" | 
|  | ) | 
|  |  | 
|  | // connectionPool maintains information about open connections to backends, and | 
|  | // allows for closing either arbitrary connections (by ID) or all connections to | 
|  | // a given backend. | 
|  | // | 
|  | // This structure exists to allow tinylb to kill all connections of a backend | 
|  | // that has just been removed from the BackendSet. | 
|  | // | 
|  | // Any time a connection is inserted into the pool, a unique ID for that | 
|  | // connection is returned. | 
|  | // | 
|  | // Backends are identified by 'target name' which is an opaque string. | 
|  | // | 
|  | // This structure is likely the performance bottleneck of the implementation, as | 
|  | // it takes a non-RW lock for every incoming connection. | 
|  | type connectionPool struct { | 
|  | mu sync.Mutex | 
|  | // detailsById maps connection ids to details about that connection. | 
|  | detailsById map[int64]*connectionDetails | 
|  | // idsByTarget maps a target name to all connection IDs that opened to it. | 
|  | idsByTarget map[string][]int64 | 
|  |  | 
|  | // cid is the connection id counter, increased any time a connection ID is | 
|  | // allocated. | 
|  | cid int64 | 
|  | } | 
|  |  | 
|  | // connectionDetails for each open connection. These are held in | 
|  | // connectionPool.details | 
|  | type connectionDetails struct { | 
|  | // conn is the active net.Conn backing this connection. | 
|  | conn net.Conn | 
|  | // target is the target name to which this connection was initiated. | 
|  | target string | 
|  | } | 
|  |  | 
|  | func newConnectionPool() *connectionPool { | 
|  | return &connectionPool{ | 
|  | detailsById: make(map[int64]*connectionDetails), | 
|  | idsByTarget: make(map[string][]int64), | 
|  | } | 
|  | } | 
|  |  | 
|  | // Insert a connection that's handled by the given target name, and return the | 
|  | // connection ID used to remove this connection later. | 
|  | func (c *connectionPool) Insert(target string, conn net.Conn) int64 { | 
|  | c.mu.Lock() | 
|  | defer c.mu.Unlock() | 
|  |  | 
|  | id := c.cid | 
|  | c.cid++ | 
|  |  | 
|  | c.detailsById[id] = &connectionDetails{ | 
|  | conn:   conn, | 
|  | target: target, | 
|  | } | 
|  | c.idsByTarget[target] = append(c.idsByTarget[target], id) | 
|  | return id | 
|  | } | 
|  |  | 
|  | // CloseConn closes the underlying connection for the given connection ID, and | 
|  | // removes that connection ID from internal tracking. | 
|  | func (c *connectionPool) CloseConn(id int64) { | 
|  | c.mu.Lock() | 
|  | defer c.mu.Unlock() | 
|  |  | 
|  | cd, ok := c.detailsById[id] | 
|  | if !ok { | 
|  | return | 
|  | } | 
|  |  | 
|  | ids := c.idsByTarget[cd.target] | 
|  | // ids is technically sorted because 'id' is always monotonically increasing, so | 
|  | // we could be smarter and do a binary search here. | 
|  | ix := -1 | 
|  | for i, id2 := range ids { | 
|  | if id2 == id { | 
|  | ix = i | 
|  | break | 
|  | } | 
|  | } | 
|  | if ix == -1 { | 
|  | panic("Programming error: connection present in detailsById but not in idsByTarget") | 
|  | } | 
|  | c.idsByTarget[cd.target] = append(ids[:ix], ids[ix+1:]...) | 
|  | cd.conn.Close() | 
|  | delete(c.detailsById, id) | 
|  | } | 
|  |  | 
|  | // CloseTarget closes all connections to a given backend target name, and removes | 
|  | // them from internal tracking. | 
|  | func (c *connectionPool) CloseTarget(target string) { | 
|  | c.mu.Lock() | 
|  | defer c.mu.Unlock() | 
|  |  | 
|  | for _, id := range c.idsByTarget[target] { | 
|  | c.detailsById[id].conn.Close() | 
|  | delete(c.detailsById, id) | 
|  | } | 
|  | delete(c.idsByTarget, target) | 
|  | } | 
|  |  | 
|  | // Targets removes all currently active backend target names. | 
|  | func (c *connectionPool) Targets() []string { | 
|  | c.mu.Lock() | 
|  | defer c.mu.Unlock() | 
|  |  | 
|  | res := make([]string, 0, len(c.idsByTarget)) | 
|  | for target, _ := range c.idsByTarget { | 
|  | res = append(res, target) | 
|  | } | 
|  | sort.Strings(res) | 
|  | return res | 
|  | } |