github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/gate/client/wsp/pool.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package wsp 20 21 import ( 22 "context" 23 "sync" 24 "time" 25 26 "github.com/google/uuid" 27 28 "github.com/e154/smart-home/adaptors" 29 "github.com/e154/smart-home/api" 30 m "github.com/e154/smart-home/models" 31 "github.com/e154/smart-home/system/jwt_manager" 32 "github.com/e154/smart-home/system/stream" 33 ) 34 35 // Pool manage a pool of connection to a remote Server 36 type Pool struct { 37 client *Client 38 target string 39 secretKey string 40 41 connections sync.Map 42 lock sync.RWMutex 43 44 done chan struct{} 45 46 api *api.Api 47 stream *stream.Stream 48 status Status 49 50 adaptors *adaptors.Adaptors 51 jwtManager jwt_manager.JwtManager 52 } 53 54 // NewPool creates a new Pool 55 func NewPool(client *Client, target string, 56 secretKey string, 57 api *api.Api, 58 stream *stream.Stream, 59 adaptors *adaptors.Adaptors, 60 jwtManager jwt_manager.JwtManager) *Pool { 61 return &Pool{ 62 client: client, 63 target: target, 64 connections: sync.Map{}, 65 secretKey: secretKey, 66 done: make(chan struct{}), 67 api: api, 68 stream: stream, 69 adaptors: adaptors, 70 jwtManager: jwtManager, 71 } 72 } 73 74 // Start connect to the remote Server 75 func (p *Pool) Start(ctx context.Context) { 76 //log.Info("Start") 77 p.connector(ctx) 78 go func() { 79 ticker := time.NewTicker(time.Second) 80 defer ticker.Stop() 81 82 for { 83 select { 84 case <-p.done: 85 return 86 case <-ticker.C: 87 p.connector(ctx) 88 } 89 } 90 }() 91 } 92 93 // Shutdown close all connection in the pool 94 func (p *Pool) Shutdown() { 95 //log.Info("Shutdown") 96 close(p.done) 97 p.connections.Range(func(key, value interface{}) bool { 98 connection := value.(*Connection) 99 connection.Close() 100 p.connections.Delete(key) 101 return true 102 }) 103 } 104 105 // The garbage collector 106 func (p *Pool) connector(ctx context.Context) { 107 p.lock.Lock() 108 defer p.lock.Unlock() 109 110 poolSize := p.Size() 111 p.status = Status{ 112 Connecting: poolSize.connecting, 113 Idle: poolSize.idle, 114 Running: poolSize.running, 115 Total: poolSize.total, 116 } 117 118 // Create enough connection to fill the pool 119 toCreate := p.client.cfg.PoolIdleSize - poolSize.idle 120 121 if toCreate < 0 { 122 toCreate = 0 123 } 124 125 // Create only one connection if the pool is empty 126 if poolSize.total == 0 { 127 toCreate = 1 128 } 129 130 // Ensure to open at most PoolMaxSize connections 131 if poolSize.total+toCreate >= p.client.cfg.PoolMaxSize { 132 toCreate = 0 133 } 134 135 if toCreate < 0 { 136 toCreate = 0 137 } 138 139 if toCreate == 0 { 140 return 141 } 142 143 // Try to reach ideal p size 144 for i := 0; i < toCreate; i++ { 145 connection := NewConnection(p, p.api, p.stream) 146 id := uuid.NewString() 147 p.connections.Store(id, connection) 148 149 go func() { 150 err := connection.Connect(ctx) 151 if err != nil { 152 //log.Errorf("Unable to connect to %s : %s", p.target, err) 153 } 154 p.connections.Delete(id) 155 }() 156 } 157 } 158 159 // Size return the current state of the pool 160 func (p *Pool) Size() (poolSize *PoolSize) { 161 poolSize = &PoolSize{} 162 p.connections.Range(func(key, value interface{}) bool { 163 poolSize.total++ 164 connection := value.(*Connection) 165 switch connection.status { 166 case CONNECTING: 167 poolSize.connecting++ 168 case IDLE: 169 poolSize.idle++ 170 case RUNNING: 171 poolSize.running++ 172 } 173 return true 174 }) 175 return 176 } 177 178 func (p *Pool) GetUser(accessToken string) (user *m.User, err error) { 179 180 claims, err := p.jwtManager.Verify(accessToken) 181 if err != nil { 182 return 183 } 184 185 user, err = p.adaptors.User.GetById(context.Background(), claims.UserId) 186 if err != nil { 187 return 188 } 189 190 return 191 }