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  }