EventCost compressing at ChargingIncrement level during Merge

This commit is contained in:
DanB
2018-10-31 20:27:52 +01:00
parent 0248805b8b
commit c9a6d158d5
3 changed files with 382 additions and 30 deletions

View File

@@ -344,18 +344,46 @@ func (ec *EventCost) accountingGetIDFromEventCost(oEC *EventCost, oAccountingID
return ec.Accounting.GetIDWithSet(oBC)
}
// appendCIl appends a ChargingInterval to existing chargers, no compression done
// appendCIl appends a ChargingInterval to existing chargers
// no compression done at ChargingInterval level, attempted on ChargingIncrement level
func (ec *EventCost) appendCIlFromEC(oEC *EventCost, cIlIdx int) {
cIl := oEC.Charges[cIlIdx]
cIl.RatingID = ec.ratingGetIDFomEventCost(oEC, cIl.RatingID)
for _, cIt := range cIl.Increments {
cIt.AccountingID = ec.accountingGetIDFromEventCost(oEC, cIt.AccountingID)
lastCIl := ec.Charges[len(ec.Charges)-1]
lastCIt := lastCIl.Increments[len(lastCIl.Increments)-1]
appendChargingIncrement := lastCIl.CompressFactor == 1 &&
lastCIl.RatingID == cIl.RatingID // attempt compressing of the ChargingIncrements
var idxFirstCIt *int // keep here the reference towards last not appended charging increment so we can create separate ChargingInterval
var idxLastCF *int // reference towards last compress not absorbed by ec.Charges
for cF := cIl.CompressFactor; cF > 0; cF-- {
for i, cIt := range cIl.Increments {
cIt.AccountingID = ec.accountingGetIDFromEventCost(oEC, cIt.AccountingID)
if idxFirstCIt != nil {
continue
}
if !appendChargingIncrement ||
!lastCIt.PartiallyEquals(cIt) {
idxFirstCIt = utils.IntPointer(i)
idxLastCF = utils.IntPointer(cF)
continue
}
lastCIt.CompressFactor += cIt.CompressFactor // compress the iterated ChargingIncrement
}
}
if idxFirstCIt != nil { // CIt was not completely absorbed
cIlCln := cIl.Clone()
cIlCln.CompressFactor = 1
cIlCln.Increments = cIlCln.Increments[*idxFirstCIt:]
ec.Charges = append(ec.Charges, cIlCln)
if *idxLastCF > 1 { // add the remaining part out of original ChargingInterval
cIl.CompressFactor = *idxLastCF - 1
ec.Charges = append(ec.Charges, cIl)
}
}
ec.Charges = append(ec.Charges, cIl)
}
// AppendChargingInterval appends or compresses a &ChargingInterval to existing ec.Chargers
func (ec *EventCost) AppendChargingIntervalFromEventCost(oEC *EventCost, cIlIdx int) {
func (ec *EventCost) appendChargingIntervalFromEventCost(oEC *EventCost, cIlIdx int) {
lenChargers := len(ec.Charges)
if lenChargers != 0 && ec.Charges[lenChargers-1].PartiallyEquals(oEC.Charges[cIlIdx]) {
ec.Charges[lenChargers-1].CompressFactor += 1
@@ -369,7 +397,7 @@ func (ec *EventCost) Merge(ecs ...*EventCost) {
for _, newEC := range ecs {
ec.AccountSummary = newEC.AccountSummary // updated AccountSummary information
for cIlIdx := range newEC.Charges {
ec.AppendChargingIntervalFromEventCost(newEC, cIlIdx)
ec.appendChargingIntervalFromEventCost(newEC, cIlIdx)
}
}
ec.ResetCounters()

View File

@@ -1225,18 +1225,6 @@ func TestECTrimMUsage(t *testing.T) {
}
})
}
/*
ec = testEC.Clone()
atUsage = time.Duration(61 * time.Second)
srplsEC, _ = ec.Trim(atUsage)
if ec.GetUsage() != atUsage {
t.Errorf("Wrongly trimmed EC: %s", utils.ToJSON(ec))
}
if srplsEC.GetUsage() != time.Duration(239*time.Second) {
t.Errorf("Wrong surplusEC: %s", utils.ToJSON(srplsEC))
}
*/
}
/*
@@ -1290,3 +1278,327 @@ func TestECMerge(t *testing.T) {
}
}
*/
func TestECMergeGT(t *testing.T) {
// InitialEventCost
ecGT := &EventCost{
CGRID: "7636f3f1a06dffa038ba7900fb57f52d28830a24",
RunID: utils.META_DEFAULT,
StartTime: time.Date(2018, 7, 27, 0, 59, 21, 0, time.UTC),
Charges: []*ChargingInterval{
&ChargingInterval{
RatingID: "cc68da4",
Increments: []*ChargingIncrement{
&ChargingIncrement{
Usage: time.Duration(102400),
AccountingID: "0d87a64",
CompressFactor: 103,
},
},
CompressFactor: 1,
},
},
AccountSummary: &AccountSummary{
Tenant: "cgrates.org",
ID: "dan",
BalanceSummaries: []*BalanceSummary{
&BalanceSummary{
UUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
ID: "addon_data",
Type: utils.DATA,
Value: 10726871040},
},
},
Rating: Rating{
"cc68da4": &RatingUnit{
RatesID: "06dee2e",
RatingFiltersID: "216b0a5",
},
},
Accounting: Accounting{
"0d87a64": &BalanceCharge{
AccountID: "cgrates.org:dan",
BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
Units: 102400,
ExtraChargeID: utils.META_NONE,
},
},
RatingFilters: RatingFilters{
"216b0a5": RatingMatchedFilters{
"DestinationID": utils.META_ANY,
"DestinationPrefix": "42502",
"RatingPlanID": utils.META_NONE,
"Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
},
},
Rates: ChargedRates{
"06dee2e": RateGroups{
&Rate{
RateIncrement: time.Duration(102400),
RateUnit: time.Duration(102400)},
},
},
}
ecGTUpdt := &EventCost{
CGRID: "7636f3f1a06dffa038ba7900fb57f52d28830a24",
RunID: utils.META_DEFAULT,
StartTime: time.Date(2018, 7, 27, 0, 59, 38, 0105472, time.UTC),
Charges: []*ChargingInterval{
&ChargingInterval{
RatingID: "6a83227",
Increments: []*ChargingIncrement{
&ChargingIncrement{
Usage: time.Duration(102400),
AccountingID: "9288f93",
CompressFactor: 84,
},
},
CompressFactor: 1,
},
},
AccountSummary: &AccountSummary{
Tenant: "cgrates.org",
ID: "dan",
BalanceSummaries: []*BalanceSummary{
&BalanceSummary{
UUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
ID: "addon_data",
Type: utils.DATA,
Value: 10718269440},
},
},
Rating: Rating{
"6a83227": &RatingUnit{
RatesID: "52f8b0f",
RatingFiltersID: "17f7216",
},
},
Accounting: Accounting{
"9288f93": &BalanceCharge{
AccountID: "cgrates.org:dan",
BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
Units: 102400,
ExtraChargeID: utils.META_NONE,
},
},
RatingFilters: RatingFilters{
"17f7216": RatingMatchedFilters{
"DestinationID": utils.META_ANY,
"DestinationPrefix": "42502",
"RatingPlanID": utils.META_NONE,
"Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
},
},
Rates: ChargedRates{
"52f8b0f": RateGroups{
&Rate{
RateIncrement: time.Duration(102400),
RateUnit: time.Duration(102400)},
},
},
}
ecGT.Merge(ecGTUpdt)
ecExpct := &EventCost{
CGRID: "7636f3f1a06dffa038ba7900fb57f52d28830a24",
RunID: utils.META_DEFAULT,
StartTime: time.Date(2018, 7, 27, 0, 59, 21, 0, time.UTC),
Charges: []*ChargingInterval{
&ChargingInterval{
RatingID: "cc68da4",
Increments: []*ChargingIncrement{
&ChargingIncrement{
Usage: time.Duration(102400),
AccountingID: "0d87a64",
CompressFactor: 187,
},
},
CompressFactor: 1,
},
},
AccountSummary: &AccountSummary{
Tenant: "cgrates.org",
ID: "dan",
BalanceSummaries: []*BalanceSummary{
&BalanceSummary{
UUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
ID: "addon_data",
Type: utils.DATA,
Value: 10718269440},
},
},
Rating: Rating{
"cc68da4": &RatingUnit{
RatesID: "06dee2e",
RatingFiltersID: "216b0a5",
},
},
Accounting: Accounting{
"0d87a64": &BalanceCharge{
AccountID: "cgrates.org:dan",
BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
Units: 102400,
ExtraChargeID: utils.META_NONE,
},
},
RatingFilters: RatingFilters{
"216b0a5": RatingMatchedFilters{
"DestinationID": utils.META_ANY,
"DestinationPrefix": "42502",
"RatingPlanID": utils.META_NONE,
"Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
},
},
Rates: ChargedRates{
"06dee2e": RateGroups{
&Rate{
RateIncrement: time.Duration(102400),
RateUnit: time.Duration(102400)},
},
},
}
if len(ecGT.Charges) != len(ecExpct.Charges) ||
!reflect.DeepEqual(ecGT.Charges[0].TotalUsage(), ecExpct.Charges[0].TotalUsage()) ||
!reflect.DeepEqual(ecGT.Charges[0].TotalCost(), ecExpct.Charges[0].TotalCost()) {
t.Errorf("expecting: %s\n\n, received: %s",
utils.ToJSON(ecExpct), utils.ToJSON(ecGT))
}
}
func TestECAppendCIlFromEC(t *testing.T) {
ec := &EventCost{
Charges: []*ChargingInterval{
&ChargingInterval{
RatingID: "cc68da4",
Increments: []*ChargingIncrement{
&ChargingIncrement{
Usage: time.Duration(102400),
AccountingID: "0d87a64",
CompressFactor: 103,
},
},
CompressFactor: 1,
},
},
Rating: Rating{
"cc68da4": &RatingUnit{
RatesID: "06dee2e",
RatingFiltersID: "216b0a5",
},
},
Accounting: Accounting{
"0d87a64": &BalanceCharge{
AccountID: "cgrates.org:dan",
BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
Units: 102400,
ExtraChargeID: utils.META_NONE,
},
},
RatingFilters: RatingFilters{
"216b0a5": RatingMatchedFilters{
"DestinationID": utils.META_ANY,
"DestinationPrefix": "42502",
"RatingPlanID": utils.META_NONE,
"Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
},
},
Rates: ChargedRates{
"06dee2e": RateGroups{
&Rate{
RateIncrement: time.Duration(102400),
RateUnit: time.Duration(102400)},
},
},
}
oEC := &EventCost{
Charges: []*ChargingInterval{
&ChargingInterval{
RatingID: "6a83227",
Increments: []*ChargingIncrement{
&ChargingIncrement{
Usage: time.Duration(102400),
AccountingID: "9288f93",
CompressFactor: 84,
},
},
CompressFactor: 1,
},
},
Rating: Rating{
"6a83227": &RatingUnit{
RatesID: "52f8b0f",
RatingFiltersID: "17f7216",
},
},
Accounting: Accounting{
"9288f93": &BalanceCharge{
AccountID: "cgrates.org:dan",
BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
Units: 102400,
ExtraChargeID: utils.META_NONE,
},
},
RatingFilters: RatingFilters{
"17f7216": RatingMatchedFilters{
"DestinationID": utils.META_ANY,
"DestinationPrefix": "42502",
"RatingPlanID": utils.META_NONE,
"Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
},
},
Rates: ChargedRates{
"52f8b0f": RateGroups{
&Rate{
RateIncrement: time.Duration(102400),
RateUnit: time.Duration(102400)},
},
},
}
ec.appendCIlFromEC(oEC, 0)
eEC := &EventCost{
Charges: []*ChargingInterval{
&ChargingInterval{
RatingID: "cc68da4",
Increments: []*ChargingIncrement{
&ChargingIncrement{
Usage: time.Duration(102400),
AccountingID: "0d87a64",
CompressFactor: 187,
},
},
CompressFactor: 1,
},
},
Rating: Rating{
"cc68da4": &RatingUnit{
RatesID: "06dee2e",
RatingFiltersID: "216b0a5",
},
},
Accounting: Accounting{
"0d87a64": &BalanceCharge{
AccountID: "cgrates.org:dan",
BalanceUUID: "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
Units: 102400,
ExtraChargeID: utils.META_NONE,
},
},
RatingFilters: RatingFilters{
"216b0a5": RatingMatchedFilters{
"DestinationID": utils.META_ANY,
"DestinationPrefix": "42502",
"RatingPlanID": utils.META_NONE,
"Subject": "9a767726-fe69-4940-b7bd-f43de9f0f8a5",
},
},
Rates: ChargedRates{
"06dee2e": RateGroups{
&Rate{
RateIncrement: time.Duration(102400),
RateUnit: time.Duration(102400)},
},
},
}
if !reflect.DeepEqual(eEC, ec) {
t.Errorf("expecting: %s, received: %s", utils.ToJSON(eEC), utils.ToJSON(ec))
}
}

View File

@@ -36,18 +36,17 @@ type ChargingInterval struct {
}
// PartiallyEquals does not compare CompressFactor, usefull for Merge
func (cIl *ChargingInterval) PartiallyEquals(oCIl *ChargingInterval) (equals bool) {
if equals = cIl.RatingID == oCIl.RatingID &&
func (cIl *ChargingInterval) PartiallyEquals(oCIl *ChargingInterval) bool {
if equals := cIl.RatingID == oCIl.RatingID &&
len(cIl.Increments) == len(oCIl.Increments); !equals {
return
return false
}
for i := range cIl.Increments {
if !cIl.Increments[i].Equals(oCIl.Increments[i]) {
equals = false
break
return false
}
}
return
return true
}
// Usage computes the total usage of this ChargingInterval, ignoring CompressFactor
@@ -136,6 +135,13 @@ func (cIt *ChargingIncrement) Equals(oCIt *ChargingIncrement) bool {
cIt.CompressFactor == oCIt.CompressFactor
}
// PartiallyEquals ignores the CompressFactor when comparing
func (cIt *ChargingIncrement) PartiallyEquals(oCIt *ChargingIncrement) bool {
return cIt.Usage == oCIt.Usage &&
cIt.Cost == oCIt.Cost &&
cIt.AccountingID == oCIt.AccountingID
}
func (cIt *ChargingIncrement) Clone() (cln *ChargingIncrement) {
cln = new(ChargingIncrement)
*cln = *cIt
@@ -161,11 +167,19 @@ type BalanceCharge struct {
}
func (bc *BalanceCharge) Equals(oBC *BalanceCharge) bool {
bcExtraChargeID := bc.ExtraChargeID
if bcExtraChargeID == "" {
bcExtraChargeID = utils.META_NONE
}
oBCExtraChargerID := oBC.ExtraChargeID
if oBCExtraChargerID == "" { // so we can compare them properly
oBCExtraChargerID = utils.META_NONE
}
return bc.AccountID == oBC.AccountID &&
bc.BalanceUUID == oBC.BalanceUUID &&
bc.RatingID == oBC.RatingID &&
bc.Units == oBC.Units &&
bc.ExtraChargeID == oBC.ExtraChargeID
bcExtraChargeID == oBCExtraChargerID
}
func (bc *BalanceCharge) Clone() *BalanceCharge {
@@ -176,15 +190,13 @@ func (bc *BalanceCharge) Clone() *BalanceCharge {
type RatingMatchedFilters map[string]interface{}
func (rf RatingMatchedFilters) Equals(oRF RatingMatchedFilters) (equals bool) {
equals = true
func (rf RatingMatchedFilters) Equals(oRF RatingMatchedFilters) bool {
for k := range rf {
if rf[k] != oRF[k] {
equals = false
break
return false
}
}
return
return true
}
func (rf RatingMatchedFilters) Clone() (cln map[string]interface{}) {