From 1f9fd6302631e2c587769ca203162a87dddb3873 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 22 Feb 2012 16:27:52 +0200 Subject: [PATCH] much better seconds for prefix --- cmd/inquirer/inquirer.go | 4 +- cmd/inquirer/registration.go | 9 +--- cmd/loader/loader.go | 17 ++++++-- cmd/stress/inquirerstress/inquirerstress.go | 4 +- timespans/calldesc.go | 26 ++++++----- timespans/calldesc_test.go | 16 ++++++- timespans/destinations.go | 15 ++++--- timespans/destinations_test.go | 3 +- timespans/intervals.go | 6 +-- timespans/kyoto_storage.go | 22 ++++++---- timespans/minute_buckets.go | 1 + timespans/redis_storage.go | 21 +++++---- timespans/test.kch | Bin 6298536 -> 6298568 bytes timespans/test_destinations.json | 1 + timespans/test_userbudgets.json | 4 +- timespans/userbudget.go | 45 ++++++++++++++++---- timespans/userbudget_test.go | 2 +- 17 files changed, 129 insertions(+), 67 deletions(-) diff --git a/cmd/inquirer/inquirer.go b/cmd/inquirer/inquirer.go index 7e6bbf25f..11bbed56b 100644 --- a/cmd/inquirer/inquirer.go +++ b/cmd/inquirer/inquirer.go @@ -4,7 +4,7 @@ import ( "errors" "github.com/rif/cgrates/timespans" "log" - "runtime" + //"runtime" "time" "flag" ) @@ -42,7 +42,7 @@ func CallRater(key *timespans.CallDescriptor) (reply *timespans.CallCost) { func main() { flag.Parse() - runtime.GOMAXPROCS(runtime.NumCPU() - 1) + //runtime.GOMAXPROCS(runtime.NumCPU() - 1) raterList = NewRaterList() go StopSingnalHandler() diff --git a/cmd/inquirer/registration.go b/cmd/inquirer/registration.go index 6ea4579a7..c11ef961d 100644 --- a/cmd/inquirer/registration.go +++ b/cmd/inquirer/registration.go @@ -4,7 +4,6 @@ import ( "os/signal" "fmt" "log" - "net" "net/rpc" "net/http" "os" @@ -21,13 +20,7 @@ func listenToRPCRaterRequests(){ raterServer := new(RaterServer) rpc.Register(raterServer) rpc.HandleHTTP() - l, e := net.Listen("tcp", *raterAddress) - - if e != nil { - log.Fatal("listen error:", e) - } - log.Print("Listening for raters on ", *raterAddress) - http.Serve(l, nil) + http.ListenAndServe(*raterAddress, nil) } /* diff --git a/cmd/loader/loader.go b/cmd/loader/loader.go index 71b32d416..b1afc7e3a 100644 --- a/cmd/loader/loader.go +++ b/cmd/loader/loader.go @@ -9,7 +9,7 @@ import ( ) var ( - storage = flag.String("storage", "kyoto", "kyoto|redis|mongo") + storage = flag.String("storage", "all", "kyoto|redis|mongo") kyotofile = flag.String("kyotofile", "storage.kch", "kyoto storage file (storage.kch)") redisserver = flag.String("redisserver", "tcp:127.0.0.1:6379", "redis server address (tcp:127.0.0.1:6379)") redisdb = flag.Int("rdb", 10, "redis database number (10)") @@ -120,13 +120,22 @@ func main() { defer storage.Close() writeToStorage(storage, callDescriptors, destinations, tariffPlans, userBudgets) case "mongo": - storage, _ := timespans.NewMongoStorage("127.0.0.1", "test") + storage, _ := timespans.NewMongoStorage(*mongoserver, *mongodb) defer storage.Close() writeToStorage(storage, callDescriptors, destinations, tariffPlans, userBudgets) - - default: + case "redis": storage, _ := timespans.NewRedisStorage(*redisserver, *redisdb) defer storage.Close() writeToStorage(storage, callDescriptors, destinations, tariffPlans, userBudgets) + default: + kyoto, _ := timespans.NewKyotoStorage(*kyotofile) + writeToStorage(kyoto, callDescriptors, destinations, tariffPlans, userBudgets) + kyoto.Close() + mongo, _ := timespans.NewMongoStorage(*mongoserver, *mongodb) + writeToStorage(mongo, callDescriptors, destinations, tariffPlans, userBudgets) + mongo.Close() + redis, _ := timespans.NewRedisStorage(*redisserver, *redisdb) + writeToStorage(redis, callDescriptors, destinations, tariffPlans, userBudgets) + redis.Close() } } diff --git a/cmd/stress/inquirerstress/inquirerstress.go b/cmd/stress/inquirerstress/inquirerstress.go index ba3e3a79d..a08f4b665 100644 --- a/cmd/stress/inquirerstress/inquirerstress.go +++ b/cmd/stress/inquirerstress/inquirerstress.go @@ -21,10 +21,9 @@ func main() { cd := timespans.CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2} result := timespans.CallCost{} client, _ := jsonrpc.Dial("tcp", "localhost:2001") - i := 0 if *parallel { var divCall *rpc.Call - for ; i < *runs; i++ { + for i := 0; i < *runs; i++ { divCall = client.Go("Responder.Get", cd, &result, nil) } <-divCall.Done @@ -34,6 +33,5 @@ func main() { } } log.Println(result) - log.Println(i) client.Close() } diff --git a/timespans/calldesc.go b/timespans/calldesc.go index 59fa29284..8ed49c6c7 100644 --- a/timespans/calldesc.go +++ b/timespans/calldesc.go @@ -7,6 +7,11 @@ import ( "time" ) +const ( + // the minimum length for a destination prefix to be matched. + MinPrefixLength = 2 +) + /* Utility function for rounding a float to a certain number of decimals (not present in math). */ @@ -53,7 +58,8 @@ func (cd *CallDescriptor) RestoreFromStorage(sg StorageGetter) (destPrefix strin key := base + destPrefix values, err := sg.GetActivationPeriods(key) //get for a smaller prefix if the orignal one was not found - for i := len(cd.DestinationPrefix); err != nil && i > 1; values, err = sg.GetActivationPeriods(key) { + + for i := len(cd.DestinationPrefix); err != nil && i >= MinPrefixLength; values, err = sg.GetActivationPeriods(key) { i-- destPrefix = cd.DestinationPrefix[:i] key = base + destPrefix @@ -129,13 +135,16 @@ Creates a CallCost structure with the cost nformation calculated for the receive */ func (cd *CallDescriptor) GetCost(sg StorageGetter) (*CallCost, error) { destPrefix, err := cd.RestoreFromStorage(sg) + cc := &CallCost{TOR: cd.TOR, CstmId: cd.CstmId, Subject: cd.Subject, DestinationPrefix: destPrefix} - userBudget, err := sg.GetUserBudget(cd.Subject) - if err != nil { + if userBudget, err := sg.GetUserBudget(cd.Subject); err == nil { nbSeconds := cd.TimeEnd.Sub(cd.TimeStart).Seconds() + log.Print("seconds: ", nbSeconds) avaliableNbSeconds := userBudget.getSecondsForPrefix(sg, cd.DestinationPrefix) + log.Print("available: ", avaliableNbSeconds) if nbSeconds < avaliableNbSeconds { - return nil, nil + cc.Cost, cc.ConnectFee = 0, 0 + return cc, nil } } @@ -150,13 +159,8 @@ func (cd *CallDescriptor) GetCost(sg StorageGetter) (*CallCost, error) { cost += ts.GetCost() } - cc := &CallCost{TOR: cd.TOR, - CstmId: cd.CstmId, - Subject: cd.Subject, - DestinationPrefix: destPrefix, - Cost: cost, - ConnectFee: connectionFee, - Timespans: timespans} + cc.Cost, cc.ConnectFee, cc.Timespans = cost, connectionFee, timespans + return cc, err } diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go index 0b8a8040a..be1f82642 100644 --- a/timespans/calldesc_test.go +++ b/timespans/calldesc_test.go @@ -153,7 +153,7 @@ func TestUniquePrice(t *testing.T) { } } -func TestSecodCost(t *testing.T) { +func TestPresentSecodCost(t *testing.T) { getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10) defer getter.Close() @@ -167,6 +167,20 @@ func TestSecodCost(t *testing.T) { } } +func TestMinutesCost(t *testing.T) { + getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10) + defer getter.Close() + + 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} + result, _ := cd.GetCost(getter) + expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", Cost: 0, ConnectFee: 0} + if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee { + t.Errorf("Expected %v was %v", expected, result) + } +} + /*********************************** BENCHMARKS ***************************************/ func BenchmarkRedisGetting(b *testing.B) { b.StopTimer() diff --git a/timespans/destinations.go b/timespans/destinations.go index f929407ed..bbb7fcca3 100644 --- a/timespans/destinations.go +++ b/timespans/destinations.go @@ -1,6 +1,7 @@ package timespans import ( + //"log" "strings" ) @@ -30,11 +31,15 @@ func (d *Destination) restore(input string) { /* De-serializes the destination for the storage. Used for key-value storages. */ -func (d *Destination) containsPrefix(prefix string) bool { - for _, p := range d.Prefixes { - if prefix == p { - return true +func (d *Destination) containsPrefix(prefix string) (bool, int) { + for i := len(prefix); i >= MinPrefixLength; { + for _, p := range d.Prefixes { + if p == prefix[:i] { + return true, i + } } + i-- } - return false + + return false, 0 } diff --git a/timespans/destinations_test.go b/timespans/destinations_test.go index d063f333b..802f9a187 100644 --- a/timespans/destinations_test.go +++ b/timespans/destinations_test.go @@ -51,7 +51,8 @@ func TestDestinationContainsPrefix(t *testing.T) { getter, _ := NewMongoStorage("127.0.0.1", "test") defer getter.Close() nationale = &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}} - if !nationale.containsPrefix("0256") { + contains, precision := nationale.containsPrefix("0256") + if !contains || precision != len("0256") { t.Error("Should contain prefix: ", nationale) } diff --git a/timespans/intervals.go b/timespans/intervals.go index f082dbdbc..8ddff29f6 100644 --- a/timespans/intervals.go +++ b/timespans/intervals.go @@ -14,7 +14,7 @@ type Interval struct { Month time.Month MonthDay int WeekDays []time.Weekday - StartTime, EndTime string // ##:##:## format + StartTime, EndTime string // ##:##:## format Ponder, ConnectFee, Price, BillingUnit float64 } @@ -46,7 +46,7 @@ func (i *Interval) Contains(t time.Time) bool { sh, _ := strconv.Atoi(split[0]) sm, _ := strconv.Atoi(split[1]) ss, _ := strconv.Atoi(split[2]) - // if the hour is before or is the same hour but the minute is before + // if the hour is before or is the same hour but the minute is before if t.Hour() < sh || (t.Hour() == sh && t.Minute() < sm) || (t.Hour() == sh && t.Minute() == sm && t.Second() < ss) { @@ -59,7 +59,7 @@ func (i *Interval) Contains(t time.Time) bool { eh, _ := strconv.Atoi(split[0]) em, _ := strconv.Atoi(split[1]) es, _ := strconv.Atoi(split[2]) - // if the hour is after or is the same hour but the minute is after + // if the hour is after or is the same hour but the minute is after if t.Hour() > eh || (t.Hour() == eh && t.Minute() > em) || (t.Hour() == eh && t.Minute() == em && t.Second() > es) { diff --git a/timespans/kyoto_storage.go b/timespans/kyoto_storage.go index 91ee9499a..b3fba9cd2 100644 --- a/timespans/kyoto_storage.go +++ b/timespans/kyoto_storage.go @@ -2,6 +2,7 @@ package timespans import ( "github.com/fsouza/gokabinet/kc" + //"log" "strings" ) @@ -42,9 +43,10 @@ func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod } func (ks *KyotoStorage) GetDestination(key string) (dest *Destination, err error) { - values, err := ks.db.Get(key) - dest = &Destination{Id: key} - dest.restore(values) + if values, err := ks.db.Get(key); err == nil { + dest = &Destination{Id: key} + dest.restore(values) + } return } @@ -53,9 +55,10 @@ func (ks *KyotoStorage) SetDestination(dest *Destination) { } func (ks *KyotoStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) { - values, err := ks.db.Get(key) - tp = &TariffPlan{Id: key} - tp.restore(values) + if values, err := ks.db.Get(key); err == nil { + tp = &TariffPlan{Id: key} + tp.restore(values) + } return } @@ -64,9 +67,10 @@ func (ks *KyotoStorage) SetTariffPlan(tp *TariffPlan) { } func (ks *KyotoStorage) GetUserBudget(key string) (ub *UserBudget, err error) { - values, err := ks.db.Get(key) - ub = &UserBudget{Id: key} - ub.restore(values) + if values, err := ks.db.Get(key); err == nil { + ub = &UserBudget{Id: key} + ub.restore(values) + } return } diff --git a/timespans/minute_buckets.go b/timespans/minute_buckets.go index 8e20e2dec..c9997630a 100644 --- a/timespans/minute_buckets.go +++ b/timespans/minute_buckets.go @@ -6,6 +6,7 @@ type MinuteBucket struct { Price float64 DestinationId string destination *Destination + precision int } /* diff --git a/timespans/redis_storage.go b/timespans/redis_storage.go index 4c024f284..a7d39890b 100644 --- a/timespans/redis_storage.go +++ b/timespans/redis_storage.go @@ -46,9 +46,10 @@ func (rs *RedisStorage) SetActivationPeriods(key string, aps []*ActivationPeriod func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error) { //rs.db.Select(rs.dbNb + 1) - values, err := rs.db.Get(key) - dest = &Destination{Id: key} - dest.restore(values.String()) + if values, err := rs.db.Get(key); err == nil { + dest = &Destination{Id: key} + dest.restore(values.String()) + } return } @@ -59,9 +60,10 @@ func (rs *RedisStorage) SetDestination(dest *Destination) { func (rs *RedisStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) { //rs.db.Select(rs.dbNb + 2) - values, err := rs.db.Get(key) - tp = &TariffPlan{Id: key} - tp.restore(values.String()) + if values, err := rs.db.Get(key); err == nil { + tp = &TariffPlan{Id: key} + tp.restore(values.String()) + } return } @@ -72,9 +74,10 @@ func (rs *RedisStorage) SetTariffPlan(tp *TariffPlan) { func (rs *RedisStorage) GetUserBudget(key string) (ub *UserBudget, err error) { //rs.db.Select(rs.dbNb + 3) - values, err := rs.db.Get(key) - ub = &UserBudget{Id: key} - ub.restore(values.String()) + if values, err := rs.db.Get(key); err == nil { + ub = &UserBudget{Id: key} + ub.restore(values.String()) + } return } diff --git a/timespans/test.kch b/timespans/test.kch index 24026e9b13232879cbba1c96f383bb2abd61850d..d4f132fc273a56024a2483679649e82c78055e33 100644 GIT binary patch delta 447 zcmbu+IZgs`7{+0k0dZJPU}K^PvJaq$8-Ll|7hC|rEukkydr2fT77nK{bQEL?E-(dG z!87o~m~a5z1q;WSYX8hY*>T>I~;K8 zUCkBzlAZGV?7t=2!aNXCNt@^Di%+97ZmwVzZmhurFV^9M9{~grLKqRKh$4nKHn52V zwvfa&cCd>*>>~w%1Ei5b7CGcmKoN&H!ZA*8iZh(!0++Z#3D>y6E$&c81y$UmhB_Yf dar1H5TOLV&qx_dud2PHko+F89EI`G7O%r(x#!dhL delta 410 zcmb`($58?S06mv%n%tEVIHY5o@fo!6sX5 ov%@ZX>~p{&M;vp)DQBFM bestBucket.Priority { - bestBucket = mb + if d == nil { + continue + } + contains, precision := d.containsPrefix(prefix) + if contains { + mb.precision = precision + bucketList = append(bucketList, mb) } } - seconds = float64(bestBucket.Seconds) - if bestBucket.Price > 0 { - seconds = math.Min(ub.Credit/bestBucket.Price, float64(seconds)) + sort.Sort(bucketList) + credit := ub.Credit + for _, mb := range bucketList { + s := float64(mb.Seconds) + if mb.Price > 0 { + s = math.Min(credit/mb.Price, s) + credit -= s + } + seconds += s } return } diff --git a/timespans/userbudget_test.go b/timespans/userbudget_test.go index e9d11b148..9816cc68a 100644 --- a/timespans/userbudget_test.go +++ b/timespans/userbudget_test.go @@ -17,7 +17,7 @@ func TestGetSeconds(t *testing.T) { ub1 := &UserBudget{Id: "rif", MinuteBuckets: []*MinuteBucket{b1, b2}, Credit: 200, tariffPlan: tf1, ResetDayOfTheMonth: 10} seconds := ub1.getSecondsForPrefix(nil, "0723") - expected := 100.0 + expected := 110.0 if seconds != expected { t.Errorf("Expected %v was %v", expected, seconds) }