rater fallback

This commit is contained in:
Radu Ioan Fericean
2012-05-22 15:56:23 +03:00
parent 4aca30122b
commit 5fda30ca29
10 changed files with 167 additions and 40 deletions

View File

@@ -45,7 +45,7 @@ func writeToStorage(storage timespans.StorageGetter,
tariffPlans []*timespans.TariffPlan,
userBudgets []*timespans.UserBudget) {
for _, cd := range callDescriptors {
storage.SetActivationPeriods(cd.GetKey(), cd.ActivationPeriods)
storage.SetActivationPeriodsOrFallback(cd.GetKey(), cd.ActivationPeriods, cd.FallbackKey)
log.Printf("Storing activation periods for %q", cd.GetKey())
}
for _, d := range destinations {

Binary file not shown.

View File

@@ -30,6 +30,29 @@
}
]
},
{"TOR": "0","CstmId":"vdf","Subject":"rif","DestinationPrefix":"0745", "FallbackKey": "vdf:radu"},
{"TOR": "0","CstmId":"vdf","Subject":"rif","DestinationPrefix":"*", "FallbackKey": "vdf:radu"},
{"TOR": "0","CstmId":"vdf","Subject":"rif","DestinationPrefix":"0721", "FallbackKey": "vdf:radu"},
{"TOR": "0","CstmId":"vdf","Subject":"radu","DestinationPrefix":"0745", "ActivationPeriods": [
{"ActivationTime": "2012-01-01T00:00:00Z", "Intervals": [
{"BillingUnit":60,"ConnectFee":0,"Month":0,"MonthDay":0,"Ponder":0,"Price":1,"StartTime":"","EndTime":""}
]
}
]
},
{"TOR": "0","CstmId":"vdf","Subject":"radu","DestinationPrefix":"00", "ActivationPeriods": [
{"ActivationTime": "2012-01-01T00:00:00Z", "Intervals": [
{"BillingUnit":60,"ConnectFee":0,"Month":0,"MonthDay":0,"Ponder":0,"Price":1,"StartTime":"","EndTime":""}
]
}
]
},
{"TOR": "0","CstmId":"vdf","Subject":"radu","DestinationPrefix":"0721", "FallbackKey": "vdf:rif"},
{"TOR": "0","CstmId":"1","Subject":"1000","DestinationPrefix":"0723", "ActivationPeriods": [
{"ActivationTime": "2012-01-01T00:00:00Z", "Intervals": [
{"BillingUnit":60,"ConnectFee":0,"Month":0,"MonthDay":0,"Ponder":0,"Price":1,"StartTime":"","EndTime":""}

View File

@@ -29,7 +29,10 @@ func TestApRestoreKyoto(t *testing.T) {
getter, _ := NewKyotoStorage("../data/test.kch")
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", storageGetter: getter}
cd := &CallDescriptor{CstmId: "vdf",
Subject: "rif",
DestinationPrefix: "0257",
storageGetter: getter}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 2 {
t.Error("Error restoring activation periods: ", cd.ActivationPeriods)
@@ -40,7 +43,10 @@ func TestApRestoreRedis(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", storageGetter: getter}
cd := &CallDescriptor{CstmId: "vdf",
Subject: "rif",
DestinationPrefix: "0257",
storageGetter: getter}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 2 {
t.Error("Error restoring activation periods: ", cd.ActivationPeriods)
@@ -68,6 +74,50 @@ func TestApStoreRestore(t *testing.T) {
}
}
func TestFallbackDirect(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0745", storageGetter: getter}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", cd.ActivationPeriods)
}
}
func TestFallbackWithBackTrace(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0745121", storageGetter: getter}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", cd.ActivationPeriods)
}
}
func TestFallbackDefault(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "00000", storageGetter: getter}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", cd.ActivationPeriods)
}
}
func TestFallbackNoInfiniteLoop(t *testing.T) {
getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0721", storageGetter: getter}
cd.SearchStorageForPrefix()
if len(cd.ActivationPeriods) != 0 {
t.Error("Error restoring activation periods: ", cd.ActivationPeriods)
}
}
/**************************** Benchmarks *************************************/
func BenchmarkActivationPeriodRestore(b *testing.B) {

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package timespans
import (
"errors"
"fmt"
"log"
"math"
@@ -27,7 +28,8 @@ import (
const (
// the minimum length for a destination prefix to be matched.
MinPrefixLength = 2
MinPrefixLength = 2
RecursionMaxDepth = 4
)
/*
@@ -57,6 +59,7 @@ type CallDescriptor struct {
Amount float64
FallbackSubject string // the subject to check for destination if not found on primary subject
ActivationPeriods []*ActivationPeriod
FallbackKey string
storageGetter StorageGetter
userBudget *UserBudget
}
@@ -95,21 +98,51 @@ func (cd *CallDescriptor) SearchStorageForPrefix() (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)
//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) {
i--
destPrefix = cd.DestinationPrefix[:i]
key = base + destPrefix
values, err := cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1)
if err != nil {
key := base + "*"
values, err = cd.getActivationPeriodsOrFallback(key, base, destPrefix, 1)
}
//load the activation preriods
if err == nil {
if err == nil && len(values) > 0 {
cd.ActivationPeriods = values
}
return
}
func (cd *CallDescriptor) getActivationPeriodsOrFallback(key, base, destPrefix string, recursionDepth int) (values []*ActivationPeriod, err error) {
if recursionDepth > RecursionMaxDepth {
err = errors.New("Max fallback recursion depth reached!" + key)
log.Print(err)
return
}
values, fallbackKey, err := cd.storageGetter.GetActivationPeriodsOrFallback(key)
if fallbackKey != "" {
base = fallbackKey + ":"
key = base + destPrefix
recursionDepth++
return cd.getActivationPeriodsOrFallback(key, base, destPrefix, recursionDepth)
}
//get for a smaller prefix if the orignal one was not found
for i := len(cd.DestinationPrefix); err != nil || fallbackKey != ""; {
if fallbackKey != "" {
base = fallbackKey + ":"
key = base + destPrefix
recursionDepth++
return cd.getActivationPeriodsOrFallback(key, base, destPrefix, recursionDepth)
}
i--
if i >= MinPrefixLength {
destPrefix = cd.DestinationPrefix[:i]
key = base + destPrefix
} else {
break
}
values, fallbackKey, err = cd.storageGetter.GetActivationPeriodsOrFallback(key)
}
return
}
/*
Constructs the key for the storage lookup.
The prefixLen is limiting the length of the destination prefix.

View File

@@ -274,7 +274,7 @@ func BenchmarkRedisGetting(b *testing.B) {
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
getter.GetActivationPeriods(cd.GetKey())
getter.GetActivationPeriodsOrFallback(cd.GetKey())
}
}
@@ -317,7 +317,7 @@ func BenchmarkKyotoGetting(b *testing.B) {
b.StartTimer()
for i := 0; i < b.N; i++ {
key := cd.GetKey()
getter.GetActivationPeriods(key)
getter.GetActivationPeriodsOrFallback(key)
}
}
@@ -374,7 +374,7 @@ func BenchmarkMongoGetting(b *testing.B) {
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
getter.GetActivationPeriods(cd.GetKey())
getter.GetActivationPeriodsOrFallback(cd.GetKey())
}
}

View File

@@ -23,8 +23,8 @@ Interface for storage providers.
*/
type StorageGetter interface {
Close()
GetActivationPeriods(string) ([]*ActivationPeriod, error)
SetActivationPeriods(string, []*ActivationPeriod) error
GetActivationPeriodsOrFallback(string) ([]*ActivationPeriod, string, error)
SetActivationPeriodsOrFallback(string, []*ActivationPeriod, string) error
GetDestination(string) (*Destination, error)
SetDestination(*Destination) error
GetTariffPlan(string) (*TariffPlan, error)

View File

@@ -22,7 +22,7 @@ import (
//"github.com/fsouza/gokabinet/kc"
"bitbucket.org/ww/cabinet"
//"log"
"bytes"
"strings"
)
type KyotoStorage struct {
@@ -40,25 +40,35 @@ func (ks *KyotoStorage) Close() {
ks.db.Close()
}
func (ks *KyotoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
values, err := ks.db.Get([]byte(key))
if err == nil {
for _, ap_string := range bytes.Split(values, []byte{'\n'}) {
func (ks *KyotoStorage) GetActivationPeriodsOrFallback(key string) (aps []*ActivationPeriod, fallbackKey string, err error) {
valuesBytes, err := ks.db.Get([]byte(key))
if err != nil {
return
}
valuesString := string(valuesBytes)
values := strings.Split(valuesString, "\n")
if len(values) > 1 {
for _, ap_string := range values {
if len(ap_string) > 0 {
ap := &ActivationPeriod{}
ap.restore(string(ap_string))
ap.restore(ap_string)
aps = append(aps, ap)
}
}
} else { // fallback case
fallbackKey = valuesString
}
return aps, err
return
}
func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
func (ks *KyotoStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) error {
result := ""
for _, ap := range aps {
result += ap.store() + "\n"
if len(aps) > 0 {
for _, ap := range aps {
result += ap.store() + "\n"
}
} else {
result = fallbackKey
}
return ks.db.Set([]byte(key), []byte(result))
}

View File

@@ -56,19 +56,20 @@ Helper type for activation periods storage.
*/
type KeyValue struct {
Key string
FallbackKey string
ActivationPeriods []*ActivationPeriod
}
func (ms *MongoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
func (ms *MongoStorage) GetActivationPeriodsOrFallback(key string) (aps []*ActivationPeriod, fallbackKey string, err error) {
ndb := ms.db.C("activationPeriods")
result := KeyValue{}
err = ndb.Find(bson.M{"key": key}).One(&result)
return result.ActivationPeriods, err
return result.ActivationPeriods, result.FallbackKey, err
}
func (ms *MongoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
func (ms *MongoStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) error {
ndb := ms.db.C("activationPeriods")
return ndb.Insert(&KeyValue{key, aps})
return ndb.Insert(&KeyValue{key, fallbackKey, aps})
}
func (ms *MongoStorage) GetDestination(key string) (result *Destination, err error) {

View File

@@ -37,27 +37,37 @@ func (rs *RedisStorage) Close() {
rs.db.Quit()
}
func (rs *RedisStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
func (rs *RedisStorage) GetActivationPeriodsOrFallback(key string) (aps []*ActivationPeriod, fallbackKey string, err error) {
//rs.db.Select(rs.dbNb)
elem, err := rs.db.Get(key)
values := elem.String()
if err == nil {
for _, ap_string := range strings.Split(values, "\n") {
if err != nil {
return
}
valuesString := elem.String()
values := strings.Split(valuesString, "\n")
if len(values) > 1 {
for _, ap_string := range values {
if len(ap_string) > 0 {
ap := &ActivationPeriod{}
ap.restore(ap_string)
aps = append(aps, ap)
}
}
} else { // fallback case
fallbackKey = valuesString
}
return aps, err
return
}
func (rs *RedisStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
func (rs *RedisStorage) SetActivationPeriodsOrFallback(key string, aps []*ActivationPeriod, fallbackKey string) error {
//.db.Select(rs.dbNb)
result := ""
for _, ap := range aps {
result += ap.store() + "\n"
if len(aps) > 0 {
for _, ap := range aps {
result += ap.store() + "\n"
}
} else {
result = fallbackKey
}
return rs.db.Set(key, result)
}