max session caclulus to consider shared groups

This commit is contained in:
Radu Ioan Fericean
2014-03-19 19:09:25 +02:00
parent 315eddb63f
commit 19b7d0beb7
6 changed files with 113 additions and 78 deletions

View File

@@ -72,8 +72,35 @@ type Account struct {
// Returns user's available minutes for the specified destination
func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances BalanceChain) {
credit = ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[CREDIT+cd.Direction], "").GetTotalValue()
balances = ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[MINUTES+cd.Direction], "")
creditBalances := ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[CREDIT+cd.Direction], "")
minuteBalances := ub.getBalancesForPrefix(cd.Destination, ub.BalanceMap[MINUTES+cd.Direction], "")
// gather all balances from shared groups
var extendedCreditBalances BalanceChain
for _, cb := range creditBalances {
if cb.SharedGroup != "" {
if sharedGroup, _ := accountingStorage.GetSharedGroup(cb.SharedGroup, false); sharedGroup != nil {
sgb := sharedGroup.GetBalances(cd.Destination, CREDIT+cd.Direction, ub)
sgb = sharedGroup.SortBalancesByStrategy(cb, sgb)
extendedCreditBalances = append(extendedCreditBalances, sgb...)
}
} else {
extendedCreditBalances = append(extendedCreditBalances, cb)
}
}
var extendedMinuteBalances BalanceChain
for _, mb := range minuteBalances {
if mb.SharedGroup != "" {
if sharedGroup, _ := accountingStorage.GetSharedGroup(mb.SharedGroup, false); sharedGroup != nil {
sgb := sharedGroup.GetBalances(cd.Destination, MINUTES+cd.Direction, ub)
sgb = sharedGroup.SortBalancesByStrategy(mb, sgb)
extendedMinuteBalances = append(extendedMinuteBalances, sgb...)
}
} else {
extendedMinuteBalances = append(extendedMinuteBalances, mb)
}
}
credit = extendedCreditBalances.GetTotalValue()
balances = extendedMinuteBalances
for _, b := range balances {
d, c := b.GetMinutesForCredit(cd, credit)
credit = c
@@ -119,7 +146,7 @@ func (ub *Account) debitBalanceAction(a *Action) error {
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", a.Balance.SharedGroup))
} else {
// add membere and save
sg.Members = append(sg.Members, ub.Id)
sg.MemberIds = append(sg.MemberIds, ub.Id)
accountingStorage.SetSharedGroup(sg.Id, sg)
}
}
@@ -268,27 +295,8 @@ func (ub *Account) debitMinutesFromSharedBalances(myBalance *Balance, cc *CallCo
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sharedGroupName))
return
}
sharingMembers := sharedGroup.Members
var allMinuteSharedBalances BalanceChain
for _, ubId := range sharingMembers {
var nUb *Account
if ubId == ub.Id { // skip the initiating user
nUb = ub
} else {
nUb, err = accountingStorage.GetAccount(ubId)
if err != nil {
Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId))
}
if nUb.Disabled {
Logger.Warning(fmt.Sprintf("Disabled user in shared group: %s (%s)", ubId, sharedGroupName))
continue
}
}
sharedMinuteBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[MINUTES+cc.Direction], sharedGroupName)
allMinuteSharedBalances = append(allMinuteSharedBalances, sharedMinuteBalances...)
}
for _, sharedBalance := range sharedGroup.GetBalancesByStrategy(myBalance, allMinuteSharedBalances) {
allMinuteSharedBalances := sharedGroup.GetBalances(cc.Destination, MINUTES+cc.Direction, ub)
for _, sharedBalance := range sharedGroup.SortBalancesByStrategy(myBalance, allMinuteSharedBalances) {
initialValue := sharedBalance.Value
sharedBalance.DebitMinutes(cc, count, sharedBalance.account, moneyBalances)
if sharedBalance.Value != initialValue {
@@ -308,26 +316,8 @@ func (ub *Account) debitMoneyFromSharedBalances(myBalance *Balance, cc *CallCost
Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sharedGroup))
return
}
sharingMembers := sharedGroup.Members
var allMoneySharedBalances BalanceChain
for _, ubId := range sharingMembers {
var nUb *Account
if ubId == ub.Id { // skip the initiating user
nUb = ub
} else {
nUb, err = accountingStorage.GetAccount(ubId)
if err != nil {
Logger.Warning(fmt.Sprintf("Could not get user balance: %s", ubId))
}
if nUb.Disabled {
Logger.Warning(fmt.Sprintf("Disabled user in shared group: %s (%s)", ubId, sharedGroupName))
continue
}
}
sharedMoneyBalances := nUb.getBalancesForPrefix(cc.Destination, nUb.BalanceMap[CREDIT+cc.Direction], sharedGroupName)
allMoneySharedBalances = append(allMoneySharedBalances, sharedMoneyBalances...)
}
for _, sharedBalance := range sharedGroup.GetBalancesByStrategy(myBalance, allMoneySharedBalances) {
allMoneySharedBalances := sharedGroup.GetBalances(cc.Destination, CREDIT+cc.Direction, ub)
for _, sharedBalance := range sharedGroup.SortBalancesByStrategy(myBalance, allMoneySharedBalances) {
initialValue := sharedBalance.Value
sharedBalance.DebitMoney(cc, count, sharedBalance.account)
if sharedBalance.Value != initialValue {

View File

@@ -1058,7 +1058,7 @@ func TestDebitShared(t *testing.T) {
CREDIT + OUTBOUND: BalanceChain{&Balance{Uuid: "moneyc", Value: 70, SharedGroup: "SG_TEST"}},
}}
sg := &SharedGroup{Id: "SG_TEST", Members: []string{rif.Id, groupie.Id}, AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}}
sg := &SharedGroup{Id: "SG_TEST", MemberIds: []string{rif.Id, groupie.Id}, AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}}
accountingStorage.SetAccount(groupie)
accountingStorage.SetSharedGroup("SG_TEST", sg)

View File

@@ -115,7 +115,7 @@ type CallDescriptor struct {
FallbackSubject string // the subject to check for destination if not found on primary subject
RatingInfos RatingInfos
Increments Increments
userBalance *Account
account *Account
}
func (cd *CallDescriptor) ValidateCallData() error {
@@ -148,13 +148,13 @@ func (cd *CallDescriptor) GetAccountKey() string {
// Gets and caches the user balance information.
func (cd *CallDescriptor) getAccount() (ub *Account, err error) {
if cd.userBalance == nil {
cd.userBalance, err = accountingStorage.GetAccount(cd.GetAccountKey())
if cd.account == nil {
cd.account, err = accountingStorage.GetAccount(cd.GetAccountKey())
}
if cd.userBalance != nil && cd.userBalance.Disabled {
if cd.account != nil && cd.account.Disabled {
return nil, fmt.Errorf("User %s is disabled", ub.Id)
}
return cd.userBalance, err
return cd.account, err
}
/*
@@ -496,13 +496,20 @@ func (origCD *CallDescriptor) getMaxSessionDuration(account *Account) (time.Dura
return utils.MinDuration(initialDuration, availableDuration), nil
}
func (origCD *CallDescriptor) GetMaxSessionDuration() (time.Duration, error) {
cd := origCD.Clone()
func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err error) {
if account, err := cd.getAccount(); err != nil || account == nil {
Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error()))
return 0, err
} else {
return cd.getMaxSessionDuration(account)
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction); err == nil {
AccLock.GuardMany(memberIds, func() (float64, error) {
duration, err = cd.getMaxSessionDuration(account)
return 0, err
})
} else {
return 0, err
}
return duration, err
}
}
@@ -563,15 +570,24 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) {
Logger.Err(fmt.Sprintf("Could not get user balance for %s: %s.", cd.GetAccountKey(), err.Error()))
return nil, err
} else {
remainingDuration, err := cd.getMaxSessionDuration(account)
if err != nil || remainingDuration == 0 {
return new(CallCost), fmt.Errorf("no more credit: %v", err)
if memberIds, err := account.GetUniqueSharedGroupMembers(cd.Destination, cd.Direction); err == nil {
AccLock.GuardMany(memberIds, func() (float64, error) {
remainingDuration, err := cd.getMaxSessionDuration(account)
if err != nil || remainingDuration == 0 {
cc, err = new(CallCost), fmt.Errorf("no more credit: %v", err)
return 0, err
}
if remainingDuration > 0 { // for postpaying client returns -1
cd.TimeEnd = cd.TimeStart.Add(remainingDuration)
}
cc, err = cd.debit(account)
return 0, err
})
} else {
return nil, err
}
if remainingDuration > 0 { // for postpaying client returns -1
cd.TimeEnd = cd.TimeStart.Add(remainingDuration)
}
return cd.debit(account)
}
return cc, err
}
func (cd *CallDescriptor) RefundIncrements() (left float64, err error) {

View File

@@ -314,7 +314,7 @@ func TestMaxSessionTimeWithAccount(t *testing.T) {
Tenant: "vdf",
Subject: "minu",
Destination: "0723",
Amount: 1000}
}
result, err := cd.GetMaxSessionDuration()
expected := time.Minute
if result != expected || err != nil {
@@ -330,8 +330,9 @@ func TestMaxSessionTimeWithAccountAlias(t *testing.T) {
TOR: "0",
Tenant: "vdf",
Subject: "a1",
Account: "a1",
Destination: "0723",
Amount: 1000}
}
result, err := cd.GetMaxSessionDuration()
expected := time.Minute
if result != expected || err != nil {
@@ -389,6 +390,7 @@ func TestMaxSessionModifiesCallDesc(t *testing.T) {
Amount: 5400}
initial := cd.Clone()
cd.GetMaxSessionDuration()
cd.account = nil // it's OK to cache the account
if !reflect.DeepEqual(cd, initial) {
t.Errorf("GetMaxSessionDuration is changing the call descriptor %+v != %+v", cd, initial)
}

View File

@@ -40,7 +40,8 @@ const (
type SharedGroup struct {
Id string
AccountParameters map[string]*SharingParameters
Members []string
MemberIds []string
members []*Account // accounts caching
}
type SharingParameters struct {
@@ -49,18 +50,18 @@ type SharingParameters struct {
}
func (sg *SharedGroup) GetMembersExceptUser(ubId string) []string {
for i, m := range sg.Members {
for i, m := range sg.MemberIds {
if m == ubId {
a := make([]string, len(sg.Members))
copy(a, sg.Members)
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.Members
return sg.MemberIds
}
func (sg *SharedGroup) GetBalancesByStrategy(myBalance *Balance, bc BalanceChain) BalanceChain {
func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc BalanceChain) BalanceChain {
sharingParameters := sg.AccountParameters[utils.ANY]
if sp, hasParamsForAccount := sg.AccountParameters[myBalance.account.Id]; hasParamsForAccount {
sharingParameters = sp
@@ -100,6 +101,32 @@ func (sg *SharedGroup) GetBalancesByStrategy(myBalance *Balance, bc BalanceChain
return bc
}
// Returns all shared group's balances collected from user accounts'
func (sg *SharedGroup) GetBalances(destination, balanceType string, ub *Account) (bc BalanceChain) {
if len(sg.members) == 0 {
for _, ubId := range sg.MemberIds {
var nUb *Account
if ubId == ub.Id { // skip the initiating user
nUb = ub
} else {
nUb, _ = accountingStorage.GetAccount(ubId)
if nUb == nil || nUb.Disabled {
continue
}
}
sg.members = append(sg.members, nUb)
sb := nUb.getBalancesForPrefix(destination, nUb.BalanceMap[balanceType], sg.Id)
bc = append(bc, sb...)
}
} else {
for _, m := range sg.members {
sb := m.getBalancesForPrefix(destination, m.BalanceMap[balanceType], sg.Id)
bc = append(bc, sb...)
}
}
return
}
type LowestBalanceChainSorter []*Balance
func (lbcs LowestBalanceChainSorter) Len() int {

View File

@@ -25,7 +25,7 @@ import (
func TestSharedGroupGetMembersExcept(t *testing.T) {
sg := &SharedGroup{
Members: []string{"1", "2", "3"},
MemberIds: []string{"1", "2", "3"},
}
a1 := sg.GetMembersExceptUser("1")
a2 := sg.GetMembersExceptUser("2")
@@ -46,7 +46,7 @@ func TestSharedPopBalanceByStrategyLow(t *testing.T) {
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
"test": &SharingParameters{Strategy: STRATEGY_LOWEST}},
}
sbc := sg.GetBalancesByStrategy(bc[1], bc)
sbc := sg.SortBalancesByStrategy(bc[1], bc)
if len(sbc) != 3 ||
sbc[0].Value != 1.0 ||
sbc[1].Value != 2.0 {
@@ -63,7 +63,7 @@ func TestSharedPopBalanceByStrategyHigh(t *testing.T) {
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
"test": &SharingParameters{Strategy: STRATEGY_HIGHEST}},
}
sbc := sg.GetBalancesByStrategy(bc[0], bc)
sbc := sg.SortBalancesByStrategy(bc[0], bc)
if len(sbc) != 3 ||
sbc[0].Value != 3.0 ||
sbc[1].Value != 2.0 {
@@ -80,7 +80,7 @@ func TestSharedPopBalanceByStrategyMineHigh(t *testing.T) {
sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{
"test": &SharingParameters{Strategy: STRATEGY_MINE_HIGHEST}},
}
sbc := sg.GetBalancesByStrategy(bc[0], bc)
sbc := sg.SortBalancesByStrategy(bc[0], bc)
if len(sbc) != 3 ||
sbc[0].Value != 2.0 ||
sbc[1].Value != 3.0 {
@@ -88,7 +88,7 @@ func TestSharedPopBalanceByStrategyMineHigh(t *testing.T) {
}
}
func TestSharedPopBalanceByStrategyRandomHigh(t *testing.T) {
/*func TestSharedPopBalanceByStrategyRandomHigh(t *testing.T) {
bc := BalanceChain{
&Balance{Uuid: "uuuu", Value: 2.0, account: &Account{Id: "test"}},
&Balance{Value: 1.0},
@@ -98,15 +98,15 @@ func TestSharedPopBalanceByStrategyRandomHigh(t *testing.T) {
"test": &SharingParameters{Strategy: STRATEGY_RANDOM}},
}
x := bc[0]
sbc := sg.GetBalancesByStrategy(bc[0], bc)
sbc := sg.SortBalancesByStrategy(bc[0], bc)
firstTest := (sbc[0].Uuid == x.Uuid)
sbc = sg.GetBalancesByStrategy(bc[0], bc)
sbc = sg.SortBalancesByStrategy(bc[0], bc)
secondTest := (sbc[0].Uuid == x.Uuid)
sbc = sg.GetBalancesByStrategy(bc[0], bc)
sbc = sg.SortBalancesByStrategy(bc[0], bc)
thirdTest := (sbc[0].Uuid == x.Uuid)
sbc = sg.GetBalancesByStrategy(bc[0], bc)
sbc = sg.SortBalancesByStrategy(bc[0], bc)
fourthTest := (sbc[0].Uuid == x.Uuid)
if firstTest && secondTest && thirdTest && fourthTest {
t.Error("Something is wrong with balance randomizer")
}
}
}*/