mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
refactor matched resources sorting
This commit is contained in:
committed by
Dan Christian Bogos
parent
37de0c3bd1
commit
3f3605b349
@@ -19,9 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package engine
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -126,7 +127,6 @@ type Resource struct {
|
||||
tUsage *float64 // sum of all usages
|
||||
dirty *bool // the usages were modified, needs save, *bool so we only save if enabled in config
|
||||
rPrf *ResourceProfile // for ordering purposes
|
||||
weight float64
|
||||
}
|
||||
|
||||
// resourceLockKey returns the ID used to lock a resource with guardian
|
||||
@@ -266,14 +266,9 @@ func (r *Resource) clearUsage(ruID string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Resources is an orderable list of Resources based on Weight
|
||||
// Resources is a collection of Resource objects.
|
||||
type Resources []*Resource
|
||||
|
||||
// Sort sorts based on Weight
|
||||
func (rs Resources) Sort() {
|
||||
sort.Slice(rs, func(i, j int) bool { return rs[i].weight > rs[j].weight })
|
||||
}
|
||||
|
||||
// unlock will unlock resources part of this slice
|
||||
func (rs Resources) unlock() {
|
||||
for _, r := range rs {
|
||||
@@ -592,6 +587,7 @@ func (rS *ResourceS) matchingResourcesForEvent(ctx *context.Context, tnt string,
|
||||
}
|
||||
}
|
||||
rs = make(Resources, 0, len(rIDs))
|
||||
weights := make(map[string]float64) // stores sorting weights by resource ID
|
||||
for resName := range rIDs {
|
||||
lkPrflID := guardian.Guardian.GuardIDs("",
|
||||
config.CgrConfig().GeneralCfg().LockingTimeout,
|
||||
@@ -639,17 +635,23 @@ func (rS *ResourceS) matchingResourcesForEvent(ctx *context.Context, tnt string,
|
||||
r.ttl = utils.DurationPointer(rPrf.UsageTTL)
|
||||
}
|
||||
r.rPrf = rPrf
|
||||
if r.weight, err = WeightFromDynamics(ctx, rPrf.Weights,
|
||||
rS.fltrS, tnt, evNm); err != nil {
|
||||
return
|
||||
weight, err := WeightFromDynamics(ctx, rPrf.Weights, rS.fltrS, tnt, evNm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
weights[r.ID] = weight
|
||||
rs = append(rs, r)
|
||||
}
|
||||
|
||||
if len(rs) == 0 {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
rs.Sort()
|
||||
|
||||
// Sort by weight (higher values first).
|
||||
slices.SortFunc(rs, func(a, b *Resource) int {
|
||||
return cmp.Compare(weights[b.ID], weights[a.ID])
|
||||
})
|
||||
|
||||
for i, r := range rs {
|
||||
if r.rPrf.Blocker && i != len(rs)-1 { // blocker will stop processing and we are not at last index
|
||||
Resources(rs[i+1:]).unlock()
|
||||
|
||||
@@ -382,24 +382,21 @@ func TestResourceRemoveExpiredUnits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
func TestResourceUsedUnits(t *testing.T) {
|
||||
var r1 *Resource
|
||||
var ru1 *ResourceUsage
|
||||
var ru2 *ResourceUsage
|
||||
ru1 = &ResourceUsage{
|
||||
ru1 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU1",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 1,
|
||||
}
|
||||
|
||||
ru2 = &ResourceUsage{
|
||||
ru2 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU2",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 2,
|
||||
}
|
||||
|
||||
r1 = &Resource{
|
||||
r1 := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL1",
|
||||
rPrf: &ResourceProfile{
|
||||
@@ -445,114 +442,22 @@ func TestResourceUsedUnits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceSort(t *testing.T) {
|
||||
var r1 *Resource
|
||||
var r2 *Resource
|
||||
var ru1 *ResourceUsage
|
||||
var ru2 *ResourceUsage
|
||||
var rs Resources
|
||||
ru1 = &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU1",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 1,
|
||||
}
|
||||
|
||||
ru2 = &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU2",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 2,
|
||||
}
|
||||
|
||||
r1 = &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL1",
|
||||
rPrf: &ResourceProfile{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL1",
|
||||
FilterIDs: []string{"FLTR_RES_RL1", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 100,
|
||||
}},
|
||||
Limit: 2,
|
||||
ThresholdIDs: []string{"TEST_ACTIONS"},
|
||||
|
||||
UsageTTL: time.Millisecond,
|
||||
AllocationMessage: "ALLOC",
|
||||
},
|
||||
Usages: map[string]*ResourceUsage{
|
||||
ru1.ID: ru1,
|
||||
},
|
||||
TTLIdx: []string{ru1.ID},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 100,
|
||||
}
|
||||
|
||||
if err := r1.recordUsage(ru2); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if err := r1.recordUsage(ru1); err == nil {
|
||||
t.Error("duplicate ResourceUsage id should not be allowed")
|
||||
}
|
||||
if _, found := r1.Usages[ru2.ID]; !found {
|
||||
t.Error("ResourceUsage was not recorded")
|
||||
}
|
||||
if *r1.tUsage != 4 {
|
||||
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
|
||||
}
|
||||
}
|
||||
r2 = &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL2",
|
||||
rPrf: &ResourceProfile{
|
||||
ID: "RL2",
|
||||
FilterIDs: []string{"FLTR_RES_RL2", "*ai:~*req.AnswerTime:2014-07-03T13:43:00Z|2014-07-03T13:44:00Z"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 50,
|
||||
}},
|
||||
Limit: 2,
|
||||
ThresholdIDs: []string{"TEST_ACTIONS"},
|
||||
UsageTTL: time.Millisecond,
|
||||
},
|
||||
// AllocationMessage: "ALLOC2",
|
||||
Usages: map[string]*ResourceUsage{
|
||||
ru2.ID: ru2,
|
||||
},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 50,
|
||||
}
|
||||
|
||||
rs = Resources{r2, r1}
|
||||
rs.Sort()
|
||||
|
||||
if rs[0].rPrf.ID != "RL1" {
|
||||
t.Error("Sort failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceClearUsage(t *testing.T) {
|
||||
var r1 *Resource
|
||||
var r2 *Resource
|
||||
var ru1 *ResourceUsage
|
||||
var ru2 *ResourceUsage
|
||||
ru1 = &ResourceUsage{
|
||||
ru1 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU1",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 1,
|
||||
}
|
||||
|
||||
ru2 = &ResourceUsage{
|
||||
ru2 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU2",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 2,
|
||||
}
|
||||
|
||||
r1 = &Resource{
|
||||
r1 := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL1",
|
||||
rPrf: &ResourceProfile{
|
||||
@@ -574,7 +479,6 @@ func TestResourceClearUsage(t *testing.T) {
|
||||
},
|
||||
TTLIdx: []string{ru1.ID},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 100,
|
||||
}
|
||||
|
||||
if err := r1.recordUsage(ru2); err != nil {
|
||||
@@ -590,7 +494,7 @@ func TestResourceClearUsage(t *testing.T) {
|
||||
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
|
||||
}
|
||||
}
|
||||
r2 = &Resource{
|
||||
r2 := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL2",
|
||||
rPrf: &ResourceProfile{
|
||||
@@ -609,15 +513,8 @@ func TestResourceClearUsage(t *testing.T) {
|
||||
ru2.ID: ru2,
|
||||
},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 50,
|
||||
}
|
||||
|
||||
rs := Resources{r2, r1}
|
||||
rs.Sort()
|
||||
|
||||
if rs[0].rPrf.ID != "RL1" {
|
||||
t.Error("Sort failed")
|
||||
}
|
||||
r1.Usages = map[string]*ResourceUsage{
|
||||
ru1.ID: ru1,
|
||||
}
|
||||
@@ -638,25 +535,21 @@ func TestResourceClearUsage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
func TestResourceRecordUsages(t *testing.T) {
|
||||
var r1 *Resource
|
||||
var r2 *Resource
|
||||
var ru1 *ResourceUsage
|
||||
var ru2 *ResourceUsage
|
||||
ru1 = &ResourceUsage{
|
||||
ru1 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU1",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 1,
|
||||
}
|
||||
|
||||
ru2 = &ResourceUsage{
|
||||
ru2 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU2",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 2,
|
||||
}
|
||||
|
||||
r1 = &Resource{
|
||||
r1 := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL1",
|
||||
rPrf: &ResourceProfile{
|
||||
@@ -678,7 +571,6 @@ func TestResourceRecordUsages(t *testing.T) {
|
||||
},
|
||||
TTLIdx: []string{ru1.ID},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 100,
|
||||
}
|
||||
|
||||
if err := r1.recordUsage(ru2); err != nil {
|
||||
@@ -694,7 +586,7 @@ func TestResourceRecordUsages(t *testing.T) {
|
||||
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
|
||||
}
|
||||
}
|
||||
r2 = &Resource{
|
||||
r2 := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL2",
|
||||
rPrf: &ResourceProfile{
|
||||
@@ -713,15 +605,9 @@ func TestResourceRecordUsages(t *testing.T) {
|
||||
ru2.ID: ru2,
|
||||
},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 50,
|
||||
}
|
||||
|
||||
rs := Resources{r2, r1}
|
||||
rs.Sort()
|
||||
|
||||
if rs[0].rPrf.ID != "RL1" {
|
||||
t.Error("Sort failed")
|
||||
}
|
||||
r1.Usages = map[string]*ResourceUsage{
|
||||
ru1.ID: ru1,
|
||||
}
|
||||
@@ -734,25 +620,21 @@ func TestResourceRecordUsages(t *testing.T) {
|
||||
}
|
||||
}
|
||||
func TestResourceAllocateResource(t *testing.T) {
|
||||
var r1 *Resource
|
||||
var r2 *Resource
|
||||
var ru1 *ResourceUsage
|
||||
var ru2 *ResourceUsage
|
||||
ru1 = &ResourceUsage{
|
||||
ru1 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU1",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 1,
|
||||
}
|
||||
|
||||
ru2 = &ResourceUsage{
|
||||
ru2 := &ResourceUsage{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RU2",
|
||||
ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC),
|
||||
Units: 2,
|
||||
}
|
||||
|
||||
r1 = &Resource{
|
||||
r1 := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL1",
|
||||
rPrf: &ResourceProfile{
|
||||
@@ -774,7 +656,6 @@ func TestResourceAllocateResource(t *testing.T) {
|
||||
},
|
||||
TTLIdx: []string{ru1.ID},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 100,
|
||||
}
|
||||
|
||||
if err := r1.recordUsage(ru2); err != nil {
|
||||
@@ -790,7 +671,7 @@ func TestResourceAllocateResource(t *testing.T) {
|
||||
t.Errorf("expecting: %+v, received: %+v", 4, r1.tUsage)
|
||||
}
|
||||
}
|
||||
r2 = &Resource{
|
||||
r2 := &Resource{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "RL2",
|
||||
rPrf: &ResourceProfile{
|
||||
@@ -809,15 +690,9 @@ func TestResourceAllocateResource(t *testing.T) {
|
||||
ru2.ID: ru2,
|
||||
},
|
||||
tUsage: utils.Float64Pointer(2),
|
||||
weight: 50,
|
||||
}
|
||||
|
||||
rs := Resources{r2, r1}
|
||||
rs.Sort()
|
||||
|
||||
if rs[0].rPrf.ID != "RL1" {
|
||||
t.Error("Sort failed")
|
||||
}
|
||||
rs := Resources{r1, r2}
|
||||
r1.Usages = map[string]*ResourceUsage{
|
||||
ru1.ID: ru1,
|
||||
}
|
||||
@@ -3449,7 +3324,6 @@ func TestResourcesV1ResourcesForEventOK(t *testing.T) {
|
||||
tUsage: utils.Float64Pointer(10),
|
||||
ttl: utils.DurationPointer(time.Minute),
|
||||
TTLIdx: []string{},
|
||||
weight: 10,
|
||||
}
|
||||
err := dm.SetResourceProfile(context.Background(), rsPrf, true)
|
||||
if err != nil {
|
||||
@@ -3489,7 +3363,6 @@ func TestResourcesV1ResourcesForEventOK(t *testing.T) {
|
||||
tUsage: utils.Float64Pointer(10),
|
||||
ttl: utils.DurationPointer(72 * time.Hour),
|
||||
TTLIdx: []string{},
|
||||
weight: 10,
|
||||
},
|
||||
}
|
||||
var reply Resources
|
||||
@@ -3794,7 +3667,6 @@ func TestResourcesV1ResourcesForEventCacheReplySet(t *testing.T) {
|
||||
tUsage: utils.Float64Pointer(10),
|
||||
ttl: utils.DurationPointer(time.Minute),
|
||||
TTLIdx: []string{},
|
||||
weight: 10,
|
||||
}
|
||||
err := dm.SetResourceProfile(context.Background(), rsPrf, true)
|
||||
if err != nil {
|
||||
@@ -3834,7 +3706,6 @@ func TestResourcesV1ResourcesForEventCacheReplySet(t *testing.T) {
|
||||
tUsage: utils.Float64Pointer(10),
|
||||
ttl: utils.DurationPointer(72 * time.Hour),
|
||||
TTLIdx: []string{},
|
||||
weight: 10,
|
||||
},
|
||||
}
|
||||
var reply Resources
|
||||
@@ -6552,7 +6423,6 @@ func TestResourcesMatchingResourcesForEventFinalCacheSetErr(t *testing.T) {
|
||||
Usages: make(map[string]*ResourceUsage),
|
||||
ttl: utils.DurationPointer(10 * time.Second),
|
||||
dirty: utils.BoolPointer(false),
|
||||
weight: 10,
|
||||
}
|
||||
|
||||
if rcv, err := rS.matchingResourcesForEvent(context.Background(), "cgrates.org", ev, ev.ID,
|
||||
|
||||
Reference in New Issue
Block a user