From 4926e33528e10413caa80445a280338581bea1f3 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 3 Apr 2015 19:20:25 +0300 Subject: [PATCH 1/5] simplified account locking and more lcr qos --- engine/accountlock.go | 38 +++++++--------------- engine/accountlock_test.go | 12 +++---- engine/action_timing.go | 4 +-- engine/calldesc.go | 66 ++++++++++++++++++++++---------------- engine/lcr.go | 42 +++++++++++++++++++----- engine/responder.go | 34 ++++++++++---------- utils/slice.go | 11 +++++++ utils/utils_test.go | 18 +++++++++++ 8 files changed, 138 insertions(+), 87 deletions(-) diff --git a/engine/accountlock.go b/engine/accountlock.go index 9f77e078f..5309f75b8 100644 --- a/engine/accountlock.go +++ b/engine/accountlock.go @@ -30,22 +30,22 @@ func init() { type AccountLock struct { queue map[string]chan bool - sync.RWMutex + mu sync.RWMutex } func NewAccountLock() *AccountLock { return &AccountLock{queue: make(map[string]chan bool)} } -func (cm *AccountLock) GuardGetCost(name string, handler func() (*CallCost, error)) (reply *CallCost, err error) { - cm.RLock() +func (cm *AccountLock) GuardCallCost(handler func() (*CallCost, error), name string) (reply *CallCost, err error) { + cm.mu.RLock() lock, exists := AccLock.queue[name] - cm.RUnlock() + cm.mu.RUnlock() if !exists { - cm.Lock() + cm.mu.Lock() lock = make(chan bool, 1) AccLock.queue[name] = lock - cm.Unlock() + cm.mu.Unlock() } lock <- true reply, err = handler() @@ -53,32 +53,16 @@ func (cm *AccountLock) GuardGetCost(name string, handler func() (*CallCost, erro return } -func (cm *AccountLock) Guard(name string, handler func() (float64, error)) (reply float64, err error) { - cm.RLock() - lock, exists := AccLock.queue[name] - cm.RUnlock() - if !exists { - cm.Lock() - lock = make(chan bool, 1) - AccLock.queue[name] = lock - cm.Unlock() - } - lock <- true - reply, err = handler() - <-lock - return -} - -func (cm *AccountLock) GuardMany(names []string, handler func() (float64, error)) (reply float64, err error) { +func (cm *AccountLock) Guard(handler func() (float64, error), names ...string) (reply float64, err error) { for _, name := range names { - cm.RLock() + cm.mu.RLock() lock, exists := AccLock.queue[name] - cm.RUnlock() + cm.mu.RUnlock() if !exists { - cm.Lock() + cm.mu.Lock() lock = make(chan bool, 1) AccLock.queue[name] = lock - cm.Unlock() + cm.mu.Unlock() } lock <- true } diff --git a/engine/accountlock_test.go b/engine/accountlock_test.go index f9f085956..db2193cd9 100644 --- a/engine/accountlock_test.go +++ b/engine/accountlock_test.go @@ -25,23 +25,23 @@ import ( ) func ATestAccountLock(t *testing.T) { - go AccLock.Guard("1", func() (float64, error) { + go AccLock.Guard(func() (float64, error) { log.Print("first 1") time.Sleep(1 * time.Second) log.Print("end first 1") return 0, nil - }) - go AccLock.Guard("2", func() (float64, error) { + }, "1") + go AccLock.Guard(func() (float64, error) { log.Print("first 2") time.Sleep(1 * time.Second) log.Print("end first 2") return 0, nil - }) - go AccLock.Guard("1", func() (float64, error) { + }, "2") + go AccLock.Guard(func() (float64, error) { log.Print("second 1") time.Sleep(1 * time.Second) log.Print("end second 1") return 0, nil - }) + }, "1") time.Sleep(3 * time.Second) } diff --git a/engine/action_timing.go b/engine/action_timing.go index e22a5f923..dbc4b032c 100644 --- a/engine/action_timing.go +++ b/engine/action_timing.go @@ -257,7 +257,7 @@ func (at *ActionTiming) Execute() (err error) { return } for _, ubId := range at.AccountIds { - _, err := AccLock.Guard(ubId, func() (float64, error) { + _, err := AccLock.Guard(func() (float64, error) { ub, err := accountingStorage.GetAccount(ubId) if err != nil { Logger.Warning(fmt.Sprintf("Could not get user balances for this id: %s. Skipping!", ubId)) @@ -270,7 +270,7 @@ func (at *ActionTiming) Execute() (err error) { //Logger.Info(fmt.Sprintf("After execute, account: %+v", ub)) accountingStorage.SetAccount(ub) return 0, nil - }) + }, ubId) if err != nil { Logger.Warning(fmt.Sprintf("Error executing action timing: %v", err)) } diff --git a/engine/calldesc.go b/engine/calldesc.go index 082028845..809023ae8 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -502,10 +502,10 @@ func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err e return 0, err } else { if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { - AccLock.GuardMany(memberIds, func() (float64, error) { + AccLock.Guard(func() (float64, error) { duration, err = cd.getMaxSessionDuration(account) return 0, err - }) + }, memberIds...) } else { return 0, err } @@ -551,10 +551,10 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { return nil, err } else { if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { - AccLock.GuardMany(memberIds, func() (float64, error) { + AccLock.Guard(func() (float64, error) { cc, err = cd.debit(account, false, true) return 0, err - }) + }, memberIds...) } else { return nil, err } @@ -573,7 +573,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { } else { //log.Printf("ACC: %+v", account) if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { - AccLock.GuardMany(memberIds, func() (float64, error) { + AccLock.Guard(func() (float64, error) { remainingDuration, err := cd.getMaxSessionDuration(account) //log.Print("AFTER MAX SESSION: ", cd) if err != nil || remainingDuration == 0 { @@ -589,7 +589,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { cc, err = cd.debit(account, false, true) //log.Print(balanceMap[0].Value, balanceMap[1].Value) return 0, err - }) + }, memberIds...) } else { return nil, err } @@ -657,7 +657,7 @@ func (cd *CallDescriptor) Clone() *CallDescriptor { } } -func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { +func (cd *CallDescriptor) GetLCR(stats StatsInterface) (LCRCost, error) { lcr, err := dataStorage.GetLCR(cd.GetLCRKey(""), false) if err != nil || lcr == nil { // try the *any customer @@ -666,26 +666,24 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { } } lcr.Sort() - lcrCost := &LCRCost{ - TimeSpans: []*LCRTimeSpan{&LCRTimeSpan{StartTime: cd.TimeStart}}, - } + lcrCost := LCRCost{&LCRTimeSpan{StartTime: cd.TimeStart}} for _, lcrActivation := range lcr.Activations { // TODO: filer entry by destination lcrEntry := lcrActivation.GetLCREntryForPrefix(cd.Destination) if lcrActivation.ActivationTime.Before(cd.TimeStart) || lcrActivation.ActivationTime.Equal(cd.TimeStart) { - lcrCost.TimeSpans[0].Entry = lcrEntry + lcrCost[0].Entry = lcrEntry } else { if lcrActivation.ActivationTime.Before(cd.TimeEnd) { // add lcr timespan - lcrCost.TimeSpans = append(lcrCost.TimeSpans, &LCRTimeSpan{ + lcrCost = append(lcrCost, &LCRTimeSpan{ StartTime: lcrActivation.ActivationTime, Entry: lcrEntry, }) } } } - for _, ts := range lcrCost.TimeSpans { + for _, ts := range lcrCost { if ts.Entry.Strategy == LCR_STRATEGY_STATIC { for _, supplier := range ts.Entry.GetSuppliers() { lcrCD := cd.Clone() @@ -718,8 +716,9 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { if cd.account, err = accountingStorage.GetAccount(cd.GetAccountKey()); err != nil { continue } - if ts.Entry.Strategy == LCR_STRATEGY_QOS_WITH_THRESHOLD { - // get stats and filter suppliers by qos thresholds + var asr, acd float64 + var qosSortParams []string + if ts.Entry.Strategy == LCR_STRATEGY_QOS || ts.Entry.Strategy == LCR_STRATEGY_QOS_WITH_THRESHOLD { rpfKey := utils.ConcatenatedKey(ratingProfileSearchKey, supplier) if rpf, err := dataStorage.GetRatingProfile(rpfKey, false); err == nil || rpf != nil { rpf.RatingPlanActivations.Sort() @@ -749,19 +748,27 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { } asrValues.Sort() acdValues.Sort() - asrMin, asrMax, acdMin, acdMax := ts.Entry.GetQOSLimits() - // skip current supplier if off limits - if asrMin > 0 && asrValues[0] < asrMin { - continue + asr = utils.Avg(asrValues) + acd = utils.Avg(acdValues) + if ts.Entry.Strategy == LCR_STRATEGY_QOS_WITH_THRESHOLD { + qosSortParams = ts.Entry.GetQOSSortParams() } - if asrMax > 0 && asrValues[len(asrValues)-1] > asrMax { - continue - } - if acdMin > 0 && acdValues[0] < float64(acdMin) { - continue - } - if acdMax > 0 && acdValues[len(acdValues)-1] > float64(acdMax) { - continue + if ts.Entry.Strategy == LCR_STRATEGY_QOS_WITH_THRESHOLD { + // filter suppliers by qos thresholds + asrMin, asrMax, acdMin, acdMax := ts.Entry.GetQOSLimits() + // skip current supplier if off limits + if asrMin > 0 && asrValues[0] < asrMin { + continue + } + if asrMax > 0 && asrValues[len(asrValues)-1] > asrMax { + continue + } + if acdMin > 0 && acdValues[0] < float64(acdMin) { + continue + } + if acdMax > 0 && acdValues[len(acdValues)-1] > float64(acdMax) { + continue + } } } } @@ -774,6 +781,11 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface) (*LCRCost, error) { ts.SupplierCosts = append(ts.SupplierCosts, &LCRSupplierCost{ Supplier: supplier, Cost: cc.Cost, + QOS: map[string]float64{ + "ASR": asr, + "ACD": acd, + }, + qosSortParams: qosSortParams, }) } } diff --git a/engine/lcr.go b/engine/lcr.go index 0ea217b29..e8bc0ba03 100644 --- a/engine/lcr.go +++ b/engine/lcr.go @@ -57,9 +57,7 @@ type LCREntry struct { precision int } -type LCRCost struct { - TimeSpans []*LCRTimeSpan -} +type LCRCost []*LCRTimeSpan type LCRTimeSpan struct { StartTime time.Time @@ -68,9 +66,11 @@ type LCRTimeSpan struct { } type LCRSupplierCost struct { - Supplier string - Cost float64 - Error error + Supplier string + Cost float64 + Error error + QOS map[string]float64 + qosSortParams []string } func (lcr *LCR) GetId() string { @@ -94,7 +94,7 @@ func (lcr *LCR) Sort() { } func (le *LCREntry) GetSuppliers() []string { - suppliers := strings.Split(le.StrategyParams, ";") + suppliers := strings.Split(le.StrategyParams, utils.INFIELD_SEP) for i := 0; i < len(suppliers); i++ { suppliers[i] = strings.TrimSpace(suppliers[i]) } @@ -103,7 +103,7 @@ func (le *LCREntry) GetSuppliers() []string { func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minACD, maxACD time.Duration) { // MIN_ASR;MAX_ASR;MIN_ACD;MAX_ACD - params := strings.Split(le.StrategyParams, ";") + params := strings.Split(le.StrategyParams, utils.INFIELD_SEP) if len(params) == 4 { var err error if minASR, err = strconv.ParseFloat(params[0], 64); err != nil { @@ -122,6 +122,14 @@ func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minACD, maxACD time. return } +func (le *LCREntry) GetQOSSortParams() []string { + // ASR;ACD + if params := strings.Split(le.StrategyParams, utils.INFIELD_SEP); len(params) > 0 { + return params + } + return []string{ASR, ACD} +} + type LCREntriesSorter []*LCREntry func (es LCREntriesSorter) Len() int { @@ -177,6 +185,8 @@ func (lts *LCRTimeSpan) Sort() { sort.Sort(LowestSupplierCostSorter(lts.SupplierCosts)) case LCR_STRATEGY_HIGHEST: sort.Sort(HighestSupplierCostSorter(lts.SupplierCosts)) + case LCR_STRATEGY_QOS: + sort.Sort(QOSSorter(lts.SupplierCosts)) } } @@ -207,3 +217,19 @@ func (hscs HighestSupplierCostSorter) Swap(i, j int) { func (hscs HighestSupplierCostSorter) Less(i, j int) bool { return hscs[i].Cost > hscs[j].Cost } + +type QOSSorter []*LCRSupplierCost + +func (qoss QOSSorter) Len() int { + return len(qoss) +} + +func (qoss QOSSorter) Swap(i, j int) { + qoss[i], qoss[j] = qoss[j], qoss[i] +} + +func (qoss QOSSorter) Less(i, j int) bool { + //for _, param := range qoss[i].qosSortParams + //qoss[i].Cost > qoss[j].Cost + return false +} diff --git a/engine/responder.go b/engine/responder.go index 6d349a313..d322da1eb 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -55,9 +55,9 @@ func (rs *Responder) GetCost(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.GetCost") *reply, err = *r, e } else { - r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { + r, e := AccLock.GuardCallCost(func() (*CallCost, error) { return arg.GetCost() - }) + }, arg.GetAccountKey()) if e != nil { return e } else if r != nil { @@ -72,9 +72,9 @@ func (rs *Responder) Debit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.Debit") *reply, err = *r, e } else { - r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { + r, e := AccLock.GuardCallCost(func() (*CallCost, error) { return arg.Debit() - }) + }, arg.GetAccountKey()) if e != nil { return e } else if r != nil { @@ -89,9 +89,9 @@ func (rs *Responder) MaxDebit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.MaxDebit") *reply, err = *r, e } else { - r, e := AccLock.GuardGetCost(arg.GetAccountKey(), func() (*CallCost, error) { + r, e := AccLock.GuardCallCost(func() (*CallCost, error) { return arg.MaxDebit() - }) + }, arg.GetAccountKey()) if e != nil { return e } else if r != nil { @@ -105,9 +105,9 @@ func (rs *Responder) RefundIncrements(arg CallDescriptor, reply *float64) (err e if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.RefundIncrements") } else { - r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { + r, e := AccLock.Guard(func() (float64, error) { return arg.RefundIncrements() - }) + }, arg.GetAccountKey()) *reply, err = r, e } return @@ -117,10 +117,10 @@ func (rs *Responder) GetMaxSessionTime(arg CallDescriptor, reply *float64) (err if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.GetMaxSessionTime") } else { - r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { + r, e := AccLock.Guard(func() (float64, error) { d, err := arg.GetMaxSessionDuration() return float64(d), err - }) + }, arg.GetAccountKey()) *reply, err = r, e } return @@ -244,7 +244,7 @@ func (rs *Responder) ProcessCdr(cdr *StoredCdr, reply *string) error { func (rs *Responder) GetLCR(cd *CallDescriptor, reply *LCRCost) error { lcrCost, err := cd.GetLCR(rs.Stats) - *reply = *lcrCost + *reply = lcrCost return err } @@ -252,9 +252,9 @@ func (rs *Responder) FlushCache(arg CallDescriptor, reply *float64) (err error) if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.FlushCache") } else { - r, e := AccLock.Guard(arg.GetAccountKey(), func() (float64, error) { + r, e := AccLock.Guard(func() (float64, error) { return 0, arg.FlushCache() - }) + }, arg.GetAccountKey()) *reply, err = r, e } return @@ -295,10 +295,10 @@ func (rs *Responder) getCallCost(key *CallDescriptor, method string) (reply *Cal time.Sleep(1 * time.Second) // wait one second and retry } else { reply = &CallCost{} - reply, err = AccLock.GuardGetCost(key.GetAccountKey(), func() (*CallCost, error) { + reply, err = AccLock.GuardCallCost(func() (*CallCost, error) { err = client.Call(method, *key, reply) return reply, err - }) + }, key.GetAccountKey()) if err != nil { Logger.Err(fmt.Sprintf(" Got en error from rater: %v", err)) } @@ -318,10 +318,10 @@ func (rs *Responder) callMethod(key *CallDescriptor, method string) (reply float Logger.Info("Waiting for raters to register...") time.Sleep(1 * time.Second) // wait one second and retry } else { - reply, err = AccLock.Guard(key.GetAccountKey(), func() (float64, error) { + reply, err = AccLock.Guard(func() (float64, error) { err = client.Call(method, *key, &reply) return reply, err - }) + }, key.GetAccountKey()) if err != nil { Logger.Info(fmt.Sprintf("Got en error from rater: %v", err)) } diff --git a/utils/slice.go b/utils/slice.go index dc5432331..a496d66dc 100644 --- a/utils/slice.go +++ b/utils/slice.go @@ -41,3 +41,14 @@ func SliceMemberHasPrefix(ss []string, prfx string) bool { } return false } + +func Avg(values []float64) float64 { + if len(values) == 0 { + return 0.0 + } + var sum float64 + for _, val := range values { + sum += val + } + return sum / float64(len(values)) +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 077d96875..8fdabdb36 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -436,3 +436,21 @@ func TestConcatenatedKey(t *testing.T) { t.Error("Unexpected key value received: ", key) } } + +func TestAvg(t *testing.T) { + values := []float64{1, 2, 3} + result := Avg(values) + expected := 2.0 + if expected != result { + t.Errorf("Wrong Avg: expected %v got %v", expected, result) + } +} + +func TestAvgEmpty(t *testing.T) { + values := []float64{} + result := Avg(values) + expected := 0.0 + if expected != result { + t.Errorf("Wrong Avg: expected %v got %v", expected, result) + } +} From 53a4895683592a8356d20de86f8e4bdf0bc81751 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 3 Apr 2015 19:40:18 +0300 Subject: [PATCH 2/5] fix build and test on lcr qos sorter --- apier/v1/accounts.go | 16 +++++----- apier/v1/apier.go | 12 ++++---- apier/v2/apier.go | 4 +-- engine/lcr.go | 10 ++++-- engine/lcr_test.go | 69 ++++++++++++++++++++++++++++++++++++++++++ scheduler/scheduler.go | 4 +-- 6 files changed, 95 insertions(+), 20 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 62cddae7a..77359dbb9 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -80,7 +80,7 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } } - _, err := engine.AccLock.Guard(engine.ACTION_TIMING_PREFIX, func() (float64, error) { + _, err := engine.AccLock.Guard(func() (float64, error) { ats, err := self.AccountDb.GetActionTimings(attrs.ActionPlanId) if err != nil { return 0, err @@ -92,7 +92,7 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return 0, err } return 0, nil - }) + }, engine.ACTION_TIMING_PREFIX) if err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } @@ -130,7 +130,7 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } balanceId := utils.AccountKey(attrs.Tenant, attrs.Account, attrs.Direction) - _, err := engine.AccLock.Guard(balanceId, func() (float64, error) { + _, err := engine.AccLock.Guard(func() (float64, error) { ub, err := self.AccountDb.GetAccount(balanceId) if err != nil { return 0, err @@ -150,7 +150,7 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r return 0, err } return 0, nil - }) + }, balanceId) if err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } @@ -174,7 +174,7 @@ func (self *ApierV1) SetAccount(attr AttrSetAccount, reply *string) error { balanceId := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction) var ub *engine.Account var ats engine.ActionPlan - _, err := engine.AccLock.Guard(balanceId, func() (float64, error) { + _, err := engine.AccLock.Guard(func() (float64, error) { if bal, _ := self.AccountDb.GetAccount(balanceId); bal != nil { ub = bal } else { // Not found in db, create it here @@ -199,17 +199,17 @@ func (self *ApierV1) SetAccount(attr AttrSetAccount, reply *string) error { return 0, err } return 0, nil - }) + }, balanceId) if err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } if len(ats) != 0 { - _, err := engine.AccLock.Guard(engine.ACTION_TIMING_PREFIX, func() (float64, error) { // ToDo: Try locking it above on read somehow + _, err := engine.AccLock.Guard(func() (float64, error) { // ToDo: Try locking it above on read somehow if err := self.AccountDb.SetActionTimings(attr.ActionPlanId, ats); err != nil { return 0, err } return 0, nil - }) + }, engine.ACTION_TIMING_PREFIX) if err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } diff --git a/apier/v1/apier.go b/apier/v1/apier.go index e70e2e1b3..60f23ba34 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -588,7 +588,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string } tag := utils.AccountKey(attr.Tenant, attr.Account, attr.BalanceDirection) - _, err = engine.AccLock.Guard(tag, func() (float64, error) { + _, err = engine.AccLock.Guard(func() (float64, error) { userBalance, err := self.AccountDb.GetAccount(tag) if err != nil { return 0, err @@ -600,7 +600,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string return 0, err } return 0, nil - }) + }, tag) if err != nil { *reply = err.Error() return err @@ -658,7 +658,7 @@ func (self *ApierV1) ResetTriggeredActions(attr AttrResetTriggeredAction, reply } } accID := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction) - _, err := engine.AccLock.Guard(accID, func() (float64, error) { + _, err := engine.AccLock.Guard(func() (float64, error) { acc, err := self.AccountDb.GetAccount(accID) if err != nil { return 0, err @@ -670,7 +670,7 @@ func (self *ApierV1) ResetTriggeredActions(attr AttrResetTriggeredAction, reply return 0, err } return 0, nil - }) + }, accID) if err != nil { *reply = err.Error() return err @@ -685,12 +685,12 @@ func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *str return fmt.Errorf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, "TPid") } dbReader := engine.NewDbReader(self.StorDb, self.RatingDb, self.AccountDb, attrs.TPid) - if _, err := engine.AccLock.Guard(attrs.KeyId(), func() (float64, error) { + if _, err := engine.AccLock.Guard(func() (float64, error) { if err := dbReader.LoadAccountActionsFiltered(&attrs); err != nil { return 0, err } return 0, nil - }); err != nil { + }, attrs.KeyId()); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } // ToDo: Get the action keys loaded by dbReader so we reload only these in cache diff --git a/apier/v2/apier.go b/apier/v2/apier.go index 9e4efc059..f7f8e7d9f 100644 --- a/apier/v2/apier.go +++ b/apier/v2/apier.go @@ -73,12 +73,12 @@ func (self *ApierV2) LoadAccountActions(attrs AttrLoadAccountActions, reply *str tpAa := &utils.TPAccountActions{TPid: attrs.TPid} tpAa.SetAccountActionsId(attrs.AccountActionsId) - if _, err := engine.AccLock.Guard(attrs.AccountActionsId, func() (float64, error) { + if _, err := engine.AccLock.Guard(func() (float64, error) { if err := dbReader.LoadAccountActionsFiltered(tpAa); err != nil { return 0, err } return 0, nil - }); err != nil { + }, attrs.AccountActionsId); err != nil { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } // ToDo: Get the action keys loaded by dbReader so we reload only these in cache diff --git a/engine/lcr.go b/engine/lcr.go index e8bc0ba03..9bdd4f54c 100644 --- a/engine/lcr.go +++ b/engine/lcr.go @@ -229,7 +229,13 @@ func (qoss QOSSorter) Swap(i, j int) { } func (qoss QOSSorter) Less(i, j int) bool { - //for _, param := range qoss[i].qosSortParams - //qoss[i].Cost > qoss[j].Cost + for _, param := range qoss[i].qosSortParams { + if qoss[i].QOS[param] < qoss[j].QOS[param] { + return true + } + if qoss[i].QOS[param] == qoss[j].QOS[param] { + continue + } + } return false } diff --git a/engine/lcr_test.go b/engine/lcr_test.go index c17b24b37..cf4837ac7 100644 --- a/engine/lcr_test.go +++ b/engine/lcr_test.go @@ -17,3 +17,72 @@ along with this program. If not, see */ package engine + +import ( + "sort" + "testing" +) + +func TestLcrQOSSorter(t *testing.T) { + s := QOSSorter{ + &LCRSupplierCost{ + QOS: map[string]float64{ + "ASR": 3, + "ACD": 3, + }, + qosSortParams: []string{ASR, ACD}, + }, + &LCRSupplierCost{ + QOS: map[string]float64{ + "ASR": 1, + "ACD": 1, + }, + qosSortParams: []string{ASR, ACD}, + }, + &LCRSupplierCost{ + QOS: map[string]float64{ + "ASR": 2, + "ACD": 2, + }, + qosSortParams: []string{ASR, ACD}, + }, + } + sort.Sort(s) + if s[0].QOS[ASR] != 1 || + s[1].QOS[ASR] != 2 || + s[2].QOS[ASR] != 3 { + t.Error("Lcr qos sort failed: ", s) + } +} + +func TestLcrQOSSorterOACD(t *testing.T) { + s := QOSSorter{ + &LCRSupplierCost{ + QOS: map[string]float64{ + "ASR": 1, + "ACD": 3, + }, + qosSortParams: []string{ASR, ACD}, + }, + &LCRSupplierCost{ + QOS: map[string]float64{ + "ASR": 1, + "ACD": 1, + }, + qosSortParams: []string{ASR, ACD}, + }, + &LCRSupplierCost{ + QOS: map[string]float64{ + "ASR": 1, + "ACD": 2, + }, + qosSortParams: []string{ASR, ACD}, + }, + } + sort.Sort(s) + if s[0].QOS[ACD] != 1 || + s[1].QOS[ACD] != 2 || + s[2].QOS[ACD] != 3 { + t.Error("Lcr qos sort failed: ", s) + } +} diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 1a93ac91a..db8b01a8a 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -108,10 +108,10 @@ func (s *Scheduler) LoadActionTimings(storage engine.AccountingStorage) { newAts = append(newAts, at) } if toBeSaved { - engine.AccLock.Guard(engine.ACTION_TIMING_PREFIX, func() (float64, error) { + engine.AccLock.Guard(func() (float64, error) { storage.SetActionTimings(key, newAts) return 0, nil - }) + }, engine.ACTION_TIMING_PREFIX) } } sort.Sort(s.queue) From acf2b44fe3aa7743e82133dbe297deafe642f1a5 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 3 Apr 2015 21:21:14 +0300 Subject: [PATCH 3/5] simler lock for account guard --- engine/accountlock.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/engine/accountlock.go b/engine/accountlock.go index 5309f75b8..6a2a20ee8 100644 --- a/engine/accountlock.go +++ b/engine/accountlock.go @@ -30,7 +30,7 @@ func init() { type AccountLock struct { queue map[string]chan bool - mu sync.RWMutex + mu sync.Mutex } func NewAccountLock() *AccountLock { @@ -38,34 +38,30 @@ func NewAccountLock() *AccountLock { } func (cm *AccountLock) GuardCallCost(handler func() (*CallCost, error), name string) (reply *CallCost, err error) { - cm.mu.RLock() + cm.mu.Lock() lock, exists := AccLock.queue[name] - cm.mu.RUnlock() if !exists { - cm.mu.Lock() lock = make(chan bool, 1) AccLock.queue[name] = lock - cm.mu.Unlock() } lock <- true + cm.mu.Unlock() reply, err = handler() <-lock return } func (cm *AccountLock) Guard(handler func() (float64, error), names ...string) (reply float64, err error) { + cm.mu.Lock() for _, name := range names { - cm.mu.RLock() lock, exists := AccLock.queue[name] - cm.mu.RUnlock() if !exists { - cm.mu.Lock() lock = make(chan bool, 1) AccLock.queue[name] = lock - cm.mu.Unlock() } lock <- true } + cm.mu.Unlock() reply, err = handler() for _, name := range names { lock := AccLock.queue[name] From 8037f99cb8fced9995c38109444fba00926e264f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 3 Apr 2015 21:49:52 +0300 Subject: [PATCH 4/5] further account guard simplification --- apier/v1/accounts.go | 8 ++++---- apier/v1/apier.go | 6 +++--- apier/v2/apier.go | 2 +- engine/accountlock.go | 16 +--------------- engine/accountlock_test.go | 6 +++--- engine/action_timing.go | 2 +- engine/calldesc.go | 6 +++--- engine/responder.go | 29 ++++++++++++++--------------- scheduler/scheduler.go | 2 +- 9 files changed, 31 insertions(+), 46 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 77359dbb9..855bd3a24 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -80,7 +80,7 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } } - _, err := engine.AccLock.Guard(func() (float64, error) { + _, err := engine.AccLock.Guard(func() (interface{}, error) { ats, err := self.AccountDb.GetActionTimings(attrs.ActionPlanId) if err != nil { return 0, err @@ -130,7 +130,7 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) } balanceId := utils.AccountKey(attrs.Tenant, attrs.Account, attrs.Direction) - _, err := engine.AccLock.Guard(func() (float64, error) { + _, err := engine.AccLock.Guard(func() (interface{}, error) { ub, err := self.AccountDb.GetAccount(balanceId) if err != nil { return 0, err @@ -174,7 +174,7 @@ func (self *ApierV1) SetAccount(attr AttrSetAccount, reply *string) error { balanceId := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction) var ub *engine.Account var ats engine.ActionPlan - _, err := engine.AccLock.Guard(func() (float64, error) { + _, err := engine.AccLock.Guard(func() (interface{}, error) { if bal, _ := self.AccountDb.GetAccount(balanceId); bal != nil { ub = bal } else { // Not found in db, create it here @@ -204,7 +204,7 @@ func (self *ApierV1) SetAccount(attr AttrSetAccount, reply *string) error { return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } if len(ats) != 0 { - _, err := engine.AccLock.Guard(func() (float64, error) { // ToDo: Try locking it above on read somehow + _, err := engine.AccLock.Guard(func() (interface{}, error) { // ToDo: Try locking it above on read somehow if err := self.AccountDb.SetActionTimings(attr.ActionPlanId, ats); err != nil { return 0, err } diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 60f23ba34..9605763ff 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -588,7 +588,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string } tag := utils.AccountKey(attr.Tenant, attr.Account, attr.BalanceDirection) - _, err = engine.AccLock.Guard(func() (float64, error) { + _, err = engine.AccLock.Guard(func() (interface{}, error) { userBalance, err := self.AccountDb.GetAccount(tag) if err != nil { return 0, err @@ -658,7 +658,7 @@ func (self *ApierV1) ResetTriggeredActions(attr AttrResetTriggeredAction, reply } } accID := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction) - _, err := engine.AccLock.Guard(func() (float64, error) { + _, err := engine.AccLock.Guard(func() (interface{}, error) { acc, err := self.AccountDb.GetAccount(accID) if err != nil { return 0, err @@ -685,7 +685,7 @@ func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *str return fmt.Errorf("%s:%s", utils.ERR_MANDATORY_IE_MISSING, "TPid") } dbReader := engine.NewDbReader(self.StorDb, self.RatingDb, self.AccountDb, attrs.TPid) - if _, err := engine.AccLock.Guard(func() (float64, error) { + if _, err := engine.AccLock.Guard(func() (interface{}, error) { if err := dbReader.LoadAccountActionsFiltered(&attrs); err != nil { return 0, err } diff --git a/apier/v2/apier.go b/apier/v2/apier.go index f7f8e7d9f..f2d65b476 100644 --- a/apier/v2/apier.go +++ b/apier/v2/apier.go @@ -73,7 +73,7 @@ func (self *ApierV2) LoadAccountActions(attrs AttrLoadAccountActions, reply *str tpAa := &utils.TPAccountActions{TPid: attrs.TPid} tpAa.SetAccountActionsId(attrs.AccountActionsId) - if _, err := engine.AccLock.Guard(func() (float64, error) { + if _, err := engine.AccLock.Guard(func() (interface{}, error) { if err := dbReader.LoadAccountActionsFiltered(tpAa); err != nil { return 0, err } diff --git a/engine/accountlock.go b/engine/accountlock.go index 6a2a20ee8..18462122c 100644 --- a/engine/accountlock.go +++ b/engine/accountlock.go @@ -37,21 +37,7 @@ func NewAccountLock() *AccountLock { return &AccountLock{queue: make(map[string]chan bool)} } -func (cm *AccountLock) GuardCallCost(handler func() (*CallCost, error), name string) (reply *CallCost, err error) { - cm.mu.Lock() - lock, exists := AccLock.queue[name] - if !exists { - lock = make(chan bool, 1) - AccLock.queue[name] = lock - } - lock <- true - cm.mu.Unlock() - reply, err = handler() - <-lock - return -} - -func (cm *AccountLock) Guard(handler func() (float64, error), names ...string) (reply float64, err error) { +func (cm *AccountLock) Guard(handler func() (interface{}, error), names ...string) (reply interface{}, err error) { cm.mu.Lock() for _, name := range names { lock, exists := AccLock.queue[name] diff --git a/engine/accountlock_test.go b/engine/accountlock_test.go index db2193cd9..3c1635d01 100644 --- a/engine/accountlock_test.go +++ b/engine/accountlock_test.go @@ -25,19 +25,19 @@ import ( ) func ATestAccountLock(t *testing.T) { - go AccLock.Guard(func() (float64, error) { + go AccLock.Guard(func() (interface{}, error) { log.Print("first 1") time.Sleep(1 * time.Second) log.Print("end first 1") return 0, nil }, "1") - go AccLock.Guard(func() (float64, error) { + go AccLock.Guard(func() (interface{}, error) { log.Print("first 2") time.Sleep(1 * time.Second) log.Print("end first 2") return 0, nil }, "2") - go AccLock.Guard(func() (float64, error) { + go AccLock.Guard(func() (interface{}, error) { log.Print("second 1") time.Sleep(1 * time.Second) log.Print("end second 1") diff --git a/engine/action_timing.go b/engine/action_timing.go index dbc4b032c..f7f9c21a7 100644 --- a/engine/action_timing.go +++ b/engine/action_timing.go @@ -257,7 +257,7 @@ func (at *ActionTiming) Execute() (err error) { return } for _, ubId := range at.AccountIds { - _, err := AccLock.Guard(func() (float64, error) { + _, err := AccLock.Guard(func() (interface{}, error) { ub, err := accountingStorage.GetAccount(ubId) if err != nil { Logger.Warning(fmt.Sprintf("Could not get user balances for this id: %s. Skipping!", ubId)) diff --git a/engine/calldesc.go b/engine/calldesc.go index 809023ae8..0c61b104c 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -502,7 +502,7 @@ func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err e return 0, err } else { if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { - AccLock.Guard(func() (float64, error) { + AccLock.Guard(func() (interface{}, error) { duration, err = cd.getMaxSessionDuration(account) return 0, err }, memberIds...) @@ -551,7 +551,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { return nil, err } else { if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { - AccLock.Guard(func() (float64, error) { + AccLock.Guard(func() (interface{}, error) { cc, err = cd.debit(account, false, true) return 0, err }, memberIds...) @@ -573,7 +573,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { } else { //log.Printf("ACC: %+v", account) if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { - AccLock.Guard(func() (float64, error) { + AccLock.Guard(func() (interface{}, error) { remainingDuration, err := cd.getMaxSessionDuration(account) //log.Print("AFTER MAX SESSION: ", cd) if err != nil || remainingDuration == 0 { diff --git a/engine/responder.go b/engine/responder.go index d322da1eb..0489a6eb4 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -55,13 +55,13 @@ func (rs *Responder) GetCost(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.GetCost") *reply, err = *r, e } else { - r, e := AccLock.GuardCallCost(func() (*CallCost, error) { + r, e := AccLock.Guard(func() (interface{}, error) { return arg.GetCost() }, arg.GetAccountKey()) if e != nil { return e } else if r != nil { - *reply = *r + *reply = *r.(*CallCost) } } return @@ -72,13 +72,13 @@ func (rs *Responder) Debit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.Debit") *reply, err = *r, e } else { - r, e := AccLock.GuardCallCost(func() (*CallCost, error) { + r, e := AccLock.Guard(func() (interface{}, error) { return arg.Debit() }, arg.GetAccountKey()) if e != nil { return e } else if r != nil { - *reply = *r + *reply = *r.(*CallCost) } } return @@ -89,13 +89,13 @@ func (rs *Responder) MaxDebit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.MaxDebit") *reply, err = *r, e } else { - r, e := AccLock.GuardCallCost(func() (*CallCost, error) { + r, e := AccLock.Guard(func() (interface{}, error) { return arg.MaxDebit() }, arg.GetAccountKey()) if e != nil { return e } else if r != nil { - *reply = *r + *reply = *r.(*CallCost) } } return @@ -105,10 +105,10 @@ func (rs *Responder) RefundIncrements(arg CallDescriptor, reply *float64) (err e if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.RefundIncrements") } else { - r, e := AccLock.Guard(func() (float64, error) { + r, e := AccLock.Guard(func() (interface{}, error) { return arg.RefundIncrements() }, arg.GetAccountKey()) - *reply, err = r, e + *reply, err = r.(float64), e } return } @@ -117,11 +117,11 @@ func (rs *Responder) GetMaxSessionTime(arg CallDescriptor, reply *float64) (err if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.GetMaxSessionTime") } else { - r, e := AccLock.Guard(func() (float64, error) { + r, e := AccLock.Guard(func() (interface{}, error) { d, err := arg.GetMaxSessionDuration() return float64(d), err }, arg.GetAccountKey()) - *reply, err = r, e + *reply, err = r.(float64), e } return } @@ -252,10 +252,10 @@ func (rs *Responder) FlushCache(arg CallDescriptor, reply *float64) (err error) if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.FlushCache") } else { - r, e := AccLock.Guard(func() (float64, error) { + r, e := AccLock.Guard(func() (interface{}, error) { return 0, arg.FlushCache() }, arg.GetAccountKey()) - *reply, err = r, e + *reply, err = r.(float64), e } return } @@ -294,8 +294,7 @@ func (rs *Responder) getCallCost(key *CallDescriptor, method string) (reply *Cal Logger.Info(" Waiting for raters to register...") time.Sleep(1 * time.Second) // wait one second and retry } else { - reply = &CallCost{} - reply, err = AccLock.GuardCallCost(func() (*CallCost, error) { + _, err = AccLock.Guard(func() (interface{}, error) { err = client.Call(method, *key, reply) return reply, err }, key.GetAccountKey()) @@ -318,7 +317,7 @@ func (rs *Responder) callMethod(key *CallDescriptor, method string) (reply float Logger.Info("Waiting for raters to register...") time.Sleep(1 * time.Second) // wait one second and retry } else { - reply, err = AccLock.Guard(func() (float64, error) { + _, err = AccLock.Guard(func() (interface{}, error) { err = client.Call(method, *key, &reply) return reply, err }, key.GetAccountKey()) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index db8b01a8a..1156778dc 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -108,7 +108,7 @@ func (s *Scheduler) LoadActionTimings(storage engine.AccountingStorage) { newAts = append(newAts, at) } if toBeSaved { - engine.AccLock.Guard(func() (float64, error) { + engine.AccLock.Guard(func() (interface{}, error) { storage.SetActionTimings(key, newAts) return 0, nil }, engine.ACTION_TIMING_PREFIX) From eb60e6caf59cebce071bbdd96486b076000458cb Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 3 Apr 2015 22:33:16 +0300 Subject: [PATCH 5/5] fix for the lock issue --- engine/account.go | 20 ++++++++------------ engine/accountlock.go | 11 ++--------- engine/calldesc.go | 6 +++--- engine/responder.go | 19 ++++++------------- engine/sharedgroup.go | 12 ------------ engine/sharedgroup_test.go | 19 +------------------ 6 files changed, 20 insertions(+), 67 deletions(-) diff --git a/engine/account.go b/engine/account.go index 3df7f04e4..293d5bdd6 100644 --- a/engine/account.go +++ b/engine/account.go @@ -566,19 +566,15 @@ func (ub *Account) GetSharedGroups() (groups []string) { return } -func (account *Account) GetUniqueSharedGroupMembers(destination, direction, category, unitType string) ([]string, error) { - creditBalances := account.getBalancesForPrefix(destination, category, account.BalanceMap[CREDIT+direction], "") - unitBalances := account.getBalancesForPrefix(destination, category, account.BalanceMap[unitType+direction], "") +func (account *Account) GetUniqueSharedGroupMembers(cd *CallDescriptor) ([]string, error) { + var balances []*Balance + balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, account.BalanceMap[CREDIT+cd.Direction], "")...) + balances = append(balances, account.getBalancesForPrefix(cd.Destination, cd.Category, account.BalanceMap[cd.TOR+cd.Direction], "")...) // gather all shared group ids var sharedGroupIds []string - for _, cb := range creditBalances { - if cb.SharedGroup != "" { - sharedGroupIds = append(sharedGroupIds, cb.SharedGroup) - } - } - for _, mb := range unitBalances { - if mb.SharedGroup != "" { - sharedGroupIds = append(sharedGroupIds, mb.SharedGroup) + for _, b := range balances { + if b.SharedGroup != "" { + sharedGroupIds = append(sharedGroupIds, b.SharedGroup) } } var memberIds []string @@ -588,7 +584,7 @@ func (account *Account) GetUniqueSharedGroupMembers(destination, direction, cate Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) return nil, err } - for _, memberId := range sharedGroup.GetMembersExceptUser(account.Id) { + for _, memberId := range sharedGroup.MemberIds { if !utils.IsSliceMember(memberIds, memberId) { memberIds = append(memberIds, memberId) } diff --git a/engine/accountlock.go b/engine/accountlock.go index 18462122c..5184ce8f2 100644 --- a/engine/accountlock.go +++ b/engine/accountlock.go @@ -22,21 +22,14 @@ import ( "sync" ) -var AccLock *AccountLock - -func init() { - AccLock = NewAccountLock() -} +// global package variable +var AccLock = &AccountLock{queue: make(map[string]chan bool)} type AccountLock struct { queue map[string]chan bool mu sync.Mutex } -func NewAccountLock() *AccountLock { - return &AccountLock{queue: make(map[string]chan bool)} -} - func (cm *AccountLock) Guard(handler func() (interface{}, error), names ...string) (reply interface{}, err error) { cm.mu.Lock() for _, name := range names { diff --git a/engine/calldesc.go b/engine/calldesc.go index 0c61b104c..50f4dd8c4 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -501,7 +501,7 @@ func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err e Logger.Err(fmt.Sprintf("Could not get user balance for <%s>: %s.", cd.GetAccountKey(), err.Error())) return 0, err } else { - if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { + if memberIds, err := account.GetUniqueSharedGroupMembers(cd); err == nil { AccLock.Guard(func() (interface{}, error) { duration, err = cd.getMaxSessionDuration(account) return 0, err @@ -550,7 +550,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { Logger.Err(fmt.Sprintf("Could not get user balance for <%s>: %s.", cd.GetAccountKey(), err.Error())) return nil, err } else { - if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { + if memberIds, err := account.GetUniqueSharedGroupMembers(cd); err == nil { AccLock.Guard(func() (interface{}, error) { cc, err = cd.debit(account, false, true) return 0, err @@ -572,7 +572,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { return nil, err } else { //log.Printf("ACC: %+v", account) - if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction, cd.Category, cd.TOR); err == nil { + if memberIds, err := account.GetUniqueSharedGroupMembers(cd); err == nil { AccLock.Guard(func() (interface{}, error) { remainingDuration, err := cd.getMaxSessionDuration(account) //log.Print("AFTER MAX SESSION: ", cd) diff --git a/engine/responder.go b/engine/responder.go index 0489a6eb4..cdfcd3b66 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -72,13 +72,11 @@ func (rs *Responder) Debit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.Debit") *reply, err = *r, e } else { - r, e := AccLock.Guard(func() (interface{}, error) { - return arg.Debit() - }, arg.GetAccountKey()) + r, e := arg.Debit() if e != nil { return e } else if r != nil { - *reply = *r.(*CallCost) + *reply = *r } } return @@ -89,13 +87,11 @@ func (rs *Responder) MaxDebit(arg CallDescriptor, reply *CallCost) (err error) { r, e := rs.getCallCost(&arg, "Responder.MaxDebit") *reply, err = *r, e } else { - r, e := AccLock.Guard(func() (interface{}, error) { - return arg.MaxDebit() - }, arg.GetAccountKey()) + r, e := arg.MaxDebit() if e != nil { return e } else if r != nil { - *reply = *r.(*CallCost) + *reply = *r } } return @@ -117,11 +113,8 @@ func (rs *Responder) GetMaxSessionTime(arg CallDescriptor, reply *float64) (err if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.GetMaxSessionTime") } else { - r, e := AccLock.Guard(func() (interface{}, error) { - d, err := arg.GetMaxSessionDuration() - return float64(d), err - }, arg.GetAccountKey()) - *reply, err = r.(float64), e + r, e := arg.GetMaxSessionDuration() + *reply, err = float64(r), e } return } diff --git a/engine/sharedgroup.go b/engine/sharedgroup.go index abbdd2000..dc6295231 100644 --- a/engine/sharedgroup.go +++ b/engine/sharedgroup.go @@ -49,18 +49,6 @@ type SharingParameters struct { RatingSubject string } -func (sg *SharedGroup) GetMembersExceptUser(ubId string) []string { - for i, m := range sg.MemberIds { - if m == ubId { - a := make([]string, len(sg.MemberIds)) - copy(a, sg.MemberIds) - a[i], a = a[len(a)-1], a[:len(a)-1] - return a - } - } - return sg.MemberIds -} - func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc BalanceChain) BalanceChain { sharingParameters := sg.AccountParameters[utils.ANY] if sp, hasParamsForAccount := sg.AccountParameters[myBalance.account.Id]; hasParamsForAccount { diff --git a/engine/sharedgroup_test.go b/engine/sharedgroup_test.go index d5565f0ed..a521028de 100644 --- a/engine/sharedgroup_test.go +++ b/engine/sharedgroup_test.go @@ -18,24 +18,7 @@ along with this program. If not, see package engine -import ( - "reflect" - "testing" -) - -func TestSharedGroupGetMembersExcept(t *testing.T) { - sg := &SharedGroup{ - MemberIds: []string{"1", "2", "3"}, - } - a1 := sg.GetMembersExceptUser("1") - a2 := sg.GetMembersExceptUser("2") - a3 := sg.GetMembersExceptUser("3") - if !reflect.DeepEqual(a1, []string{"3", "2"}) || - !reflect.DeepEqual(a2, []string{"1", "3"}) || - !reflect.DeepEqual(a3, []string{"1", "2"}) { - t.Error("Error getting shared group members: ", a1, a2, a3) - } -} +import "testing" func TestSharedPopBalanceByStrategyLow(t *testing.T) { bc := BalanceChain{