mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
started adding test for shared group and created utility methods for
getting shared balances
This commit is contained in:
@@ -68,3 +68,21 @@ func (cm *AccountLock) Guard(name string, handler func() (float64, error)) (repl
|
||||
<-lock
|
||||
return
|
||||
}
|
||||
|
||||
func (cm *AccountLock) GuardMany(names []string, handler func() (float64, error)) (reply float64, err error) {
|
||||
for _, name := range names {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -18,6 +18,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
STRATEGY_LOWEST_FIRST = "*lowest_first"
|
||||
STRATEGY_HIGHEST_FIRST = "*highest_first"
|
||||
STRATEGY_RANDOM = "*random"
|
||||
)
|
||||
|
||||
type SharedGroup struct {
|
||||
Id string
|
||||
Strategy string
|
||||
@@ -26,3 +38,46 @@ type SharedGroup struct {
|
||||
Weight float64
|
||||
Members []string
|
||||
}
|
||||
|
||||
func (sg *SharedGroup) GetMembersExceptUser(ubId string) []string {
|
||||
for i, m := range sg.Members {
|
||||
if m == ubId {
|
||||
a := make([]string, len(sg.Members))
|
||||
copy(a, sg.Members)
|
||||
a[i], a = a[len(a)-1], a[:len(a)-1]
|
||||
return a
|
||||
}
|
||||
}
|
||||
return sg.Members
|
||||
}
|
||||
|
||||
func (sg *SharedGroup) PopBalanceByStrategy(balanceChain *BalanceChain) (bal *Balance) {
|
||||
bc := *balanceChain
|
||||
if len(bc) == 0 {
|
||||
return
|
||||
}
|
||||
index := 0
|
||||
switch sg.Strategy {
|
||||
case STRATEGY_RANDOM:
|
||||
rand.Seed(time.Now().Unix())
|
||||
index = rand.Intn(len(bc))
|
||||
case STRATEGY_LOWEST_FIRST:
|
||||
minVal := math.MaxFloat64
|
||||
for i, b := range bc {
|
||||
if b.Value < minVal {
|
||||
minVal = b.Value
|
||||
index = i
|
||||
}
|
||||
}
|
||||
case STRATEGY_HIGHEST_FIRST:
|
||||
maxVal := math.SmallestNonzeroFloat64
|
||||
for i, b := range bc {
|
||||
if b.Value > maxVal {
|
||||
maxVal = b.Value
|
||||
index = i
|
||||
}
|
||||
}
|
||||
}
|
||||
bal, bc[index], *balanceChain = bc[index], bc[len(bc)-1], bc[:len(bc)-1]
|
||||
return
|
||||
}
|
||||
|
||||
75
engine/sharedgroup_test.go
Normal file
75
engine/sharedgroup_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2013 ITsysCOM
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSharedGroupGetMembersExcept(t *testing.T) {
|
||||
sg := &SharedGroup{
|
||||
Members: []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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSharedPopBalanceByStrategyLow(t *testing.T) {
|
||||
bc := BalanceChain{
|
||||
&Balance{Value: 2.0},
|
||||
&Balance{Value: 1.0},
|
||||
&Balance{Value: 3.0},
|
||||
}
|
||||
sg := &SharedGroup{Strategy: STRATEGY_LOWEST_FIRST}
|
||||
b := sg.PopBalanceByStrategy(&bc)
|
||||
if b.Value != 1.0 {
|
||||
t.Error("Error popping the right balance according to strategy: ", b, bc)
|
||||
}
|
||||
if len(bc) != 2 ||
|
||||
bc[0].Value != 2.0 ||
|
||||
bc[1].Value != 3.0 {
|
||||
t.Error("Error removing balance from chain: ", bc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedPopBalanceByStrategyHigh(t *testing.T) {
|
||||
bc := BalanceChain{
|
||||
&Balance{Value: 2.0},
|
||||
&Balance{Value: 1.0},
|
||||
&Balance{Value: 3.0},
|
||||
}
|
||||
sg := &SharedGroup{Strategy: STRATEGY_HIGHEST_FIRST}
|
||||
b := sg.PopBalanceByStrategy(&bc)
|
||||
if b.Value != 3.0 {
|
||||
t.Error("Error popping the right balance according to strategy: ", b, bc)
|
||||
}
|
||||
if len(bc) != 2 ||
|
||||
bc[0].Value != 2.0 ||
|
||||
bc[1].Value != 1.0 {
|
||||
t.Error("Error removing balance from chain: ", bc)
|
||||
}
|
||||
}
|
||||
@@ -189,31 +189,7 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
|
||||
} else {
|
||||
// chack if it's shared
|
||||
if balance.SharedGroup != "" {
|
||||
sharedGroup, err := accountingStorage.GetSharedGroup(balance.SharedGroup, false)
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("Could not get shared group: %s", balance.SharedGroup))
|
||||
continue
|
||||
}
|
||||
for _, ubId := range sharedGroup.Members {
|
||||
if ubId == ub.Id { // skip the initiating user
|
||||
continue
|
||||
}
|
||||
AccLock.Guard(ubId, func() (float64, error) {
|
||||
nUb, err := accountingStorage.GetUserBalance(ubId)
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId))
|
||||
}
|
||||
sharedMinuteBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[MINUTES+cc.Direction], balance.SharedGroup)
|
||||
sharedMoneyBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[CREDIT+cc.Direction], balance.SharedGroup)
|
||||
for _, sharedBalance := range sharedMinuteBalances {
|
||||
// FIXME: insert money balances after users balances
|
||||
allMoneyBalances := append(usefulMoneyBalances, sharedMoneyBalances)
|
||||
sharedBalance.DebitMinutes(cc, count, nUb, allMoneyBalances)
|
||||
// FIXME: save nUb
|
||||
}
|
||||
return 0, nil
|
||||
})
|
||||
}
|
||||
ub.debitFromSharedBalances(balance.SharedGroup, cc, usefulMoneyBalances, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,6 +241,35 @@ func (ub *UserBalance) debitCreditBalance(cc *CallCost, count bool) error {
|
||||
return returnError
|
||||
}
|
||||
|
||||
func (ub *UserBalance) debitFromSharedBalances(sharedGroupName string, cc *CallCost, moneyBalances BalanceChain, count bool) {
|
||||
sharedGroup, err := accountingStorage.GetSharedGroup(sharedGroupName, false)
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("Could not get shared group: %s", sharedGroup))
|
||||
return
|
||||
}
|
||||
sharingMembers := sharedGroup.GetMembersExceptUser(ub.Id)
|
||||
AccLock.GuardMany(sharingMembers, func() (float64, error) {
|
||||
for _, ubId := range sharingMembers {
|
||||
if ubId == ub.Id { // skip the initiating user
|
||||
continue
|
||||
}
|
||||
|
||||
nUb, err := accountingStorage.GetUserBalance(ubId)
|
||||
if err != nil {
|
||||
Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId))
|
||||
}
|
||||
sharedMinuteBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[MINUTES+cc.Direction], sharedGroupName)
|
||||
sharedMoneyBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[CREDIT+cc.Direction], sharedGroupName)
|
||||
for _, sharedBalance := range sharedMinuteBalances {
|
||||
allMoneyBalances := append(moneyBalances, sharedMoneyBalances...)
|
||||
sharedBalance.DebitMinutes(cc, count, nUb, allMoneyBalances)
|
||||
accountingStorage.SetUserBalance(nUb)
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ub *UserBalance) GetDefaultMoneyBalance(direction string) *Balance {
|
||||
for _, balance := range ub.BalanceMap[CREDIT+direction] {
|
||||
if balance.IsDefault() {
|
||||
|
||||
Reference in New Issue
Block a user