mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Adding DynamicWeight support to RateS
This commit is contained in:
@@ -29,6 +29,11 @@ import (
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
type rpWithWeight struct {
|
||||
*engine.RateProfile
|
||||
weight float64
|
||||
}
|
||||
|
||||
func newRatesWithWinner(rIt *rateWithTimes) *ratesWithWinner {
|
||||
return &ratesWithWinner{
|
||||
rts: map[string]*rateWithTimes{
|
||||
@@ -53,7 +58,7 @@ type ratesWithWinner struct {
|
||||
//add will add the rate to the rates
|
||||
func (rs *ratesWithWinner) add(rWt *rateWithTimes) {
|
||||
rs.rts[rWt.id()] = rWt
|
||||
if rs.wnr == nil { //|| rs.wnr.rt.Weight < rWt.rt.Weight {
|
||||
if rs.wnr == nil || rs.wnr.weight < rWt.weight {
|
||||
rs.wnr = rWt
|
||||
}
|
||||
}
|
||||
@@ -75,6 +80,7 @@ type rateWithTimes struct {
|
||||
rt *engine.Rate
|
||||
aTime,
|
||||
iTime time.Time
|
||||
weight float64
|
||||
}
|
||||
|
||||
// id is used to provide an unique identifier for a rateWithTimes
|
||||
@@ -92,7 +98,7 @@ type orderedRate struct {
|
||||
|
||||
// orderRatesOnIntervals will order the rates based on ActivationInterval and intervalStart of each Rate
|
||||
// there can be only one winning Rate for each interval, prioritized by the Weight
|
||||
func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Duration,
|
||||
func orderRatesOnIntervals(aRts []*engine.Rate, wghts []float64, sTime time.Time, usage time.Duration,
|
||||
isDuration bool, verbosity int) (ordRts []*orderedRate, err error) {
|
||||
|
||||
endTime := sTime.Add(usage)
|
||||
@@ -100,16 +106,17 @@ func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Dura
|
||||
// index the received rates based on unique times they run
|
||||
rtIdx := make(map[time.Time]*ratesWithWinner) // map[ActivationTimes]*ratesWithWinner
|
||||
allRates := make(map[string]*rateWithTimes)
|
||||
for _, rt := range aRts {
|
||||
for i, rt := range aRts {
|
||||
var rTimes [][]time.Time
|
||||
if rTimes, err = rt.RunTimes(sTime, endTime, verbosity); err != nil {
|
||||
return
|
||||
}
|
||||
for _, rTimeSet := range rTimes {
|
||||
rIt := &rateWithTimes{
|
||||
rt: rt,
|
||||
aTime: rTimeSet[0],
|
||||
iTime: rTimeSet[1],
|
||||
rt: rt,
|
||||
aTime: rTimeSet[0],
|
||||
iTime: rTimeSet[1],
|
||||
weight: wghts[i], // weights are in order of aRts
|
||||
}
|
||||
allRates[rIt.id()] = rIt
|
||||
if _, hasKey := rtIdx[rTimeSet[0]]; !hasKey {
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/cgrates/cgrates/engine"
|
||||
)
|
||||
|
||||
/*
|
||||
func TestOrderRatesOnIntervals(t *testing.T) {
|
||||
rt0 := &engine.Rate{
|
||||
ID: "RATE0",
|
||||
@@ -132,7 +133,8 @@ func TestOrderRatesOnIntervals(t *testing.T) {
|
||||
utils.ToIJSON(expOrdered), utils.ToIJSON(ordRts))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsChristmasDay(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "ALWAYS_RATE",
|
||||
@@ -243,7 +245,8 @@ func TestOrderRatesOnIntervalsChristmasDay(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsDoubleRates1(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "ALWAYS_RATE",
|
||||
@@ -318,6 +321,7 @@ func TestOrderRatesOnIntervalsDoubleRates1(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsEveryTwentyFiveMins(t *testing.T) {
|
||||
@@ -390,7 +394,7 @@ func TestOrderRatesOnIntervalsEveryTwentyFiveMins(t *testing.T) {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsOneMinutePause(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "ALWAYS_RATE",
|
||||
@@ -462,7 +466,7 @@ func TestOrderRatesOnIntervalsOneMinutePause(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsNewYear(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
@@ -548,15 +552,16 @@ func TestOrderRatesOnIntervalsNewYear(t *testing.T) {
|
||||
}
|
||||
*/
|
||||
|
||||
func TestOrderRateOnIntervalsEveryHourEveryDay(t *testing.T) {
|
||||
rtEveryHour := &engine.Rate{
|
||||
ID: "HOUR_RATE",
|
||||
ActivationTimes: "* */1 * * *",
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
//func TestOrderRateOnIntervalsEveryHourEveryDay(t *testing.T) {
|
||||
// rtEveryHour := &engine.Rate{
|
||||
// ID: "HOUR_RATE",
|
||||
// ActivationTimes: "* */1 * * *",
|
||||
// Weights: utils.DynamicWeights{
|
||||
// {
|
||||
// Weight: 10,
|
||||
// },
|
||||
// },
|
||||
/*
|
||||
IntervalRates: []*engine.IntervalRate{
|
||||
{
|
||||
IntervalStart: 0,
|
||||
@@ -605,7 +610,8 @@ func TestOrderRateOnIntervalsEveryHourEveryDay(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsOneHourInThreeRates(t *testing.T) {
|
||||
rtOneHour1 := &engine.Rate{
|
||||
ID: "HOUR_RATE_1",
|
||||
@@ -693,6 +699,7 @@ func TestOrderRatesOnIntervalsOneHourInThreeRates(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func TestOrderRateOnIntervalsEveryThreeHours(t *testing.T) {
|
||||
@@ -1002,7 +1009,7 @@ func TestOrderRatesOnIntervalsOnePrinciapalRateCase1(t *testing.T) {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsOnePrinciapalRateCase2(t *testing.T) {
|
||||
rtPrincipal := &engine.Rate{
|
||||
ID: "PRINCIPAL_RATE",
|
||||
@@ -1092,6 +1099,7 @@ func TestOrderRatesOnIntervalsOnePrinciapalRateCase2(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsEvenOddMinutes(t *testing.T) {
|
||||
@@ -1489,24 +1497,25 @@ func TestOrderRatesOnIntervalsSpecialHour(t *testing.T) {
|
||||
|
||||
*/
|
||||
|
||||
func TestOrderRateIntervalsRateEveryTenMinutes(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "DAY_RATE",
|
||||
ActivationTimes: "* * 21 7 *",
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
IntervalRates: []*engine.IntervalRate{
|
||||
{
|
||||
IntervalStart: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
rtEveryTenMin := &engine.Rate{
|
||||
ID: "EVERY_TEN_MIN",
|
||||
ActivationTimes: "*/20 * * * *",
|
||||
//func TestOrderRateIntervalsRateEveryTenMinutes(t *testing.T) {
|
||||
// rt1 := &engine.Rate{
|
||||
// ID: "DAY_RATE",
|
||||
// ActivationTimes: "* * 21 7 *",
|
||||
// Weights: utils.DynamicWeights{
|
||||
// {
|
||||
// Weight: 10,
|
||||
// },
|
||||
// },
|
||||
// IntervalRates: []*engine.IntervalRate{
|
||||
// {
|
||||
// IntervalStart: 0,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// rtEveryTenMin := &engine.Rate{
|
||||
// ID: "EVERY_TEN_MIN",
|
||||
// ActivationTimes: "*/20 * * * *",
|
||||
/*
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 20,
|
||||
@@ -1554,6 +1563,7 @@ func TestOrderRateIntervalsRateEveryTenMinutes(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func TestOrderRatesOnIntervalsDayOfTheWeek(t *testing.T) {
|
||||
@@ -1673,6 +1683,7 @@ func TestNewRatesWithWinner(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestOrderRatesOnIntervalCaseMaxIterations(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "RT_1",
|
||||
@@ -1695,7 +1706,8 @@ func TestOrderRatesOnIntervalCaseMaxIterations(t *testing.T) {
|
||||
t.Errorf("Expected %+v, received %+v", expectedErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalIsDirectionFalse(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "RT_1",
|
||||
@@ -1729,7 +1741,8 @@ func TestOrderRatesOnIntervalIsDirectionFalse(t *testing.T) {
|
||||
t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalWinnNill(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "RT_1",
|
||||
@@ -1763,7 +1776,8 @@ func TestOrderRatesOnIntervalWinnNill(t *testing.T) {
|
||||
t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalIntervalStartHigherThanEndIdx(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "RT_1",
|
||||
@@ -1795,7 +1809,8 @@ func TestOrderRatesOnIntervalIntervalStartHigherThanEndIdx(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestOrderRatesOnIntervalStartLowerThanEndIdx(t *testing.T) {
|
||||
rt1 := &engine.Rate{
|
||||
ID: "RT_1",
|
||||
@@ -1832,6 +1847,7 @@ func TestOrderRatesOnIntervalStartLowerThanEndIdx(t *testing.T) {
|
||||
t.Errorf("Expected %+v, received %+v", utils.ToJSON(expected), utils.ToJSON(ordRts))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestComputeRateSIntervals(t *testing.T) {
|
||||
minDecimal, err := utils.NewDecimalFromUsage("1m")
|
||||
@@ -2255,6 +2271,7 @@ func TestComputeRateSIntervalsWIthFixedFee(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestComputeRateSIntervals2(t *testing.T) {
|
||||
minDecimal, err := utils.NewDecimalFromUsage("1m")
|
||||
if err != nil {
|
||||
@@ -2369,6 +2386,7 @@ func TestComputeRateSIntervals2(t *testing.T) {
|
||||
t.Errorf("Expected %+v \n, received %+v", utils.ToJSON(eRtIvls), utils.ToJSON(rcveRtIvls))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestComputeRateSIntervalsEvery30Seconds(t *testing.T) {
|
||||
tsecDecimal, err := utils.NewDecimalFromUsage("30s")
|
||||
|
||||
@@ -91,6 +91,7 @@ func (rS *RateS) matchingRateProfileForEvent(tnt string, rPfIDs []string, args *
|
||||
}
|
||||
rPfIDs = rPfIDMp.AsSlice()
|
||||
}
|
||||
var rpWw *rpWithWeight
|
||||
for _, rPfID := range rPfIDs {
|
||||
var rPf *engine.RateProfile
|
||||
if rPf, err = rS.dm.GetRateProfile(tnt, rPfID,
|
||||
@@ -111,15 +112,20 @@ func (rS *RateS) matchingRateProfileForEvent(tnt string, rPfIDs []string, args *
|
||||
} else if !pass {
|
||||
continue
|
||||
}
|
||||
if rtPfl == nil { //|| rtPfl.Weight < rPf.Weight {
|
||||
rtPfl = rPf
|
||||
var rPfWeight float64
|
||||
if rPfWeight, err = engine.WeightFromDynamics(rPf.Weights,
|
||||
rS.filterS, tnt, evNm); err != nil {
|
||||
return
|
||||
}
|
||||
if rpWw == nil || rpWw.weight < rPfWeight {
|
||||
rpWw = &rpWithWeight{rPf, rPfWeight}
|
||||
}
|
||||
}
|
||||
if rtPfl == nil {
|
||||
return nil, utils.ErrNotFound
|
||||
}
|
||||
|
||||
return
|
||||
return rpWw.RateProfile, nil
|
||||
}
|
||||
|
||||
// rateProfileCostForEvent computes the rateProfileCost for an event based on a preselected rate profile
|
||||
@@ -153,6 +159,14 @@ func (rS *RateS) rateProfileCostForEvent(rtPfl *engine.RateProfile, args *utils.
|
||||
}
|
||||
aRates = append(aRates, rt)
|
||||
}
|
||||
// populate weights to be used on ordering
|
||||
wghts := make([]float64, len(aRates))
|
||||
for i, aRt := range aRates {
|
||||
if wghts[i], err = engine.WeightFromDynamics(aRt.Weights,
|
||||
rS.filterS, args.CGREvent.Tenant, evNm); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
var sTime time.Time
|
||||
if sTime, err = args.StartTime(rS.cfg.GeneralCfg().DefaultTimezone); err != nil {
|
||||
return
|
||||
@@ -162,7 +176,7 @@ func (rS *RateS) rateProfileCostForEvent(rtPfl *engine.RateProfile, args *utils.
|
||||
return
|
||||
}
|
||||
var ordRts []*orderedRate
|
||||
if ordRts, err = orderRatesOnIntervals(aRates, sTime, usage, true, verbosity); err != nil {
|
||||
if ordRts, err = orderRatesOnIntervals(aRates, wghts, sTime, usage, true, verbosity); err != nil {
|
||||
return
|
||||
}
|
||||
rpCost = &engine.RateProfileCost{
|
||||
|
||||
@@ -119,6 +119,7 @@ func TestMatchingRateProfileForEventActivationInterval(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestRateProfileCostForEvent(t *testing.T) {
|
||||
defaultCfg := config.NewDefaultCGRConfig()
|
||||
data := engine.NewInternalDB(nil, nil, true)
|
||||
@@ -222,6 +223,7 @@ func TestRateProfileCostForEvent(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestRateProfileCostForEventUnmatchEvent(t *testing.T) {
|
||||
defaultCfg := config.NewDefaultCGRConfig()
|
||||
@@ -320,6 +322,7 @@ func TestRateProfileCostForEventUnmatchEvent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestMatchingRateProfileEvent(t *testing.T) {
|
||||
defaultCfg := config.NewDefaultCGRConfig()
|
||||
data := engine.NewInternalDB(nil, nil, true)
|
||||
@@ -468,7 +471,8 @@ func TestMatchingRateProfileEvent(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestV1CostForEventError(t *testing.T) {
|
||||
defaultCfg := config.NewDefaultCGRConfig()
|
||||
data := engine.NewInternalDB(nil, nil, true)
|
||||
@@ -553,6 +557,7 @@ func TestV1CostForEventError(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// go test -run=^$ -v -bench=BenchmarkRateS_V1CostForEvent -benchtime=5s
|
||||
func BenchmarkRateS_V1CostForEvent(b *testing.B) {
|
||||
|
||||
@@ -288,7 +288,7 @@ type BalanceWithWeight struct {
|
||||
Weight float64
|
||||
}
|
||||
|
||||
// Balances is a sortable list of Balances
|
||||
// BalancesWithWeight is a sortable list of BalanceWithWeight
|
||||
type BalancesWithWeight []*BalanceWithWeight
|
||||
|
||||
// Sort is part of sort interface, sort based on Weight
|
||||
|
||||
Reference in New Issue
Block a user