mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
137 lines
3.4 KiB
Go
137 lines
3.4 KiB
Go
/*
|
|
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
|
|
Copyright (C) ITsysCOM GmbH
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
package engine
|
|
|
|
import (
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/cgrates/cgrates/utils"
|
|
)
|
|
|
|
// Caps the structure that allocs requests for API
|
|
type Caps struct {
|
|
strategy string
|
|
aReqs chan struct{}
|
|
}
|
|
|
|
// NewCaps creates a new caps
|
|
func NewCaps(reqs int, strategy string) *Caps {
|
|
return &Caps{
|
|
strategy: strategy,
|
|
aReqs: make(chan struct{}, reqs),
|
|
}
|
|
}
|
|
|
|
// IsLimited returns true if the limit is not 0
|
|
func (cR *Caps) IsLimited() bool {
|
|
return cap(cR.aReqs) != 0
|
|
}
|
|
|
|
// Allocated returns the number of requests actively serviced
|
|
func (cR *Caps) Allocated() int {
|
|
return len(cR.aReqs)
|
|
}
|
|
|
|
// Allocate will reserve a channel for the API call
|
|
func (cR *Caps) Allocate() (err error) {
|
|
switch cR.strategy {
|
|
case utils.MetaBusy:
|
|
if len(cR.aReqs) == cap(cR.aReqs) {
|
|
return utils.ErrMaxConcurrentRPCExceededNoCaps
|
|
}
|
|
fallthrough
|
|
case utils.MetaQueue:
|
|
cR.aReqs <- struct{}{}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Deallocate will free a channel for the API call
|
|
func (cR *Caps) Deallocate() {
|
|
<-cR.aReqs
|
|
}
|
|
|
|
// NewCapsStats returns the stats for the caps
|
|
func NewCapsStats(sampleinterval time.Duration, caps *Caps, stopChan chan struct{}) (cs *CapsStats) {
|
|
st, _ := NewStatAverage(1, utils.MetaDynReq, nil)
|
|
cs = &CapsStats{st: st}
|
|
go cs.loop(sampleinterval, stopChan, caps)
|
|
return
|
|
}
|
|
|
|
// CapsStats stores the stats for caps
|
|
type CapsStats struct {
|
|
sync.RWMutex
|
|
st StatMetric
|
|
peak int
|
|
}
|
|
|
|
// OnEvict the function that should be called on cache eviction
|
|
func (cs *CapsStats) OnEvict(itmID string, value any) {
|
|
cs.st.RemEvent(itmID)
|
|
}
|
|
|
|
func (cs *CapsStats) loop(intr time.Duration, stopChan chan struct{}, caps *Caps) {
|
|
for {
|
|
select {
|
|
case <-stopChan:
|
|
return
|
|
case <-time.After(intr):
|
|
evID := time.Now().String()
|
|
val := caps.Allocated()
|
|
cs.addSample(evID, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (cs *CapsStats) addSample(evID string, val int) {
|
|
cs.Lock()
|
|
Cache.SetWithoutReplicate(utils.CacheCapsEvents, evID, val, nil, true, utils.NonTransactional)
|
|
cs.st.AddEvent(evID, floatDP(val))
|
|
if val > cs.peak {
|
|
cs.peak = val
|
|
}
|
|
cs.Unlock()
|
|
}
|
|
|
|
// GetPeak returns the maximum allocated caps
|
|
func (cs *CapsStats) GetPeak() (peak int) {
|
|
cs.RLock()
|
|
peak = cs.peak
|
|
cs.RUnlock()
|
|
return
|
|
}
|
|
|
|
// GetAverage returns the average allocated caps
|
|
func (cs *CapsStats) GetAverage(roundingDecimals int) (avg float64) {
|
|
cs.RLock()
|
|
avg = cs.st.GetFloat64Value(roundingDecimals)
|
|
cs.RUnlock()
|
|
return
|
|
}
|
|
|
|
// floatDP should be only used by capstats
|
|
type floatDP float64
|
|
|
|
func (f floatDP) String() string { return strconv.FormatFloat(float64(f), 'f', -1, 64) }
|
|
func (f floatDP) FieldAsInterface(fldPath []string) (any, error) { return float64(f), nil }
|
|
func (f floatDP) FieldAsString(fldPath []string) (string, error) { return f.String(), nil }
|