github.com/kaydxh/golang@v0.0.131/pkg/pool/instance/pool.instance.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package instance 23 24 import ( 25 "context" 26 "fmt" 27 "path/filepath" 28 "sync" 29 "time" 30 31 runtime_ "github.com/kaydxh/golang/go/runtime" 32 time_ "github.com/kaydxh/golang/go/time" 33 "github.com/sirupsen/logrus" 34 ) 35 36 type GlobalInitFunc func() error 37 type GlobalReleaseFunc func() error 38 type LocalInitFunc func(instance interface{}) error 39 type LocalReleaseFunc func(instance interface{}) error 40 type NewFunc func() interface{} 41 type DeleteFunc func(instance interface{}) 42 43 type LoadBalanceMode int 44 45 const ( 46 RoundRobinBalanceMode LoadBalanceMode = 0 47 RandomLoadBalanceMode LoadBalanceMode = 1 48 LeastLoadBalanceMode LoadBalanceMode = 2 49 ) 50 51 type PoolOptions struct { 52 name string 53 resevePoolSizePerCore int64 54 capacityPoolSizePerCore int64 55 idleTimeout time.Duration 56 // the wait time to try get instance again, 0 means wait forever, 57 // execute the block without any timeout 58 waitTimeoutOnce time.Duration 59 60 // the total wait time to try get instance, 0 means wait forever, 61 // execute the block without any timeout 62 waitTimeoutTotal time.Duration 63 loadBalanceMode LoadBalanceMode 64 65 coreIDs []int64 66 67 modelPaths []string 68 batchSize int64 69 70 globalInitFunc GlobalInitFunc 71 globalReleaseFunc GlobalReleaseFunc 72 localInitFunc LocalInitFunc 73 localReleaseFunc LocalReleaseFunc 74 deleteFunc DeleteFunc 75 76 enabledPrintCostTime bool 77 } 78 79 type Pool struct { 80 newFunc NewFunc 81 82 holders map[int64]chan *CoreInstanceHolder 83 opts PoolOptions 84 mu sync.RWMutex 85 } 86 87 func defaultPoolOptions() PoolOptions { 88 return PoolOptions{ 89 resevePoolSizePerCore: 0, 90 capacityPoolSizePerCore: 1, 91 idleTimeout: 10 * time.Second, 92 waitTimeoutOnce: 10 * time.Millisecond, 93 } 94 } 95 96 func NewPool(newFunc NewFunc, opts ...PoolOption) (*Pool, error) { 97 if newFunc == nil { 98 return nil, fmt.Errorf("new func is nil") 99 100 } 101 p := &Pool{ 102 newFunc: newFunc, 103 holders: make(map[int64]chan *CoreInstanceHolder), 104 opts: defaultPoolOptions(), 105 } 106 p.ApplyOptions(opts...) 107 108 if p.opts.resevePoolSizePerCore < 0 { 109 p.opts.resevePoolSizePerCore = 0 110 } 111 112 if p.opts.capacityPoolSizePerCore < p.opts.resevePoolSizePerCore { 113 p.opts.capacityPoolSizePerCore = p.opts.resevePoolSizePerCore 114 } 115 116 return p, nil 117 } 118 119 func (p *Pool) init(ctx context.Context) error { 120 121 for _, id := range p.opts.coreIDs { 122 if id < 0 { 123 continue 124 } 125 126 p.holders[id] = make(chan *CoreInstanceHolder, p.opts.capacityPoolSizePerCore) 127 for i := int64(0); i < p.opts.resevePoolSizePerCore; i++ { 128 holder := &CoreInstanceHolder{ 129 Name: p.opts.name, 130 CoreID: id, 131 ModelPaths: p.opts.modelPaths, 132 BatchSize: p.opts.batchSize, 133 } 134 err := holder.Do(ctx, func() { 135 holder.Instance = p.newFunc() 136 }) 137 if err != nil { 138 return err 139 } 140 if holder.Instance == nil { 141 return fmt.Errorf("instance the result of new func is nil") 142 } 143 144 var processErr error 145 err = holder.Do(ctx, func() { 146 processErr = p.opts.localInitFunc(holder.Instance) 147 }) 148 149 if processErr != nil { 150 return processErr 151 } 152 if err != nil { 153 return err 154 } 155 156 p.holders[id] <- holder 157 } 158 } 159 160 return nil 161 } 162 163 func (p *Pool) GlobalInit(ctx context.Context) error { 164 err := p.globalInit(ctx) 165 if err != nil { 166 return err 167 } 168 169 return p.init(ctx) 170 } 171 172 func (p *Pool) globalInit(ctx context.Context) error { 173 if p.opts.globalInitFunc == nil { 174 return nil 175 } 176 177 return p.opts.globalInitFunc() 178 } 179 180 func (p *Pool) GlobalRelease(ctx context.Context) error { 181 for _, ch := range p.holders { 182 select { 183 case holder := <-ch: 184 holder.Do(ctx, func() { 185 if p.opts.localReleaseFunc != nil { 186 p.opts.localReleaseFunc(holder.Instance) 187 } 188 189 if p.opts.deleteFunc != nil { 190 p.opts.deleteFunc(holder.Instance) 191 } 192 }) 193 194 case <-ctx.Done(): 195 return ctx.Err() 196 197 default: 198 } 199 } 200 201 return p.opts.globalReleaseFunc() 202 } 203 204 func (p *Pool) globalRelease(ctx context.Context) error { 205 if p.opts.globalReleaseFunc == nil { 206 return nil 207 } 208 209 return p.opts.globalReleaseFunc() 210 } 211 212 func (p *Pool) GetByCoreId(ctx context.Context, coreID int64) (*CoreInstanceHolder, error) { 213 if p.opts.capacityPoolSizePerCore <= 0 { 214 return nil, fmt.Errorf("no instance, capacity pool size per core is <= 0") 215 } 216 217 if p.opts.waitTimeoutOnce == 0 { 218 select { 219 case holder := <-p.holders[coreID]: 220 logrus.Infof("get a instance in core id: %v", coreID) 221 return holder, nil 222 case <-ctx.Done(): 223 return nil, ContextCanceledError{Message: ctx.Err().Error()} 224 } 225 } 226 227 timer := time.NewTimer(p.opts.waitTimeoutOnce) 228 defer timer.Stop() 229 230 select { 231 case holder := <-p.holders[coreID]: 232 logrus.Infof("get a instance in core id: %v", coreID) 233 return holder, nil 234 case <-ctx.Done(): 235 logrus.WithError(ctx.Err()).Errorf("get a instance in core id: %v context canceled", coreID) 236 return nil, ContextCanceledError{Message: ctx.Err().Error()} 237 case <-timer.C: 238 msg := fmt.Sprintf("get instance timeout: %v, try again.", p.opts.waitTimeoutOnce) 239 logrus.Warnf(msg) 240 return nil, TimeoutError{Message: msg} 241 } 242 243 } 244 245 func (p *Pool) Get(ctx context.Context) (*CoreInstanceHolder, error) { 246 247 switch p.opts.loadBalanceMode { 248 case RoundRobinBalanceMode: 249 return p.GetWithRoundRobinMode(ctx) 250 default: 251 return nil, fmt.Errorf("not support the loadBalance mode: %v", p.opts.loadBalanceMode) 252 } 253 } 254 255 // until get instance, unless context canceled 256 func (p *Pool) GetWithRoundRobinMode(ctx context.Context) (*CoreInstanceHolder, error) { 257 258 remain := p.opts.waitTimeoutTotal 259 260 for { 261 for _, id := range p.opts.coreIDs { 262 tc := time_.New(true) 263 264 holder, err := p.GetByCoreId(ctx, id) 265 if err == nil { 266 return holder, nil 267 } 268 269 if p.opts.waitTimeoutTotal > 0 { 270 remain -= tc.Elapse() 271 if remain <= 0 { 272 msg := fmt.Sprintf("get instance total timeout: %v, remain: %v", p.opts.waitTimeoutTotal, remain) 273 logrus.Errorf(msg) 274 return nil, TimeoutError{Message: msg} 275 } 276 } 277 278 switch err.(type) { 279 case ContextCanceledError: 280 return nil, err 281 default: 282 } 283 } 284 } 285 } 286 287 func (p *Pool) Put(ctx context.Context, holder *CoreInstanceHolder) error { 288 select { 289 case p.holders[holder.CoreID] <- holder: 290 fmt.Println("put") 291 return nil 292 293 case <-ctx.Done(): 294 return ctx.Err() 295 296 default: 297 } 298 299 // if get this line, it means put instance error, then we delete this instance 300 if p.opts.localReleaseFunc != nil { 301 holder.Do(ctx, func() { 302 p.opts.localReleaseFunc(holder.Instance) 303 }) 304 305 } 306 if p.opts.deleteFunc != nil { 307 holder.Do(ctx, func() { 308 p.opts.deleteFunc(holder.Instance) 309 }) 310 } 311 312 return nil 313 } 314 315 func (p *Pool) Invoke( 316 ctx context.Context, 317 f func(ctx context.Context, instance interface{}) (interface{}, error), 318 ) (response interface{}, err error) { 319 320 holder := &CoreInstanceHolder{} 321 tc := time_.New(p.opts.enabledPrintCostTime) 322 summary := func() { 323 var coreID int64 = -1 324 if holder != nil { 325 coreID = holder.CoreID 326 } 327 tc.Tick(fmt.Sprintf("Invoke %v no coreID: %v", filepath.Base(runtime_.NameOfFunction(f)), coreID)) 328 logrus.Infof(tc.String()) 329 } 330 defer summary() 331 332 if f == nil { 333 return nil, nil 334 } 335 336 holder, err = p.Get(ctx) 337 if err != nil { 338 return nil, err 339 } 340 defer p.Put(ctx, holder) 341 342 var processErr error 343 err = holder.Do(ctx, func() { 344 response, processErr = f(ctx, holder.Instance) 345 }) 346 347 if processErr != nil { 348 err = processErr 349 } 350 351 return response, err 352 }