Update Set/Remove ThresoldProfile

This commit is contained in:
TeoV
2019-03-22 15:18:29 +02:00
committed by Dan Christian Bogos
parent 6f25a4a779
commit 950907c366
9 changed files with 154 additions and 145 deletions

View File

@@ -173,20 +173,22 @@ func testV1FIdxSetThresholdProfile(t *testing.T) {
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
}
tPrfl = &engine.ThresholdProfile{
Tenant: tenant,
ID: "TEST_PROFILE1",
FilterIDs: []string{"TestFilter"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
tPrfl = &ThresholdWrapper{
ThresholdProfile: &engine.ThresholdProfile{
Tenant: tenant,
ID: "TEST_PROFILE1",
FilterIDs: []string{"TestFilter"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
},
MaxHits: 1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
ActionIDs: []string{"ACT_1", "ACT_2"},
Async: true,
},
MaxHits: 1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
ActionIDs: []string{"ACT_1", "ACT_2"},
Async: true,
}
if err := tFIdxRpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil {
t.Error(err)
@@ -196,8 +198,8 @@ func testV1FIdxSetThresholdProfile(t *testing.T) {
if err := tFIdxRpc.Call("ApierV1.GetThresholdProfile",
&utils.TenantID{Tenant: tenant, ID: "TEST_PROFILE1"}, &reply); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(tPrfl, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl, reply)
} else if !reflect.DeepEqual(tPrfl.ThresholdProfile, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl.ThresholdProfile, reply)
}
if err := tFIdxRpc.Call("ApierV1.RemoveFilterIndexes", &AttrRemFilterIndexes{
ItemType: utils.MetaThresholds, Tenant: tenant}, &result); err != nil {
@@ -270,20 +272,22 @@ func testV1FIdxSetSecondThresholdProfile(t *testing.T) {
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
}
tPrfl = &engine.ThresholdProfile{
Tenant: tenant,
ID: "TEST_PROFILE2",
FilterIDs: []string{"TestFilter2"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
tPrfl = &ThresholdWrapper{
ThresholdProfile: &engine.ThresholdProfile{
Tenant: tenant,
ID: "TEST_PROFILE2",
FilterIDs: []string{"TestFilter2"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
},
MaxHits: 1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
ActionIDs: []string{"ACT_1", "ACT_2"},
Async: true,
},
MaxHits: 1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
ActionIDs: []string{"ACT_1", "ACT_2"},
Async: true,
}
if err := tFIdxRpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil {
t.Error(err)
@@ -293,8 +297,8 @@ func testV1FIdxSetSecondThresholdProfile(t *testing.T) {
if err := tFIdxRpc.Call("ApierV1.GetThresholdProfile",
&utils.TenantID{Tenant: tenant, ID: "TEST_PROFILE2"}, &reply); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(tPrfl, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl, reply)
} else if !reflect.DeepEqual(tPrfl.ThresholdProfile, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl.ThresholdProfile, reply)
}
if err := tFIdxRpc.Call("ApierV1.RemoveFilterIndexes", &AttrRemFilterIndexes{
ItemType: utils.MetaThresholds, Tenant: tenant}, &result); err != nil {

View File

@@ -178,19 +178,21 @@ func testV1FIdxCaSetThresholdProfile(t *testing.T) {
} else if result != utils.OK {
t.Error("Unexpected reply returned", result)
}
tPrfl = &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "TEST_PROFILE1",
FilterIDs: []string{"TestFilter"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
tPrfl = &ThresholdWrapper{
ThresholdProfile: &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "TEST_PROFILE1",
FilterIDs: []string{"TestFilter"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
},
MinHits: 1,
MaxHits: -1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
Async: true,
},
MinHits: 1,
MaxHits: -1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
Async: true,
}
if err := tFIdxCaRpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil {
@@ -267,18 +269,20 @@ func testV1FIdxCaUpdateThresholdProfile(t *testing.T) {
} else if result != utils.OK {
t.Error("Unexpected reply returned", result)
}
tPrfl = &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "TEST_PROFILE1",
FilterIDs: []string{"TestFilter2"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
tPrfl = &ThresholdWrapper{
ThresholdProfile: &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "TEST_PROFILE1",
FilterIDs: []string{"TestFilter2"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
},
MaxHits: -1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
Async: true,
},
MaxHits: -1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
Async: true,
}
if err := tFIdxCaRpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil {
t.Error(err)

View File

@@ -66,11 +66,11 @@ func (apierV1 *ApierV1) SetStatQueueProfile(arg *StatQueueWrapper, reply *string
return utils.APIErrorHandler(err)
}
//handle caching for StatQueueProfile
args := engine.ArgsGetCacheItem{
argCache := engine.ArgsGetCacheItem{
CacheID: utils.CacheStatQueueProfiles,
ItemID: arg.TenantID(),
}
if err := apierV1.CallCache(GetCacheOpt(arg.Cache), args); err != nil {
if err := apierV1.CallCache(GetCacheOpt(arg.Cache), argCache); err != nil {
return utils.APIErrorHandler(err)
}
//compose metrics for StatQueue
@@ -86,11 +86,11 @@ func (apierV1 *ApierV1) SetStatQueueProfile(arg *StatQueueWrapper, reply *string
return utils.APIErrorHandler(err)
}
//handle caching for StatQueues
args = engine.ArgsGetCacheItem{
argCache = engine.ArgsGetCacheItem{
CacheID: utils.CacheStatQueues,
ItemID: arg.TenantID(),
}
if err := apierV1.CallCache(GetCacheOpt(arg.Cache), args); err != nil {
if err := apierV1.CallCache(GetCacheOpt(arg.Cache), argCache); err != nil {
return utils.APIErrorHandler(err)
}

View File

@@ -86,32 +86,69 @@ func (apierV1 *ApierV1) GetThresholdProfileIDs(tenant string, thPrfIDs *[]string
return nil
}
type ThresholdWrapper struct {
*engine.ThresholdProfile
Cache *string
}
// SetThresholdProfile alters/creates a ThresholdProfile
func (apierV1 *ApierV1) SetThresholdProfile(thp *engine.ThresholdProfile, reply *string) error {
if missing := utils.MissingStructFields(thp, []string{"Tenant", "ID"}); len(missing) != 0 {
func (apierV1 *ApierV1) SetThresholdProfile(args *ThresholdWrapper, reply *string) error {
if missing := utils.MissingStructFields(args.ThresholdProfile, []string{"Tenant", "ID"}); len(missing) != 0 {
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := apierV1.DataManager.SetThresholdProfile(thp, true); err != nil {
if err := apierV1.DataManager.SetThresholdProfile(args.ThresholdProfile, true); err != nil {
return utils.APIErrorHandler(err)
}
if err := apierV1.DataManager.SetThreshold(&engine.Threshold{Tenant: thp.Tenant, ID: thp.ID}); err != nil {
//handle caching for ThresholdProfile
argCache := engine.ArgsGetCacheItem{
CacheID: utils.CacheThresholdProfiles,
ItemID: args.TenantID(),
}
if err := apierV1.CallCache(GetCacheOpt(args.Cache), argCache); err != nil {
return utils.APIErrorHandler(err)
}
if err := apierV1.DataManager.SetThreshold(&engine.Threshold{Tenant: args.Tenant, ID: args.ID}); err != nil {
return err
}
//handle caching for Threshold
argCache = engine.ArgsGetCacheItem{
CacheID: utils.CacheThresholds,
ItemID: args.TenantID(),
}
if err := apierV1.CallCache(GetCacheOpt(args.Cache), argCache); err != nil {
return utils.APIErrorHandler(err)
}
*reply = utils.OK
return nil
}
// Remove a specific Threshold Profile
func (apierV1 *ApierV1) RemoveThresholdProfile(args *utils.TenantID, reply *string) error {
func (apierV1 *ApierV1) RemoveThresholdProfile(args *utils.TenantIDWrapper, reply *string) error {
if missing := utils.MissingStructFields(args, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
if err := apierV1.DataManager.RemoveThresholdProfile(args.Tenant, args.ID, utils.NonTransactional, true); err != nil {
return utils.APIErrorHandler(err)
}
//handle caching for ThresholdProfile
argCache := engine.ArgsGetCacheItem{
CacheID: utils.CacheThresholdProfiles,
ItemID: args.TenantID(),
}
if err := apierV1.CallCache(GetCacheOpt(args.Cache), argCache); err != nil {
return utils.APIErrorHandler(err)
}
if err := apierV1.DataManager.RemoveThreshold(args.Tenant, args.ID, utils.NonTransactional); err != nil {
return utils.APIErrorHandler(err)
}
//handle caching for Threshold
argCache = engine.ArgsGetCacheItem{
CacheID: utils.CacheThresholds,
ItemID: args.TenantID(),
}
if err := apierV1.CallCache(GetCacheOpt(args.Cache), argCache); err != nil {
return utils.APIErrorHandler(err)
}
*reply = utils.OK
return nil
}

View File

@@ -36,7 +36,7 @@ var (
tSv1CfgPath string
tSv1Cfg *config.CGRConfig
tSv1Rpc *rpc.Client
tPrfl *engine.ThresholdProfile
tPrfl *ThresholdWrapper
tSv1ConfDIR string //run tests for specific configuration
)
@@ -354,20 +354,22 @@ func testV1TSSetThresholdProfile(t *testing.T) {
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
}
tPrfl = &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "THD_Test",
FilterIDs: []string{"*string:Account:1001"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
tPrfl = &ThresholdWrapper{
ThresholdProfile: &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "THD_Test",
FilterIDs: []string{"*string:Account:1001"},
ActivationInterval: &utils.ActivationInterval{
ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC),
},
MaxHits: -1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
ActionIDs: []string{"ACT_1"},
Async: true,
},
MaxHits: -1,
MinSleep: time.Duration(5 * time.Minute),
Blocker: false,
Weight: 20.0,
ActionIDs: []string{"ACT_1"},
Async: true,
}
if err := tSv1Rpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil {
t.Error(err)
@@ -377,8 +379,8 @@ func testV1TSSetThresholdProfile(t *testing.T) {
if err := tSv1Rpc.Call("ApierV1.GetThresholdProfile",
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_Test"}, &reply); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(tPrfl, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl, reply)
} else if !reflect.DeepEqual(tPrfl.ThresholdProfile, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl.ThresholdProfile, reply)
}
}
@@ -394,15 +396,15 @@ func testV1TSUpdateThresholdProfile(t *testing.T) {
if err := tSv1Rpc.Call("ApierV1.GetThresholdProfile",
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_Test"}, &reply); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(tPrfl, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl, reply)
} else if !reflect.DeepEqual(tPrfl.ThresholdProfile, reply) {
t.Errorf("Expecting: %+v, received: %+v", tPrfl.ThresholdProfile, reply)
}
}
func testV1TSRemoveThresholdProfile(t *testing.T) {
var resp string
if err := tSv1Rpc.Call("ApierV1.RemoveThresholdProfile",
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_Test"}, &resp); err != nil {
&utils.TenantIDWrapper{Tenant: "cgrates.org", ID: "THD_Test"}, &resp); err != nil {
t.Error(err)
} else if resp != utils.OK {
t.Error("Unexpected reply returned", resp)
@@ -414,7 +416,7 @@ func testV1TSRemoveThresholdProfile(t *testing.T) {
t.Errorf("Recived %s and the error:%+v", utils.ToJSON(sqp), err)
}
if err := tSv1Rpc.Call("ApierV1.RemoveThresholdProfile",
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_Test"}, &resp); err.Error() != utils.ErrNotFound.Error() {
&utils.TenantIDWrapper{Tenant: "cgrates.org", ID: "THD_Test"}, &resp); err.Error() != utils.ErrNotFound.Error() {
t.Errorf("Expected error: %v recived: %v", utils.ErrNotFound, err)
}
}
@@ -427,10 +429,12 @@ func testV1TSMaxHits(t *testing.T) {
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
}
tPrfl = &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "TH3",
MaxHits: 3,
tPrfl = &ThresholdWrapper{
ThresholdProfile: &engine.ThresholdProfile{
Tenant: "cgrates.org",
ID: "TH3",
MaxHits: 3,
},
}
//set
if err := tSv1Rpc.Call("ApierV1.SetThresholdProfile", tPrfl, &reply); err != nil {
@@ -487,7 +491,7 @@ func testV1TSMaxHits(t *testing.T) {
if err := tSv1Rpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "TH3"}, &td); err == nil ||
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
t.Errorf("Err : %+v \n, td : %+v", err, utils.ToJSON(td))
}
}

View File

@@ -423,22 +423,12 @@ func (dm *DataManager) GetThreshold(tenant, id string,
}
func (dm *DataManager) SetThreshold(th *Threshold) (err error) {
if err = dm.DataDB().SetThresholdDrv(th); err != nil {
return
}
if err = dm.CacheDataFromDB(utils.ThresholdPrefix, []string{th.TenantID()}, true); err != nil {
return
}
return
return dm.DataDB().SetThresholdDrv(th)
}
func (dm *DataManager) RemoveThreshold(tenant, id, transactionID string) (err error) {
if err = dm.DataDB().RemoveThresholdDrv(tenant, id); err != nil {
return
}
Cache.Remove(utils.CacheThresholds, utils.ConcatenatedKey(tenant, id),
cacheCommit(transactionID), transactionID)
return
return dm.DataDB().RemoveThresholdDrv(tenant, id)
}
func (dm *DataManager) GetThresholdProfile(tenant, id string, cacheRead, cacheWrite bool,
@@ -475,10 +465,6 @@ func (dm *DataManager) SetThresholdProfile(th *ThresholdProfile, withIndex bool)
if err = dm.DataDB().SetThresholdProfileDrv(th); err != nil {
return err
}
if err = dm.CacheDataFromDB(utils.ThresholdProfilePrefix,
[]string{th.TenantID()}, true); err != nil {
return
}
if withIndex {
if oldTh != nil {
var needsRemove bool
@@ -508,8 +494,6 @@ func (dm *DataManager) RemoveThresholdProfile(tenant, id,
if err = dm.DataDB().RemThresholdProfileDrv(tenant, id); err != nil {
return
}
Cache.Remove(utils.CacheThresholdProfiles, utils.ConcatenatedKey(tenant, id),
cacheCommit(transactionID), transactionID)
if oldTh == nil {
return utils.ErrNotFound
}

View File

@@ -1601,13 +1601,6 @@ func testOnStorITThresholdProfile(t *testing.T) {
if err := onStor.SetThresholdProfile(th, true); err != nil {
t.Error(err)
}
//get from cache
if rcv, err := onStor.GetThresholdProfile(th.Tenant, th.ID,
true, false, utils.NonTransactional); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(th, rcv) {
t.Errorf("Expecting: %v, received: %v", utils.ToJSON(th), utils.ToJSON(rcv))
}
//get from database
if rcv, err := onStor.GetThresholdProfile(th.Tenant, th.ID,
false, false, utils.NonTransactional); err != nil {
@@ -1627,13 +1620,6 @@ func testOnStorITThresholdProfile(t *testing.T) {
t.Error(err)
}
time.Sleep(sleepDelay)
//get from cache
if rcv, err := onStor.GetThresholdProfile(th.Tenant, th.ID,
true, false, utils.NonTransactional); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(th, rcv) {
t.Errorf("Expecting: %v, received: %v", utils.ToJSON(th), utils.ToJSON(rcv))
}
//get from database
if rcv, err := onStor.GetThresholdProfile(th.Tenant, th.ID,
false, false, utils.NonTransactional); err != nil {
@@ -1645,11 +1631,6 @@ func testOnStorITThresholdProfile(t *testing.T) {
th.ID, utils.NonTransactional, false); err != nil {
t.Error(err)
}
//check cache if removed
if _, rcvErr := onStor.GetThresholdProfile(th.Tenant,
th.ID, true, false, utils.NonTransactional); rcvErr != utils.ErrNotFound {
t.Error(rcvErr)
}
//check database if removed
if _, rcvErr := onStor.GetThresholdProfile(th.Tenant,
th.ID, false, false, utils.NonTransactional); rcvErr != utils.ErrNotFound {
@@ -1671,13 +1652,6 @@ func testOnStorITThreshold(t *testing.T) {
if err := onStor.SetThreshold(th); err != nil {
t.Error(err)
}
//get from cache
if rcv, err := onStor.GetThreshold("cgrates.org", "TH1",
true, false, utils.NonTransactional); err != nil {
t.Error(err)
} else if !(reflect.DeepEqual(th, rcv)) {
t.Errorf("Expecting: %v, received: %v", utils.ToJSON(th), utils.ToJSON(rcv))
}
//get from database
if rcv, err := onStor.GetThreshold("cgrates.org", "TH1",
false, false, utils.NonTransactional); err != nil {
@@ -1697,13 +1671,6 @@ func testOnStorITThreshold(t *testing.T) {
t.Error(err)
}
time.Sleep(sleepDelay)
//get from cache
if rcv, err := onStor.GetThreshold("cgrates.org", "TH1",
true, false, utils.NonTransactional); err != nil {
t.Error(err)
} else if !(reflect.DeepEqual(th, rcv)) {
t.Errorf("Expecting: %v, received: %v", utils.ToJSON(th), utils.ToJSON(rcv))
}
//get from database
if rcv, err := onStor.GetThreshold("cgrates.org", "TH1",
false, false, utils.NonTransactional); err != nil {
@@ -1714,11 +1681,6 @@ func testOnStorITThreshold(t *testing.T) {
if err := onStor.RemoveThreshold(th.Tenant, th.ID, utils.NonTransactional); err != nil {
t.Error(err)
}
//check cache if removed
if _, rcvErr := onStor.GetThreshold(th.Tenant, th.ID,
true, false, utils.NonTransactional); rcvErr != utils.ErrNotFound {
t.Error(rcvErr)
}
//check database if removed
if _, rcvErr := onStor.GetThreshold(th.Tenant, th.ID,
false, false, utils.NonTransactional); rcvErr != utils.ErrNotFound {

View File

@@ -141,6 +141,13 @@ func (sS *StatService) StoreStatQueue(sq *StatQueue) (err error) {
sq.TenantID(), err.Error()))
return
}
//since we no longer handle cache in DataManager do here a manul caching
if err = sS.dm.CacheDataFromDB(utils.StatQueuePrefix, []string{sq.TenantID()}, true); err != nil {
utils.Logger.Warning(
fmt.Sprintf("<StatS> failed caching StatQueue with ID: %s, error: %s",
sq.TenantID(), err.Error()))
return
}
*sq.dirty = false
return
}

View File

@@ -303,7 +303,14 @@ func (tS *ThresholdService) processEvent(args *ArgsProcessEvent) (thresholdsIDs
if t.dirty == nil || t.Hits == t.tPrfl.MaxHits { // one time threshold
if err = tS.dm.RemoveThreshold(t.Tenant, t.ID, utils.NonTransactional); err != nil {
utils.Logger.Warning(
fmt.Sprintf("<ThresholdService> failed removing non-recurrent threshold: %s, error: %s",
fmt.Sprintf("<ThresholdService> failed removing from database non-recurrent threshold: %s, error: %s",
t.TenantID(), err.Error()))
withErrors = true
}
//since we don't handle in DataManager caching we do a manual remove here
if err = tS.dm.CacheDataFromDB(utils.ThresholdPrefix, []string{t.TenantID()}, true); err != nil {
utils.Logger.Warning(
fmt.Sprintf("<ThresholdService> failed removing from cache non-recurrent threshold: %s, error: %s",
t.TenantID(), err.Error()))
withErrors = true
}