mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
rater fallback
This commit is contained in:
@@ -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 {
|
||||
|
||||
BIN
data/test.kch
BIN
data/test.kch
Binary file not shown.
@@ -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":""}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user