diff --git a/cmd/stress/cgr-spansstress/cgr-spansstress.go b/cmd/stress/cgr-spansstress/cgr-spansstress.go
index 6bdd7ec72..64aac69ea 100644
--- a/cmd/stress/cgr-spansstress/cgr-spansstress.go
+++ b/cmd/stress/cgr-spansstress/cgr-spansstress.go
@@ -55,8 +55,8 @@ func main() {
Direction: "*out",
TOR: "call",
Tenant: "cgrates.org",
- Subject: "1001",
- Destination: "+49",
+ Subject: "dan",
+ Destination: "+31676016500",
}
getter, err := engine.ConfigureDataStorage(utils.REDIS, "127.0.0.1", "6379", "10", "", "", utils.MSGPACK)
if err != nil {
@@ -75,7 +75,7 @@ func main() {
j := 0
start := time.Now()
for i := 0; i < *runs; i++ {
- result, err = cd.Debit()
+ result, err = cd.GetCost()
if *memprofile != "" {
runtime.MemProfileRate = 1
runtime.GC()
diff --git a/engine/destinations.go b/engine/destinations.go
index c9b97308b..55d50d298 100644
--- a/engine/destinations.go
+++ b/engine/destinations.go
@@ -20,6 +20,8 @@ package engine
import (
"strings"
+
+ "github.com/cgrates/cgrates/utils"
)
/*
@@ -27,16 +29,16 @@ Structure that gathers multiple destination prefixes under a common id.
*/
type Destination struct {
Id string
- Prefixes []string
+ Prefixes map[string]interface{}
}
func (d *Destination) containsPrefix(prefix string) (precision int) {
if d == nil {
return
}
- for _, p := range d.Prefixes {
- if strings.Index(prefix, p) == 0 && len(p) > precision {
- precision = len(p)
+ for i, p := range utils.SplitPrefix(prefix) {
+ if _, found := d.Prefixes[p]; found {
+ return len(prefix) - i
}
}
return
@@ -44,8 +46,8 @@ func (d *Destination) containsPrefix(prefix string) (precision int) {
func (d *Destination) String() (result string) {
result = d.Id + ": "
- for _, p := range d.Prefixes {
- result += p + ", "
+ for k, _ := range d.Prefixes {
+ result += k + ", "
}
result = strings.TrimRight(result, ", ")
return result
diff --git a/engine/destinations_test.go b/engine/destinations_test.go
index a4ebc2105..004d12141 100644
--- a/engine/destinations_test.go
+++ b/engine/destinations_test.go
@@ -22,13 +22,12 @@ import (
"encoding/json"
"github.com/cgrates/cgrates/cache2go"
- "github.com/cgrates/cgrates/utils"
"testing"
)
func TestDestinationStoreRestore(t *testing.T) {
- nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
+ nationale := &Destination{Id: "nat", Prefixes: map[string]interface{}{"0257": nil, "0256": nil, "0723": nil}}
s, _ := json.Marshal(nationale)
d1 := &Destination{Id: "nat"}
json.Unmarshal(s, d1)
@@ -39,20 +38,22 @@ func TestDestinationStoreRestore(t *testing.T) {
}
func TestDestinationStorageStore(t *testing.T) {
- nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
+ nationale := &Destination{Id: "nat", Prefixes: map[string]interface{}{"0257": nil, "0256": nil, "0723": nil}}
err := storageGetter.SetDestination(nationale)
if err != nil {
t.Error("Error storing destination: ", err)
}
result, err := storageGetter.GetDestination(nationale.Id, false)
- var ss utils.StringSlice = result.Prefixes
- if !ss.Contains("0257") || !ss.Contains("0256") || !ss.Contains("0723") {
+ _, a := nationale.Prefixes["0257"]
+ _, b := nationale.Prefixes["0256"]
+ _, c := nationale.Prefixes["0723"]
+ if !a || !b || !c {
t.Errorf("Expected %q was %q", nationale, result)
}
}
func TestDestinationContainsPrefix(t *testing.T) {
- nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
+ nationale := &Destination{Id: "nat", Prefixes: map[string]interface{}{"0257": nil, "0256": nil, "0723": nil}}
precision := nationale.containsPrefix("0256")
if precision != len("0256") {
t.Error("Should contain prefix: ", nationale)
@@ -60,7 +61,7 @@ func TestDestinationContainsPrefix(t *testing.T) {
}
func TestDestinationContainsPrefixLong(t *testing.T) {
- nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
+ nationale := &Destination{Id: "nat", Prefixes: map[string]interface{}{"0257": nil, "0256": nil, "0723": nil}}
precision := nationale.containsPrefix("0256723045")
if precision != len("0256") {
t.Error("Should contain prefix: ", nationale)
@@ -68,7 +69,7 @@ func TestDestinationContainsPrefixLong(t *testing.T) {
}
func TestDestinationContainsPrefixWrong(t *testing.T) {
- nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
+ nationale := &Destination{Id: "nat", Prefixes: map[string]interface{}{"0257": nil, "0256": nil, "0723": nil}}
precision := nationale.containsPrefix("01234567")
if precision != 0 {
t.Error("Should not contain prefix: ", nationale)
@@ -155,7 +156,7 @@ func TestNonConcurrentDestReadWrite(t *testing.T) {
/********************************* Benchmarks **********************************/
func BenchmarkDestinationStorageStoreRestore(b *testing.B) {
- nationale := &Destination{Id: "nat", Prefixes: []string{"0257", "0256", "0723"}}
+ nationale := &Destination{Id: "nat", Prefixes: map[string]interface{}{"0257": nil, "0256": nil, "0723": nil}}
for i := 0; i < b.N; i++ {
storageGetter.SetDestination(nationale)
storageGetter.GetDestination(nationale.Id, true)
diff --git a/engine/history_test.go b/engine/history_test.go
index 6f78e9393..71f98110e 100644
--- a/engine/history_test.go
+++ b/engine/history_test.go
@@ -19,23 +19,15 @@ along with this program. If not, see
package engine
import (
- "github.com/cgrates/cgrates/history"
+ "strings"
"testing"
+
+ "github.com/cgrates/cgrates/history"
)
-func TestHistoryDestinations(t *testing.T) {
+func TestHistoryRatinPlans(t *testing.T) {
scribe := historyScribe.(*history.MockScribe)
- expected := `[{"Key":"ALL","Object":{"Id":"ALL","Prefixes":["49","41","43"]}}
-{"Key":"GERMANY","Object":{"Id":"GERMANY","Prefixes":["49"]}}
-{"Key":"GERMANY_O2","Object":{"Id":"GERMANY_O2","Prefixes":["41"]}}
-{"Key":"GERMANY_PREMIUM","Object":{"Id":"GERMANY_PREMIUM","Prefixes":["43"]}}
-{"Key":"NAT","Object":{"Id":"NAT","Prefixes":["0256","0257","0723"]}}
-{"Key":"PSTN_70","Object":{"Id":"PSTN_70","Prefixes":["+4970"]}}
-{"Key":"PSTN_71","Object":{"Id":"PSTN_71","Prefixes":["+4971"]}}
-{"Key":"PSTN_72","Object":{"Id":"PSTN_72","Prefixes":["+4972"]}}
-{"Key":"RET","Object":{"Id":"RET","Prefixes":["0723","0724"]}}
-{"Key":"nat","Object":{"Id":"nat","Prefixes":["0257","0256","0723"]}}]`
- if scribe.DestBuf.String() != expected {
- t.Error("Error in destination history content:", scribe.DestBuf.String())
+ if !strings.Contains(scribe.RpBuf.String(), `{"Key":"*out:vdf:0:minu","Object":{"Id":"*out:vdf:0:minu","RatingPlanActivations":[{"ActivationTime":"2012-01-01T00:00:00Z","RatingPlanId":"EVENING","FallbackKeys":null}]}}`) {
+ t.Error("Error in destination history content:", scribe.RpBuf.String())
}
}
diff --git a/engine/loader_csv.go b/engine/loader_csv.go
index 93cc553f1..f1d315e91 100644
--- a/engine/loader_csv.go
+++ b/engine/loader_csv.go
@@ -22,11 +22,12 @@ import (
"encoding/csv"
"errors"
"fmt"
- "github.com/cgrates/cgrates/utils"
"log"
"os"
"strconv"
"strings"
+
+ "github.com/cgrates/cgrates/utils"
)
type CSVReader struct {
@@ -198,10 +199,10 @@ func (csvr *CSVReader) LoadDestinations() (err error) {
}
}
if dest == nil {
- dest = &Destination{Id: tag}
+ dest = &Destination{Id: tag, Prefixes: make(map[string]interface{}, 1)}
csvr.destinations = append(csvr.destinations, dest)
}
- dest.Prefixes = append(dest.Prefixes, record[1])
+ dest.Prefixes[record[1]] = nil
}
return
}
diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go
index 314ede77c..3c910144d 100644
--- a/engine/loader_csv_test.go
+++ b/engine/loader_csv_test.go
@@ -151,39 +151,39 @@ func TestLoadDestinations(t *testing.T) {
for _, d := range csvr.destinations {
switch d.Id {
case "NAT":
- if !reflect.DeepEqual(d.Prefixes, []string{`0256`, `0257`, `0723`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`0256`: nil, `0257`: nil, `0723`: nil}) {
t.Error("Faild to load destinations", d)
}
case "ALL":
- if !reflect.DeepEqual(d.Prefixes, []string{`49`, `41`, `43`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`49`: nil, `41`: nil, `43`: nil}) {
t.Error("Faild to load destinations", d)
}
case "RET":
- if !reflect.DeepEqual(d.Prefixes, []string{`0723`, `0724`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`0723`: nil, `0724`: nil}) {
t.Error("Faild to load destinations", d)
}
case "GERMANY":
- if !reflect.DeepEqual(d.Prefixes, []string{`49`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`49`: nil}) {
t.Error("Faild to load destinations", d)
}
case "GERMANY_O2":
- if !reflect.DeepEqual(d.Prefixes, []string{`41`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`41`: nil}) {
t.Error("Faild to load destinations", d)
}
case "GERMANY_PREMIUM":
- if !reflect.DeepEqual(d.Prefixes, []string{`43`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`43`: nil}) {
t.Error("Faild to load destinations", d)
}
case "PSTN_71":
- if !reflect.DeepEqual(d.Prefixes, []string{`+4971`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`+4971`: nil}) {
t.Error("Faild to load destinations", d)
}
case "PSTN_72":
- if !reflect.DeepEqual(d.Prefixes, []string{`+4972`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`+4972`: nil}) {
t.Error("Faild to load destinations", d)
}
case "PSTN_70":
- if !reflect.DeepEqual(d.Prefixes, []string{`+4970`}) {
+ if !reflect.DeepEqual(d.Prefixes, map[string]interface{}{`+4970`: nil}) {
t.Error("Faild to load destinations", d)
}
default:
@@ -554,7 +554,7 @@ func TestLoadRatingProfiles(t *testing.T) {
RatingPlanActivations: RatingPlanActivations{&RatingPlanActivation{
ActivationTime: time.Date(2013, 10, 1, 0, 0, 0, 0, time.UTC),
RatingPlanId: "TDRT",
- FallbackKeys: []string{"*out:test:0:rif", "*out:test:0:danb"},
+ FallbackKeys: []string{"*out:test:0:danb", "*out:test:0:rif"},
}},
}
if !reflect.DeepEqual(rp, expected) {
diff --git a/engine/storage_map.go b/engine/storage_map.go
index 52ea74cc8..a318c29f6 100644
--- a/engine/storage_map.go
+++ b/engine/storage_map.go
@@ -46,6 +46,9 @@ func (ms *MapStorage) Flush() error {
}
func (ms *MapStorage) PreCache(dKeys, rpKeys, rpfKeys, actKeys []string) error {
+ if dKeys == nil && rpKeys == nil && rpfKeys == nil && actKeys == nil {
+ cache2go.Flush()
+ }
for k, _ := range ms.dict {
if strings.HasPrefix(k, DESTINATION_PREFIX) {
cache2go.RemKey(k)
diff --git a/engine/storage_redis.go b/engine/storage_redis.go
index 60926c856..a5c792b16 100644
--- a/engine/storage_redis.go
+++ b/engine/storage_redis.go
@@ -69,6 +69,9 @@ func (rs *RedisStorage) Flush() (err error) {
}
func (rs *RedisStorage) PreCache(dKeys, rpKeys, rpfKeys, actKeys []string) (err error) {
+ if dKeys == nil && rpKeys == nil && rpfKeys == nil && actKeys == nil {
+ cache2go.Flush()
+ }
if dKeys == nil {
Logger.Info("Caching all destinations")
if dKeys, err = rs.db.Keys(DESTINATION_PREFIX + "*"); err != nil {
diff --git a/engine/storage_sql.go b/engine/storage_sql.go
index a3297c50e..3f57958d6 100644
--- a/engine/storage_sql.go
+++ b/engine/storage_sql.go
@@ -23,10 +23,11 @@ import (
"database/sql"
"encoding/json"
"fmt"
- "github.com/cgrates/cgrates/utils"
"io/ioutil"
"strings"
"time"
+
+ "github.com/cgrates/cgrates/utils"
)
type SQLStorage struct {
@@ -163,7 +164,7 @@ func (self *SQLStorage) GetTPDestination(tpid, destTag string) (*Destination, er
return nil, err
}
defer rows.Close()
- d := &Destination{Id: destTag}
+ d := &Destination{Id: destTag, Prefixes: make(map[string]interface{}, 1)}
i := 0
for rows.Next() {
i++ //Keep here a reference so we know we got at least one prefix
@@ -172,7 +173,7 @@ func (self *SQLStorage) GetTPDestination(tpid, destTag string) (*Destination, er
if err != nil {
return nil, err
}
- d.Prefixes = append(d.Prefixes, pref)
+ d.Prefixes[pref] = nil
}
if i == 0 {
return nil, nil
@@ -186,11 +187,13 @@ func (self *SQLStorage) SetTPDestination(tpid string, dest *Destination) error {
}
var buffer bytes.Buffer // Use bytes buffer istead of string concatenation since that becomes quite heavy on large prefixes
buffer.WriteString(fmt.Sprintf("INSERT INTO %s (tpid, tag, prefix) VALUES ", utils.TBL_TP_DESTINATIONS))
- for idx, prefix := range dest.Prefixes {
+ idx := 0
+ for prefix, _ := range dest.Prefixes {
if idx != 0 {
buffer.WriteRune(',')
}
buffer.WriteString(fmt.Sprintf("('%s','%s','%s')", tpid, dest.Id, prefix))
+ idx++
}
buffer.WriteString(" ON DUPLICATE KEY UPDATE prefix=values(prefix)")
if _, err := self.Db.Exec(buffer.String()); err != nil {
@@ -801,10 +804,10 @@ func (self *SQLStorage) GetTpDestinations(tpid, tag string) ([]*Destination, err
}
}
if dest == nil {
- dest = &Destination{Id: tag}
+ dest = &Destination{Id: tag, Prefixes: make(map[string]interface{}, 1)}
dests = append(dests, dest)
}
- dest.Prefixes = append(dest.Prefixes, prefix)
+ dest.Prefixes[prefix] = nil
}
return dests, nil
}
diff --git a/engine/storage_test.go b/engine/storage_test.go
index 3b5323e55..dfdd3c458 100644
--- a/engine/storage_test.go
+++ b/engine/storage_test.go
@@ -96,11 +96,13 @@ func TestStorageDestinationContainsPrefixNotExisting(t *testing.T) {
}
func TestPreCacheRefresh(t *testing.T) {
- storageGetter.SetDestination(&Destination{"T11", []string{"0"}})
+ storageGetter.SetDestination(&Destination{"T11", map[string]interface{}{"0": nil}})
storageGetter.GetDestination("T11", false)
- storageGetter.SetDestination(&Destination{"T11", []string{"1"}})
+ storageGetter.SetDestination(&Destination{"T11", map[string]interface{}{"1": nil}})
storageGetter.PreCache(nil, nil, nil, nil)
- if d, err := storageGetter.GetDestination("T11", false); err != nil || d.Prefixes[0] != "1" {
+ d, err := storageGetter.GetDestination("T11", false)
+ _, ok := d.Prefixes["1"]
+ if err != nil || !ok {
t.Error("Error refreshing cache:", d)
}
}
diff --git a/engine/tpimporter_csv.go b/engine/tpimporter_csv.go
index f2bfb401b..e0d7b3165 100644
--- a/engine/tpimporter_csv.go
+++ b/engine/tpimporter_csv.go
@@ -19,11 +19,12 @@ along with this program. If not, see
package engine
import (
- "github.com/cgrates/cgrates/utils"
"io"
"io/ioutil"
"log"
"strconv"
+
+ "github.com/cgrates/cgrates/utils"
)
// Import tariff plan from csv into storDb
@@ -114,7 +115,7 @@ func (self *TPCSVImporter) importDestinations(fn string) error {
}
continue
}
- dst := &Destination{record[0], []string{record[1]}}
+ dst := &Destination{record[0], map[string]interface{}{record[1]: nil}}
if err := self.StorDb.SetTPDestination(self.TPid, dst); err != nil {
if self.Verbose {
log.Printf("Ignoring line %d, storDb operational error: <%s> ", lineNr, err.Error())
diff --git a/engine/userbalance_test.go b/engine/userbalance_test.go
index 766820dbe..715d43e9b 100644
--- a/engine/userbalance_test.go
+++ b/engine/userbalance_test.go
@@ -24,8 +24,8 @@ import (
)
var (
- NAT = &Destination{Id: "NAT", Prefixes: []string{"0257", "0256", "0723"}}
- RET = &Destination{Id: "RET", Prefixes: []string{"0723", "0724"}}
+ NAT = &Destination{Id: "NAT", Prefixes: map[string]interface{}{"0257": nil, "0256": nil, "0723": nil}}
+ RET = &Destination{Id: "RET", Prefixes: map[string]interface{}{"0723": nil, "0724": nil}}
)
func init() {
diff --git a/utils/apitpdata.go b/utils/apitpdata.go
index 64fc6a1cd..eb0f3a7b9 100644
--- a/utils/apitpdata.go
+++ b/utils/apitpdata.go
@@ -20,14 +20,15 @@ package utils
import (
"fmt"
+ "sort"
"strings"
"time"
)
type TPDestination struct {
- TPid string // Tariff plan id
- DestinationId string // Destination id
- Prefixes []string // Prefixes attached to this destination
+ TPid string // Tariff plan id
+ DestinationId string // Destination id
+ Prefixes map[string]interface{} // Prefixes attached to this destination
}
// This file deals with tp_* data definition
@@ -171,17 +172,25 @@ type TPRatingActivation struct {
// Helper to return the subject fallback keys we need in dataDb
func FallbackSubjKeys(direction, tenant, tor, fallbackSubjects string) []string {
- var subjKeys []string
+ var sslice sort.StringSlice
if len(fallbackSubjects) != 0 {
- var sslice StringSlice
for _, fbs := range strings.Split(fallbackSubjects, string(FALLBACK_SEP)) {
newKey := fmt.Sprintf("%s:%s:%s:%s", direction, tenant, tor, fbs)
- if !sslice.Contains(newKey) {
- subjKeys = append(subjKeys, newKey)
- }
+ i := sslice.Search(newKey)
+ if i < len(sslice) && sslice[i] != newKey {
+ // not found so insert it
+ sslice = append(sslice, "")
+ copy(sslice[i+1:], sslice[i:])
+ sslice[i] = newKey
+ } else {
+ if i == len(sslice) {
+ // not found and at the end
+ sslice = append(sslice, newKey)
+ }
+ } // newKey was foundfound
}
}
- return subjKeys
+ return sslice
}
type AttrTPRatingProfileIds struct {
diff --git a/utils/coreutils.go b/utils/coreutils.go
index ca677bd15..d1407ff10 100644
--- a/utils/coreutils.go
+++ b/utils/coreutils.go
@@ -132,22 +132,12 @@ func RoundTo(whole, amount time.Duration) time.Duration {
return time.Duration((w - math.Mod(a, w)) + a)
}
-type StringSlice []string
-
-func (ss StringSlice) Contains(needle string) bool {
- for _, hay := range ss {
- if hay == needle {
- return true
- }
- }
- return false
-}
-
-func SplitPrefixInterface(prefix string) []interface{} {
- var subs []interface{}
+func SplitPrefix(prefix string) []string {
+ length := int(math.Max(float64(len(prefix)-1), 0))
+ subs := make([]string, length)
max := len(prefix)
- for i := 0; i < len(prefix)-1; i++ {
- subs = append(subs, prefix[:max-i])
+ for i := 0; i < length; i++ {
+ subs[i] = prefix[:max-i]
}
return subs
}
diff --git a/utils/utils_test.go b/utils/utils_test.go
index 94bde469d..d642bac42 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -235,14 +235,14 @@ func TestRound(t *testing.T) {
}
func TestSplitPrefix(t *testing.T) {
- a := SplitPrefixInterface("0123456789")
+ a := SplitPrefix("0123456789")
if len(a) != 9 {
t.Error("Error splitting prefix: ", a)
}
}
func TestSplitPrefixEmpty(t *testing.T) {
- a := SplitPrefixInterface("")
+ a := SplitPrefix("")
if len(a) != 0 {
t.Error("Error splitting prefix: ", a)
}