RateS computeRateSIntervals accepting usageStart

This commit is contained in:
DanB
2020-11-13 14:18:01 +01:00
parent fd6b9f6678
commit 16fe6c2b2c
5 changed files with 100 additions and 15 deletions

View File

@@ -236,3 +236,7 @@ func CostForIntervals(rtIvls []*RateSInterval) (cost *utils.Decimal) {
}
return
}
// CompressIntervals will compress intervals which equal
func CompressIntervals(rtIvls []*RateSInterval) {
}

View File

@@ -178,8 +178,7 @@ func orderRatesOnIntervals(aRts []*engine.Rate, sTime time.Time, usage time.Dura
}
// computeRateSIntervals will give out the cost projection for the given orderedRates and usage
func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*engine.RateSInterval, err error) {
var rtUsageSIdx time.Duration // rtUsageSIdx for one rate
func computeRateSIntervals(rts []*orderedRate, usageStart, usage time.Duration) (rtIvls []*engine.RateSInterval, err error) {
for i, rt := range rts {
isLastRt := i == len(rts)-1
var rtUsageEIdx time.Duration
@@ -189,7 +188,7 @@ func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*e
rtUsageEIdx = usage
}
var rIcmts []*engine.RateSIncrement
iRtUsageSIdx := rtUsageSIdx
iRtUsageSIdx := usageStart
iRtUsageEIdx := rtUsageEIdx
for j, iRt := range rt.IntervalRates {
if iRtUsageSIdx >= rtUsageEIdx { // charged enough for interval
@@ -201,9 +200,10 @@ func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*e
rt.UID(), iRtUsageSIdx)
}
isLastIRt := j == len(rt.IntervalRates)-1
if iRt.IntervalStart > iRtUsageSIdx ||
(!isLastIRt && rt.IntervalRates[j+1].IntervalStart <= iRtUsageSIdx) {
break // the rates should be already ordered, break here
if iRt.IntervalStart > iRtUsageSIdx {
break
} else if !isLastIRt && rt.IntervalRates[j+1].IntervalStart <= iRtUsageSIdx {
continue // the rates should be already ordered, break here
}
if !isLastIRt {
iRtUsageEIdx = rt.IntervalRates[j+1].IntervalStart
@@ -236,13 +236,13 @@ func computeRateSIntervals(rts []*orderedRate, usage time.Duration) (rtIvls []*e
}
rtIvls = append(rtIvls,
&engine.RateSInterval{
UsageStart: rtUsageSIdx,
UsageStart: usageStart,
Increments: rIcmts,
CompressFactor: 1})
if iRtUsageSIdx >= usage { // charged enough for the usage
break
}
rtUsageSIdx = rtUsageEIdx // continue for the next interval
usageStart = rtUsageEIdx // continue for the next interval
}
return
}

View File

@@ -1669,9 +1669,63 @@ func TestComputeRateSIntervals(t *testing.T) {
CompressFactor: 1,
},
}
if rtIvls, err := computeRateSIntervals(rts, time.Duration(130*time.Second)); err != nil {
if rtIvls, err := computeRateSIntervals(rts,
time.Duration(0), time.Duration(130*time.Second)); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eRtIvls, rtIvls) {
t.Errorf("expecting: %+v, received: %+v", eRtIvls, rtIvls)
}
rts = []*orderedRate{
{
time.Duration(0),
rt0,
},
{
time.Duration(90 * time.Second),
rt1,
},
}
eRtIvls = []*engine.RateSInterval{
{
UsageStart: time.Duration(time.Minute),
Increments: []*engine.RateSIncrement{
{
UsageStart: time.Duration(time.Minute),
Usage: time.Duration(30 * time.Second),
Rate: rt0,
IntervalRateIndex: 1,
CompressFactor: 30,
},
},
CompressFactor: 1,
},
{
UsageStart: time.Duration(90 * time.Second),
Increments: []*engine.RateSIncrement{
{
UsageStart: time.Duration(90 * time.Second),
Usage: time.Duration(30 * time.Second),
Rate: rt1,
IntervalRateIndex: 0,
CompressFactor: 30,
},
{
UsageStart: time.Duration(2 * time.Minute),
Usage: time.Duration(10 * time.Second),
Rate: rt1,
IntervalRateIndex: 1,
CompressFactor: 10,
},
},
CompressFactor: 1,
},
}
if rtIvls, err := computeRateSIntervals(rts,
time.Duration(time.Minute), time.Duration(130*time.Second)); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eRtIvls, rtIvls) {
t.Errorf("expecting: %+v, received: %+v", utils.ToIJSON(eRtIvls), utils.ToIJSON(rtIvls))
}
}

View File

@@ -133,30 +133,43 @@ func (rS *RateS) matchingRateProfileForEvent(tnt string, args *ArgsCostForEvent,
func (rS *RateS) rateProfileCostForEvent(rtPfl *engine.RateProfile, args *ArgsCostForEvent) (rts []*engine.RateSInterval, err error) {
var rtIDs utils.StringSet
if rtIDs, err = engine.MatchingItemIDsForEvent(
args.CGRevent.Event,
args.CGREventWithOpts.CGREvent.Event,
rS.cfg.RateSCfg().RateStringIndexedFields,
rS.cfg.RateSCfg().RatePrefixIndexedFields,
rS.cfg.RateSCfg().RateSuffixIndexedFields,
rS.dm,
utils.CacheRateFilterIndexes,
utils.ConcatenatedKey(args.CGRevent.Tenant, rtPfl.ID),
utils.ConcatenatedKey(args.CGREventWithOpts.CGREvent.Tenant, rtPfl.ID),
rS.cfg.RateSCfg().RateIndexedSelects,
rS.cfg.RateSCfg().RateNestedFields,
); err != nil {
return
}
aRates := make([]*engine.Rate, len(rtIDs))
evNm := utils.MapStorage{utils.MetaReq: cgrEv.Event}
evNm := utils.MapStorage{utils.MetaReq: args.CGREventWithOpts.CGREvent.Event}
for rtID := range rtIDs {
rt := rtPfl.Rates[rtID] // pick the rate directly from map based on matched ID
var pass bool
if pass, err = rS.filterS.Pass(cgrEv.Tenant, rt.FilterIDs, evNm); err != nil {
if pass, err = rS.filterS.Pass(args.CGREventWithOpts.CGREvent.Tenant, rt.FilterIDs, evNm); err != nil {
return
} else if !pass {
continue
}
aRates = append(aRates, rt)
}
ordRts := orderRatesOnIntervals(aRates)
var sTime time.Time
if sTime, err = args.StartTime(rS.cfg.GeneralCfg().DefaultTimezone); err != nil {
return
}
var usage time.Duration
if usage, err = args.Usage(); err != nil {
return
}
var ordRts []*orderedRate
if ordRts, err = orderRatesOnIntervals(aRates, sTime, usage, true, 1000000); err != nil {
return
}
return
}
*/
@@ -191,6 +204,19 @@ func (args *ArgsCostForEvent) StartTime(tmz string) (sTime time.Time, err error)
return time.Now(), nil
}
// usage returns the event time used to check active rate profiles
func (args *ArgsCostForEvent) Usage() (usage time.Duration, err error) {
if uIface, has := args.Opts[utils.OptsRatesUsage]; has {
return utils.IfaceAsDuration(uIface)
}
if usage, err = args.CGREvent.FieldAsDuration(utils.Usage); err != nil {
if err != utils.ErrNotFound {
return
}
}
return time.Duration(time.Minute), nil
}
// V1CostForEvent will be called to calculate the cost for an event
func (rS *RateS) V1CostForEvent(args *ArgsCostForEvent, cC *utils.ChargedCost) (err error) {
return

View File

@@ -2343,7 +2343,7 @@ var (
)
// CGROptionsSet the possible cgr options
var CGROptionsSet = NewStringSet([]string{OptsRatesStartTime, OptsSessionTTL, OptsSessionTTLMaxDelay,
var CGROptionsSet = NewStringSet([]string{OptsRatesStartTime, OptsRatesUsage, OptsSessionTTL, OptsSessionTTLMaxDelay,
OptsSessionTTLLastUsed, OptsSessionTTLLastUsage, OptsSessionTTLUsage, OptsDebitInterval, OptsStirATest,
OptsStirPayloadMaxDuration, OptsStirIdentity, OptsStirOriginatorTn, OptsStirOriginatorURI,
OptsStirDestinationTn, OptsStirDestinationURI, OptsStirPublicKeyPath, OptsStirPrivateKeyPath,
@@ -2372,6 +2372,7 @@ const (
OptsRoutesLimit = "*routes_limit"
OptsRoutesOffset = "*routes_offset"
OptsRatesStartTime = "*ratesStartTime"
OptsRatesUsage = "*ratesUsage"
OptsSessionTTL = "*sessionTTL"
OptsSessionTTLMaxDelay = "*sessionTTLMaxDelay"
OptsSessionTTLLastUsed = "*sessionTTLLastUsed"