mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
359 lines
12 KiB
Go
359 lines
12 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 v1
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/cgrates/birpc/context"
|
|
"github.com/cgrates/cgrates/engine"
|
|
"github.com/cgrates/cgrates/utils"
|
|
)
|
|
|
|
// CallCache caching the item based on cacheopt
|
|
// visible in APIerSv2
|
|
func (apierSv1 *APIerSv1) CallCache(cacheopt string, tnt, cacheID, itemID, groupID string,
|
|
filters *[]string, contexts []string, opts map[string]any) (err error) {
|
|
var reply, method string
|
|
var args any
|
|
switch utils.FirstNonEmpty(cacheopt, apierSv1.Config.GeneralCfg().DefaultCaching) {
|
|
case utils.MetaNone:
|
|
return
|
|
case utils.MetaReload:
|
|
method = utils.CacheSv1ReloadCache
|
|
var argCache map[string][]string
|
|
if argCache, err = apierSv1.composeArgsReload(tnt, cacheID, itemID, filters, contexts); err != nil {
|
|
return
|
|
}
|
|
args = utils.NewAttrReloadCacheWithOptsFromMap(argCache, tnt, opts)
|
|
case utils.MetaLoad:
|
|
method = utils.CacheSv1LoadCache
|
|
var argCache map[string][]string
|
|
if argCache, err = apierSv1.composeArgsReload(tnt, cacheID, itemID, filters, contexts); err != nil {
|
|
return
|
|
}
|
|
args = utils.NewAttrReloadCacheWithOptsFromMap(argCache, tnt, opts)
|
|
case utils.MetaRemove:
|
|
if groupID != utils.EmptyString {
|
|
method = utils.CacheSv1RemoveGroup
|
|
args = &utils.ArgsGetGroupWithAPIOpts{
|
|
Tenant: tnt,
|
|
APIOpts: opts,
|
|
ArgsGetGroup: utils.ArgsGetGroup{
|
|
CacheID: cacheID,
|
|
GroupID: groupID,
|
|
},
|
|
}
|
|
break
|
|
}
|
|
method = utils.CacheSv1RemoveItems
|
|
var argCache map[string][]string
|
|
if argCache, err = apierSv1.composeArgsReload(tnt, cacheID, itemID, filters, contexts); err != nil {
|
|
return
|
|
}
|
|
args = utils.NewAttrReloadCacheWithOptsFromMap(argCache, tnt, opts)
|
|
case utils.MetaClear:
|
|
cacheIDs := make([]string, 1, 3)
|
|
cacheIDs[0] = cacheID
|
|
// do not send a EmptyString if the item doesn't have indexes
|
|
if cIdx, has := utils.CacheInstanceToCacheIndex[cacheID]; has {
|
|
cacheIDs = append(cacheIDs, cIdx)
|
|
}
|
|
switch cacheID { // add the items to the cache reload
|
|
case utils.CacheThresholdProfiles:
|
|
cacheIDs = append(cacheIDs, utils.CacheThresholds)
|
|
case utils.CacheResourceProfiles:
|
|
cacheIDs = append(cacheIDs, utils.CacheResources)
|
|
case utils.CacheIPProfiles:
|
|
cacheIDs = append(cacheIDs, utils.CacheIPAllocations)
|
|
case utils.CacheStatQueueProfiles:
|
|
cacheIDs = append(cacheIDs, utils.CacheStatQueues)
|
|
}
|
|
method = utils.CacheSv1Clear
|
|
args = &utils.AttrCacheIDsWithAPIOpts{
|
|
Tenant: tnt,
|
|
CacheIDs: cacheIDs,
|
|
APIOpts: opts,
|
|
}
|
|
|
|
}
|
|
return apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
|
|
method, args, &reply)
|
|
}
|
|
|
|
// composeArgsReload add the ItemID to AttrReloadCache
|
|
// for a specific CacheID
|
|
func (apierSv1 *APIerSv1) composeArgsReload(tnt, cacheID, itemID string, filterIDs *[]string, contexts []string) (argCache map[string][]string, err error) {
|
|
argCache = map[string][]string{cacheID: {itemID}}
|
|
switch cacheID { // add the items to the cache reload
|
|
case utils.CacheThresholdProfiles:
|
|
argCache[utils.CacheThresholds] = []string{itemID}
|
|
case utils.CacheResourceProfiles:
|
|
argCache[utils.CacheResources] = []string{itemID}
|
|
case utils.CacheIPProfiles:
|
|
argCache[utils.CacheIPAllocations] = []string{itemID}
|
|
case utils.CacheStatQueueProfiles:
|
|
argCache[utils.CacheStatQueues] = []string{itemID}
|
|
}
|
|
if filterIDs == nil { // in case we remove a profile we do not need to reload the indexes
|
|
return
|
|
}
|
|
// populate the indexes
|
|
idxCacheID := utils.CacheInstanceToCacheIndex[cacheID]
|
|
if len(*filterIDs) == 0 { // in case we do not have any filters reload the *none filter indexes
|
|
indxID := utils.ConcatenatedKey(utils.MetaNone, utils.MetaAny, utils.MetaAny)
|
|
if cacheID != utils.CacheAttributeProfiles &&
|
|
cacheID != utils.CacheDispatcherProfiles {
|
|
argCache[idxCacheID] = []string{utils.ConcatenatedKey(tnt, indxID)}
|
|
return
|
|
}
|
|
argCache[idxCacheID] = make([]string, len(contexts))
|
|
for i, ctx := range contexts {
|
|
argCache[idxCacheID][i] = utils.ConcatenatedKey(tnt, ctx, indxID)
|
|
}
|
|
return
|
|
}
|
|
indxIDs := make([]string, 0, len(*filterIDs))
|
|
for _, id := range *filterIDs {
|
|
var fltr *engine.Filter
|
|
if fltr, err = apierSv1.DataManager.GetFilter(tnt, id, true, true, utils.NonTransactional); err != nil {
|
|
return
|
|
}
|
|
for _, flt := range fltr.Rules {
|
|
if !engine.FilterIndexTypes.Has(flt.Type) ||
|
|
engine.IsDynamicDPPath(flt.Element) {
|
|
continue
|
|
}
|
|
isDyn := strings.HasPrefix(flt.Element, utils.DynamicDataPrefix)
|
|
for _, fldVal := range flt.Values {
|
|
if engine.IsDynamicDPPath(fldVal) {
|
|
continue
|
|
}
|
|
if isDyn {
|
|
if !strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
|
|
indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, flt.Element[1:], fldVal))
|
|
}
|
|
} else if strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
|
|
indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, fldVal[1:], flt.Element))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if cacheID != utils.CacheAttributeProfiles &&
|
|
cacheID != utils.CacheDispatcherProfiles {
|
|
argCache[idxCacheID] = make([]string, len(indxIDs))
|
|
for i, indxID := range indxIDs {
|
|
argCache[idxCacheID][i] = utils.ConcatenatedKey(tnt, indxID)
|
|
}
|
|
return
|
|
}
|
|
|
|
argCache[idxCacheID] = make([]string, 0, len(indxIDs)*len(indxIDs))
|
|
for _, ctx := range contexts {
|
|
for _, indxID := range indxIDs {
|
|
argCache[idxCacheID] = append(argCache[idxCacheID], utils.ConcatenatedKey(tnt, ctx, indxID))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// callCacheForIndexes will only call CacheClear because don't have access at ItemID
|
|
func (apierSv1 *APIerSv1) callCacheForRemoveIndexes(cacheopt string, tnt, cacheID string,
|
|
itemIDs []string, opts map[string]any) (err error) {
|
|
var reply, method string
|
|
var args any = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
|
|
switch utils.FirstNonEmpty(cacheopt, apierSv1.Config.GeneralCfg().DefaultCaching) {
|
|
case utils.MetaNone:
|
|
return
|
|
case utils.MetaReload:
|
|
method = utils.CacheSv1ReloadCache
|
|
case utils.MetaLoad:
|
|
method = utils.CacheSv1LoadCache
|
|
case utils.MetaRemove:
|
|
method = utils.CacheSv1RemoveItems
|
|
case utils.MetaClear:
|
|
method = utils.CacheSv1Clear
|
|
args = &utils.AttrCacheIDsWithAPIOpts{
|
|
Tenant: tnt,
|
|
CacheIDs: []string{cacheID},
|
|
APIOpts: opts,
|
|
}
|
|
}
|
|
return apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
|
|
method, args, &reply)
|
|
}
|
|
|
|
func (apierSv1 *APIerSv1) callCacheForComputeIndexes(cacheopt, tnt string,
|
|
cacheItems map[string][]string, opts map[string]any) (err error) {
|
|
var reply, method string
|
|
var args any = utils.NewAttrReloadCacheWithOptsFromMap(cacheItems, tnt, opts)
|
|
switch utils.FirstNonEmpty(cacheopt, apierSv1.Config.GeneralCfg().DefaultCaching) {
|
|
case utils.MetaNone:
|
|
return
|
|
case utils.MetaReload:
|
|
method = utils.CacheSv1ReloadCache
|
|
case utils.MetaLoad:
|
|
method = utils.CacheSv1LoadCache
|
|
case utils.MetaRemove:
|
|
method = utils.CacheSv1RemoveItems
|
|
case utils.MetaClear:
|
|
method = utils.CacheSv1Clear
|
|
cacheIDs := make([]string, 0, len(cacheItems))
|
|
for idx := range cacheItems {
|
|
cacheIDs = append(cacheIDs, idx)
|
|
}
|
|
args = &utils.AttrCacheIDsWithAPIOpts{
|
|
Tenant: tnt,
|
|
CacheIDs: cacheIDs,
|
|
APIOpts: opts,
|
|
}
|
|
}
|
|
return apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
|
|
method, args, &reply)
|
|
}
|
|
|
|
// callCacheRevDestinations used for reverse destination, loadIDs and indexes replication
|
|
func (apierSv1 *APIerSv1) callCacheMultiple(cacheopt, tnt, cacheID string, itemIDs []string, opts map[string]any) (err error) {
|
|
if len(itemIDs) == 0 {
|
|
return
|
|
}
|
|
var reply, method string
|
|
var args any
|
|
switch utils.FirstNonEmpty(cacheopt, apierSv1.Config.GeneralCfg().DefaultCaching) {
|
|
case utils.MetaNone:
|
|
return
|
|
case utils.MetaReload:
|
|
method = utils.CacheSv1ReloadCache
|
|
args = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
|
|
case utils.MetaLoad:
|
|
method = utils.CacheSv1LoadCache
|
|
args = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
|
|
case utils.MetaRemove:
|
|
method = utils.CacheSv1RemoveItems
|
|
args = utils.NewAttrReloadCacheWithOptsFromMap(map[string][]string{cacheID: itemIDs}, tnt, opts)
|
|
case utils.MetaClear:
|
|
method = utils.CacheSv1Clear
|
|
args = &utils.AttrCacheIDsWithAPIOpts{
|
|
Tenant: tnt,
|
|
CacheIDs: []string{cacheID},
|
|
APIOpts: opts,
|
|
}
|
|
}
|
|
return apierSv1.ConnMgr.Call(context.TODO(), apierSv1.Config.ApierCfg().CachesConns,
|
|
method, args, &reply)
|
|
}
|
|
|
|
func composeCacheArgsForFilter(dm *engine.DataManager, fltr *engine.Filter, tnt, tntID string, args map[string][]string) (_ map[string][]string, err error) {
|
|
indxIDs := make([]string, 0, len(fltr.Rules))
|
|
for _, flt := range fltr.Rules {
|
|
if !engine.FilterIndexTypes.Has(flt.Type) ||
|
|
engine.IsDynamicDPPath(flt.Element) {
|
|
continue
|
|
}
|
|
isDyn := strings.HasPrefix(flt.Element, utils.DynamicDataPrefix)
|
|
for _, fldVal := range flt.Values {
|
|
if engine.IsDynamicDPPath(fldVal) {
|
|
continue
|
|
}
|
|
if isDyn {
|
|
if !strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
|
|
indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, flt.Element[1:], fldVal))
|
|
}
|
|
} else if strings.HasPrefix(fldVal, utils.DynamicDataPrefix) {
|
|
indxIDs = append(indxIDs, utils.ConcatenatedKey(flt.Type, fldVal[1:], flt.Element))
|
|
}
|
|
}
|
|
}
|
|
if len(indxIDs) == 0 { // no index
|
|
return args, nil
|
|
}
|
|
|
|
var rcvIndx map[string]utils.StringSet
|
|
if rcvIndx, err = dm.GetIndexes(utils.CacheReverseFilterIndexes, tntID,
|
|
true, true); err != nil && err != utils.ErrNotFound { // error when geting the revers
|
|
return
|
|
}
|
|
if err == utils.ErrNotFound || len(rcvIndx) == 0 { // no reverse index for this filter
|
|
return args, nil
|
|
}
|
|
|
|
for k, ids := range rcvIndx {
|
|
switch k {
|
|
default:
|
|
for _, indx := range indxIDs {
|
|
args[k] = append(args[k], utils.ConcatenatedKey(tnt, indx))
|
|
}
|
|
case utils.CacheAttributeFilterIndexes: // this is slow
|
|
for attrID := range ids {
|
|
var attr *engine.AttributeProfile
|
|
if attr, err = dm.GetAttributeProfile(tnt, attrID, true, true, utils.NonTransactional); err != nil {
|
|
return
|
|
}
|
|
for _, ctx := range attr.Contexts {
|
|
for _, indx := range indxIDs {
|
|
args[utils.CacheAttributeFilterIndexes] = append(args[utils.CacheAttributeFilterIndexes], utils.ConcatenatedKey(tnt, ctx, indx))
|
|
}
|
|
}
|
|
}
|
|
case utils.CacheDispatcherFilterIndexes: // this is slow
|
|
for attrID := range ids {
|
|
var attr *engine.DispatcherProfile
|
|
if attr, err = dm.GetDispatcherProfile(tnt, attrID, true, true, utils.NonTransactional); err != nil {
|
|
return
|
|
}
|
|
for _, ctx := range attr.Subsystems {
|
|
for _, indx := range indxIDs {
|
|
args[utils.CacheDispatcherFilterIndexes] = append(args[utils.CacheDispatcherFilterIndexes], utils.ConcatenatedKey(tnt, ctx, indx))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return args, nil
|
|
}
|
|
|
|
// callCacheForFilter will call the cache for filter
|
|
func callCacheForFilter(connMgr *engine.ConnManager, cacheConns []string, cacheopt, dftCache, tnt string,
|
|
argC map[string][]string, opts map[string]any) (err error) {
|
|
var reply, method string
|
|
var args any = utils.NewAttrReloadCacheWithOptsFromMap(argC, tnt, opts)
|
|
switch utils.FirstNonEmpty(cacheopt, dftCache) {
|
|
case utils.MetaNone:
|
|
return
|
|
case utils.MetaReload:
|
|
method = utils.CacheSv1ReloadCache
|
|
case utils.MetaLoad:
|
|
method = utils.CacheSv1LoadCache
|
|
case utils.MetaRemove:
|
|
method = utils.CacheSv1RemoveItems
|
|
case utils.MetaClear:
|
|
cacheIDs := make([]string, 0, len(argC))
|
|
for k := range argC {
|
|
cacheIDs = append(cacheIDs, k)
|
|
}
|
|
method = utils.CacheSv1Clear
|
|
args = &utils.AttrCacheIDsWithAPIOpts{
|
|
Tenant: tnt,
|
|
CacheIDs: cacheIDs,
|
|
APIOpts: opts,
|
|
}
|
|
}
|
|
return connMgr.Call(context.TODO(), cacheConns, method, args, &reply)
|
|
}
|