mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-15 13:19:53 +05:00
debit buget functions operational
This commit is contained in:
13
README
13
README
@@ -1,10 +1,9 @@
|
||||
Rating system designed to be used in VoIP Carriers World.
|
||||
|
||||
Features
|
||||
- Fast RatingEngine, holds all rating information into memory
|
||||
- Unlimited spanned timebased rating: considers both ActiveFrom/RateStartsAt
|
||||
- Unlimited rate profiles chanining for fallback
|
||||
- Rating Fields: ConnectFee, RateIn, RateOut
|
||||
- High accuracy rating: configurable to miliseconds
|
||||
- Flexible rates storage - plugin based rates loading
|
||||
- Flexibility to rate various information from the cdrs as rating subject.
|
||||
* Fast RatingEngine, holds all rating information into memory
|
||||
* Unlimited spanned timebased rating: considers both ActiveFrom/RateStartsAt
|
||||
* Unlimited rate profiles chanining for fallback
|
||||
* Rating Fields: ConnectFee, RateIn, RateOut
|
||||
* High accuracy rating: configurable to miliseconds
|
||||
* Flexible rates storage - plugin based rates loading
|
||||
|
||||
@@ -28,7 +28,7 @@ RPC method providing the rating information from the storage.
|
||||
*/
|
||||
func (s *Storage) GetCost(cd timespans.CallDescriptor, reply *timespans.CallCost) (err error) {
|
||||
descriptor := &cd
|
||||
descriptor.storageGetter = s.sg
|
||||
descriptor.StorageGetter = s.sg
|
||||
r, e := descriptor.GetCost()
|
||||
*reply, err = *r, e
|
||||
return nil
|
||||
|
||||
@@ -34,12 +34,14 @@ func main() {
|
||||
|
||||
getter, _ := timespans.NewRedisStorage("", 10)
|
||||
defer getter.Close()
|
||||
|
||||
|
||||
cd.StorageGetter = getter
|
||||
|
||||
i := 0
|
||||
log.Printf("Runnning %d cycles...", *runs)
|
||||
|
||||
for j := 0; j < *runs; j++ {
|
||||
result, _ = cd.GetCost(getter)
|
||||
result, _ = cd.GetCost()
|
||||
}
|
||||
|
||||
log.Print(result)
|
||||
|
||||
@@ -37,7 +37,7 @@ type CallDescriptor struct {
|
||||
CstmId, Subject, DestinationPrefix string
|
||||
TimeStart, TimeEnd time.Time
|
||||
ActivationPeriods []*ActivationPeriod
|
||||
storageGetter StorageGetter
|
||||
StorageGetter StorageGetter
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -57,10 +57,10 @@ func (cd *CallDescriptor) RestoreFromStorage() (destPrefix string, err error) {
|
||||
base := fmt.Sprintf("%s:%s:", cd.CstmId, cd.Subject)
|
||||
destPrefix = cd.DestinationPrefix
|
||||
key := base + destPrefix
|
||||
values, err := cd.storageGetter.GetActivationPeriods(key)
|
||||
values, err := cd.StorageGetter.GetActivationPeriods(key)
|
||||
//get for a smaller prefix if the orignal one was not found
|
||||
|
||||
for i := len(cd.DestinationPrefix); err != nil && i >= MinPrefixLength; values, err = cd.storageGetter.GetActivationPeriods(key) {
|
||||
for i := len(cd.DestinationPrefix); err != nil && i >= MinPrefixLength; values, err = cd.StorageGetter.GetActivationPeriods(key) {
|
||||
i--
|
||||
destPrefix = cd.DestinationPrefix[:i]
|
||||
key = base + destPrefix
|
||||
@@ -92,9 +92,10 @@ Splits the received timespan into sub time spans according to the activation per
|
||||
*/
|
||||
func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeSpan) {
|
||||
timespans = append(timespans, firstSpan)
|
||||
// split on (free) minute buckets
|
||||
if userBudget, err := cd.storageGetter.GetUserBudget(cd.Subject); err == nil && userBudget != nil {
|
||||
_, bucketList := userBudget.getSecondsForPrefix(cd.storageGetter, cd.DestinationPrefix)
|
||||
// split on (free) minute buckets
|
||||
if userBudget, err := cd.StorageGetter.GetUserBudget(cd.Subject); err == nil && userBudget != nil {
|
||||
userBudget.mux.RLock()
|
||||
_, bucketList := userBudget.getSecondsForPrefix(cd.StorageGetter, cd.DestinationPrefix)
|
||||
for _, mb := range bucketList {
|
||||
for i := 0; i < len(timespans); i++ {
|
||||
if timespans[i].MinuteInfo != nil {
|
||||
@@ -108,7 +109,9 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS
|
||||
}
|
||||
}
|
||||
}
|
||||
userBudget.mux.RUnlock()
|
||||
}
|
||||
|
||||
if firstSpan.MinuteInfo != nil {
|
||||
return // all the timespans are on minutes
|
||||
}
|
||||
@@ -206,7 +209,7 @@ Returns the cost of a second in the present time conditions.
|
||||
func (cd *CallDescriptor) GetMaxSessionTime(maxSessionSeconds int) (seconds int, err error) {
|
||||
_, err = cd.RestoreFromStorage()
|
||||
now := time.Now()
|
||||
maxDuration, _ := time.ParseDuration(fmt.Sprintf("%ds", maxSessionSeconds))
|
||||
maxDuration, _ := time.ParseDuration(fmt.Sprintf("%vs", maxSessionSeconds))
|
||||
ts := &TimeSpan{TimeStart: now, TimeEnd: now.Add(maxDuration)}
|
||||
timespans := cd.splitTimeSpan(ts)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ func TestKyotoSplitSpans(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
|
||||
cd.RestoreFromStorage()
|
||||
timespans := cd.splitInTimeSpans()
|
||||
@@ -27,7 +27,7 @@ func TestRedisSplitSpans(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
cd.RestoreFromStorage()
|
||||
|
||||
timespans := cd.splitInTimeSpans()
|
||||
@@ -43,13 +43,13 @@ func TestKyotoGetCost(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
t.Errorf("Expected %v was %v", expected, result)
|
||||
}
|
||||
cd = &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd = &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ = cd.GetCost()
|
||||
expected = &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 540, ConnectFee: 0}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func TestRedisGetCost(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -74,7 +74,7 @@ func TestMongoGetCost(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -88,7 +88,7 @@ func TestFullDestNotFound(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256308200", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", Cost: 540, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -102,7 +102,7 @@ func TestMultipleActivationPeriods(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 8, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 8, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 330, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -118,7 +118,7 @@ func TestSpansMultipleActivationPeriods(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 7, 23, 50, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 8, 0, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 360, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -132,7 +132,7 @@ func TestLessThanAMinute(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 8, 23, 50, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 8, 23, 50, 30, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257308200", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 0.5, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -146,7 +146,7 @@ func TestUniquePrice(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723045326", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723045326", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", Cost: 60.35, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -160,7 +160,7 @@ func TestPresentSecodCost(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.getPresentSecondCost()
|
||||
expected := 0.016
|
||||
if result != expected {
|
||||
@@ -174,7 +174,7 @@ func TestMinutesCost(t *testing.T) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "minutosu", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "minutosu", DestinationPrefix: "0723", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
result, _ := cd.GetCost()
|
||||
expected := &CallCost{CstmId: "vdf", Subject: "minutosu", DestinationPrefix: "0723", Cost: 0, ConnectFee: 0}
|
||||
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
|
||||
@@ -204,7 +204,7 @@ func BenchmarkRedisRestoring(b *testing.B) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cd.RestoreFromStorage()
|
||||
@@ -218,7 +218,7 @@ func BenchmarkRedisGetCost(b *testing.B) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cd.GetCost()
|
||||
@@ -232,7 +232,7 @@ func BenchmarkKyotoGetting(b *testing.B) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
key := cd.GetKey()
|
||||
@@ -247,7 +247,7 @@ func BenchmarkKyotoRestoring(b *testing.B) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cd.RestoreFromStorage()
|
||||
@@ -261,7 +261,7 @@ func BenchmarkSplitting(b *testing.B) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
cd.RestoreFromStorage()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -276,7 +276,7 @@ func BenchmarkKyotoGetCost(b *testing.B) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cd.GetCost()
|
||||
@@ -304,7 +304,7 @@ func BenchmarkMongoGetCost(b *testing.B) {
|
||||
|
||||
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
|
||||
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, storageGetter: getter}
|
||||
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2, StorageGetter: getter}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cd.GetCost()
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -18,22 +19,29 @@ type UserBudget struct {
|
||||
TariffPlanId string
|
||||
tariffPlan *TariffPlan
|
||||
MinuteBuckets []*MinuteBucket
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
type AmountTooBig byte
|
||||
|
||||
func (a AmountTooBig) Error() string {
|
||||
return "Amount excedes budget!"
|
||||
}
|
||||
|
||||
/*
|
||||
Structure to store minute buckets according to priority, precision or price.
|
||||
*/
|
||||
type BucketSorter []*MinuteBucket
|
||||
type bucketsorter []*MinuteBucket
|
||||
|
||||
func (bs BucketSorter) Len() int {
|
||||
func (bs bucketsorter) Len() int {
|
||||
return len(bs)
|
||||
}
|
||||
|
||||
func (bs BucketSorter) Swap(i, j int) {
|
||||
func (bs bucketsorter) Swap(i, j int) {
|
||||
bs[i], bs[j] = bs[j], bs[i]
|
||||
}
|
||||
|
||||
func (bs BucketSorter) Less(j, i int) bool {
|
||||
func (bs bucketsorter) Less(j, i int) bool {
|
||||
return bs[i].Priority < bs[j].Priority ||
|
||||
bs[i].precision < bs[j].precision ||
|
||||
bs[i].Price > bs[j].Price
|
||||
@@ -92,14 +100,14 @@ func (ub *UserBudget) getTariffPlan(storage StorageGetter) (tp *TariffPlan) {
|
||||
/*
|
||||
Returns user's avaliable minutes for the specified destination
|
||||
*/
|
||||
func (ub *UserBudget) getSecondsForPrefix(storage StorageGetter, prefix string) (seconds float64, bucketList BucketSorter) {
|
||||
func (ub *UserBudget) getSecondsForPrefix(sg StorageGetter, prefix string) (seconds float64, bucketList bucketsorter) {
|
||||
if len(ub.MinuteBuckets) == 0 {
|
||||
log.Print("There are no minute buckets to check for user", ub.Id)
|
||||
return
|
||||
}
|
||||
|
||||
for _, mb := range ub.MinuteBuckets {
|
||||
d := mb.getDestination(storage)
|
||||
d := mb.getDestination(sg)
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
@@ -115,8 +123,60 @@ func (ub *UserBudget) getSecondsForPrefix(storage StorageGetter, prefix string)
|
||||
credit := ub.Credit
|
||||
for _, mb := range bucketList {
|
||||
s := mb.GetSecondsForCredit(credit)
|
||||
credit -= s
|
||||
credit -= s * mb.Price
|
||||
seconds += s
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Debits some amount of user's money credit. Returns the remaining credit in user's budget.
|
||||
*/
|
||||
func (ub *UserBudget) debitMoneyBudget(sg StorageGetter, amount float64) float64 {
|
||||
ub.mux.Lock()
|
||||
defer ub.mux.Unlock()
|
||||
ub.Credit -= amount
|
||||
sg.SetUserBudget(ub)
|
||||
return ub.Credit
|
||||
}
|
||||
|
||||
/*
|
||||
Debits the recived amount of seconds from user's minute buckets.
|
||||
All the appropriate buckets will be debited until all amount of minutes is consumed.
|
||||
If the amount is bigger than the sum of all seconds in the minute buckets than nothing will be
|
||||
debited and an error will be returned.
|
||||
*/
|
||||
func (ub *UserBudget) debitMinutesBudget(sg StorageGetter, amount float64, prefix string) error {
|
||||
ub.mux.Lock()
|
||||
defer ub.mux.Unlock()
|
||||
avaliableNbSeconds, bucketList := ub.getSecondsForPrefix(sg, prefix)
|
||||
if avaliableNbSeconds < amount {
|
||||
return new(AmountTooBig)
|
||||
}
|
||||
for _, mb := range bucketList {
|
||||
if mb.Seconds < amount {
|
||||
amount -= mb.Seconds
|
||||
mb.Seconds = 0
|
||||
} else {
|
||||
mb.Seconds -= amount
|
||||
break
|
||||
}
|
||||
}
|
||||
sg.SetUserBudget(ub)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Debits some amount of user's SMS budget. Returns the remaining SMS in user's budget.
|
||||
If the amount is bigger than the budget than nothing wil be debited and an error will be returned
|
||||
*/
|
||||
func (ub *UserBudget) debitSMSBuget(sg StorageGetter, amount int) (int, error) {
|
||||
ub.mux.Lock()
|
||||
defer ub.mux.Unlock()
|
||||
if ub.SmsCredit < amount {
|
||||
return ub.SmsCredit, new(AmountTooBig)
|
||||
}
|
||||
ub.SmsCredit -= amount
|
||||
sg.SetUserBudget(ub)
|
||||
return ub.SmsCredit, nil
|
||||
}
|
||||
|
||||
@@ -91,6 +91,130 @@ func TestUserBudgetMongoStore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitMoneyBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "o4her", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, ResetDayOfTheMonth: 10}
|
||||
result := rifsBudget.debitMoneyBudget(getter, 6)
|
||||
if rifsBudget.Credit != 15 || result != rifsBudget.Credit {
|
||||
t.Errorf("Expected %v was %v", 15, rifsBudget.Credit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitAllMoneyBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.01, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, ResetDayOfTheMonth: 10}
|
||||
rifsBudget.debitMoneyBudget(getter, 21)
|
||||
result := rifsBudget.debitMoneyBudget(getter, 0)
|
||||
if rifsBudget.Credit != 0 || result != rifsBudget.Credit {
|
||||
t.Errorf("Expected %v was %v", 0, rifsBudget.Credit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitMoreMoneyBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, ResetDayOfTheMonth: 10}
|
||||
result := rifsBudget.debitMoneyBudget(getter, 22)
|
||||
if rifsBudget.Credit != -1 || result != rifsBudget.Credit {
|
||||
t.Errorf("Expected %v was %v", -1, rifsBudget.Credit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitMinuteBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, ResetDayOfTheMonth: 10}
|
||||
err := rifsBudget.debitMinutesBudget(getter, 6, "0723")
|
||||
if b2.Seconds != 94 || err != nil {
|
||||
t.Log(err)
|
||||
t.Errorf("Expected %v was %v", 94, b2.Seconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitMultipleBucketsMinuteBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, ResetDayOfTheMonth: 10}
|
||||
err := rifsBudget.debitMinutesBudget(getter, 105, "0723")
|
||||
if b2.Seconds != 0 || b1.Seconds != 5 || err != nil {
|
||||
t.Log(err)
|
||||
t.Errorf("Expected %v was %v", 0, b2.Seconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitAllMinuteBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, ResetDayOfTheMonth: 10}
|
||||
err := rifsBudget.debitMinutesBudget(getter, 110, "0723")
|
||||
if b2.Seconds != 0 || b1.Seconds != 0 || err != nil {
|
||||
t.Log(err)
|
||||
t.Errorf("Expected %v was %v", 0, b2.Seconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitMoreMinuteBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, ResetDayOfTheMonth: 10}
|
||||
err := rifsBudget.debitMinutesBudget(getter, 115, "0723")
|
||||
if b2.Seconds != 100 || b1.Seconds != 10 || err == nil {
|
||||
t.Errorf("Expected %v was %v", 1000, b2.Seconds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitSMSBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, SmsCredit: 100, ResetDayOfTheMonth: 10}
|
||||
result, err := rifsBudget.debitSMSBuget(getter, 12)
|
||||
if rifsBudget.SmsCredit != 88 || result != rifsBudget.SmsCredit || err != nil {
|
||||
t.Errorf("Expected %v was %v", 88, rifsBudget.SmsCredit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitAllSMSBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, SmsCredit: 100, ResetDayOfTheMonth: 10}
|
||||
result, err := rifsBudget.debitSMSBuget(getter, 100)
|
||||
if rifsBudget.SmsCredit != 0 || result != rifsBudget.SmsCredit || err != nil {
|
||||
t.Errorf("Expected %v was %v", 0, rifsBudget.SmsCredit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebitMoreSMSBudget(t *testing.T) {
|
||||
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
|
||||
defer getter.Close()
|
||||
b1 := &MinuteBucket{Seconds: 10, Priority: 10, Price: 0.0, DestinationId: "nationale"}
|
||||
b2 := &MinuteBucket{Seconds: 100, Priority: 20, Price: 0.0, DestinationId: "retea"}
|
||||
rifsBudget := &UserBudget{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 21, SmsCredit: 100, ResetDayOfTheMonth: 10}
|
||||
result, err := rifsBudget.debitSMSBuget(getter, 110)
|
||||
if rifsBudget.SmsCredit != 100 || result != rifsBudget.SmsCredit || err == nil {
|
||||
t.Errorf("Expected %v was %v", 100, rifsBudget.SmsCredit)
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************** Benchmarks *******************************/
|
||||
|
||||
func BenchmarkGetSecondForPrefix(b *testing.B) {
|
||||
|
||||
Reference in New Issue
Block a user