diff --git a/cmd/loader/loader.go b/cmd/loader/loader.go
index cca8528f4..1a2dd75f1 100644
--- a/cmd/loader/loader.go
+++ b/cmd/loader/loader.go
@@ -18,6 +18,8 @@ along with this program. If not, see
package main
import (
+ "bytes"
+ //"encoding/gob"
"encoding/json"
"flag"
"github.com/rif/cgrates/timespans"
@@ -39,6 +41,27 @@ var (
ubfile = flag.String("ubfile", "ub.json", "User budgets file")
)
+func testGob(key string, aps []*timespans.ActivationPeriod) {
+ var buf bytes.Buffer
+ enc := json.NewEncoder(&buf)
+ dec := json.NewDecoder(&buf)
+
+ enc.Encode(aps)
+ result := buf.String()
+
+ aps1 := make([]*timespans.ActivationPeriod, 0)
+ buf.Reset()
+ buf.WriteString(result)
+ err := dec.Decode(&aps1)
+ log.Print("Err: ", err)
+
+ buf.Reset()
+ enc.Encode(aps1)
+ result1 := buf.String()
+
+ log.Print("Equal? ", result == result1, len(result), len(result1))
+}
+
func writeToStorage(storage timespans.StorageGetter,
callDescriptors []*timespans.CallDescriptor,
destinations []*timespans.Destination,
@@ -47,6 +70,7 @@ func writeToStorage(storage timespans.StorageGetter,
for _, cd := range callDescriptors {
storage.SetActivationPeriods(cd.GetKey(), cd.ActivationPeriods)
log.Printf("Storing activation periods for %q", cd.GetKey())
+ testGob(cd.GetKey(), cd.ActivationPeriods)
}
for _, d := range destinations {
storage.SetDestination(d)
@@ -64,7 +88,6 @@ func writeToStorage(storage timespans.StorageGetter,
func main() {
flag.Parse()
-
log.Printf("Reading from %s, %s, %s", *apfile, *destfile, *tpfile)
// reading activation periods
diff --git a/timespans/activationperiod.go b/timespans/activationperiod.go
index 613e69e2d..9f8b4b0dd 100644
--- a/timespans/activationperiod.go
+++ b/timespans/activationperiod.go
@@ -18,8 +18,6 @@ along with this program. If not, see
package timespans
import (
- "strconv"
- "strings"
"time"
//"log"
)
@@ -40,57 +38,3 @@ func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
ap.Intervals = append(ap.Intervals, i)
}
}
-
-/*
-Serializes the activation periods for the storage. Used for key-value storages.
-*/
-func (ap *ActivationPeriod) store() (result string) {
- result += strconv.FormatInt(ap.ActivationTime.UnixNano(), 10) + ";"
- for _, i := range ap.Intervals {
- var is string
- is = strconv.Itoa(int(i.Month)) + "|"
- is += strconv.Itoa(i.MonthDay) + "|"
- for _, wd := range i.WeekDays {
- is += strconv.Itoa(int(wd)) + ","
- }
- is = strings.TrimRight(is, ",") + "|"
- is += i.StartTime + "|"
- is += i.EndTime + "|"
- is += strconv.FormatFloat(i.Ponder, 'f', -1, 64) + "|"
- is += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + "|"
- is += strconv.FormatFloat(i.Price, 'f', -1, 64) + "|"
- is += strconv.FormatFloat(i.BillingUnit, 'f', -1, 64)
- result += is + ";"
- }
- return
-}
-
-/*
-De-serializes the activation periods for the storage. Used for key-value storages.
-*/
-func (ap *ActivationPeriod) restore(input string) {
- elements := strings.Split(input, ";")
- unixNano, _ := strconv.ParseInt(elements[0], 10, 64)
- ap.ActivationTime = time.Unix(0, unixNano).In(time.UTC)
- for _, is := range elements[1 : len(elements)-1] {
- i := &Interval{}
- ise := strings.Split(is, "|")
- month, _ := strconv.Atoi(ise[0])
- i.Month = time.Month(month)
- i.MonthDay, _ = strconv.Atoi(ise[1])
- for _, d := range strings.Split(ise[2], ",") {
- if d != "" {
- wd, _ := strconv.Atoi(d)
- i.WeekDays = append(i.WeekDays, time.Weekday(wd))
- }
- }
- i.StartTime = ise[3]
- i.EndTime = ise[4]
- i.Ponder, _ = strconv.ParseFloat(ise[5], 64)
- i.ConnectFee, _ = strconv.ParseFloat(ise[6], 64)
- i.Price, _ = strconv.ParseFloat(ise[7], 64)
- i.BillingUnit, _ = strconv.ParseFloat(ise[8], 64)
-
- ap.Intervals = append(ap.Intervals, i)
- }
-}
diff --git a/timespans/activationperiod_test.go b/timespans/activationperiod_test.go
index e30fef9a2..04a0b80e9 100644
--- a/timespans/activationperiod_test.go
+++ b/timespans/activationperiod_test.go
@@ -19,64 +19,9 @@ package timespans
import (
"testing"
- "time"
//"log"
)
func TestApStoreRestore(t *testing.T) {
- d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
- i := &Interval{Month: time.February,
- MonthDay: 1,
- WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
- StartTime: "14:30:00",
- EndTime: "15:00:00"}
- ap := &ActivationPeriod{ActivationTime: d}
- ap.AddInterval(i)
- result := ap.store()
- expected := "1328106601000000000;2|1|3,4|14:30:00|15:00:00|0|0|0|0;"
- if result != expected {
- t.Errorf("Expected %q was %q", expected, result)
- }
- ap1 := ActivationPeriod{}
- ap1.restore(result)
- if ap1.ActivationTime != ap.ActivationTime {
- t.Errorf("Expected %v was %v", ap.ActivationTime, ap1.ActivationTime)
- }
- i1 := ap1.Intervals[0]
- if i1.Month != i.Month {
- t.Errorf("Expected %q was %q", i.Month, i1.Month)
- }
- if i1.MonthDay != i.MonthDay {
- t.Errorf("Expected %q was %q", i.MonthDay, i1.MonthDay)
- }
- for j, wd := range i1.WeekDays {
- if wd != i1.WeekDays[j] {
- t.Errorf("Expected %q was %q", i.StartTime, i1.StartTime)
- }
- }
- if i1.StartTime != i.StartTime {
- t.Errorf("Expected %q was %q", i.StartTime, i1.StartTime)
- }
- if i1.EndTime != i.EndTime {
- t.Errorf("Expected %q was %q", i.EndTime, i1.EndTime)
- }
- if i1.Ponder != i.Ponder {
- t.Errorf("Expected %q was %q", i.Ponder, i1.Ponder)
- }
- if i1.ConnectFee != i.ConnectFee {
- t.Errorf("Expected %q was %q", i.ConnectFee, i1.ConnectFee)
- }
- if i1.Price != i.Price {
- t.Errorf("Expected %q was %q", i.Price, i1.Price)
- }
- if i1.BillingUnit != i.BillingUnit {
- t.Errorf("Expected %q was %q", i.BillingUnit, i1.BillingUnit)
- }
-}
-func BenchmarkActivationPeriodRestore(b *testing.B) {
- ap := ActivationPeriod{}
- for i := 0; i < b.N; i++ {
- ap.restore("1328106601;2|1|3,4|14:30:00|15:00:00|0|0|0|0;")
- }
}
diff --git a/timespans/calldesc.go b/timespans/calldesc.go
index 01ef57f15..888081061 100644
--- a/timespans/calldesc.go
+++ b/timespans/calldesc.go
@@ -86,9 +86,9 @@ func (cd *CallDescriptor) SetStorageGetter(sg StorageGetter) {
}
/*
-Restores the activation periods from storage.
+Restores the activation periods for the specified prefix from storage.
*/
-func (cd *CallDescriptor) RestoreFromStorage() (destPrefix string, err error) {
+func (cd *CallDescriptor) SearchStorageForPrefix() (destPrefix string, err error) {
cd.ActivationPeriods = make([]*ActivationPeriod, 0)
base := fmt.Sprintf("%s:%s:", cd.CstmId, cd.Subject)
destPrefix = cd.DestinationPrefix
@@ -200,14 +200,14 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS
Creates a CallCost structure with the cost nformation calculated for the received CallDescriptor.
*/
func (cd *CallDescriptor) GetCost() (*CallCost, error) {
- destPrefix, err := cd.RestoreFromStorage()
+ destPrefix, err := cd.SearchStorageForPrefix()
timespans := cd.splitInTimeSpans()
cost := 0.0
connectionFee := 0.0
for i, ts := range timespans {
- if ts.MinuteInfo == nil && i == 0 {
+ if i == 0 && ts.MinuteInfo == nil && ts.Interval != nil {
connectionFee = ts.Interval.ConnectFee
}
cost += ts.GetCost(cd)
@@ -227,7 +227,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
Returns the cost of a second in the present time conditions.
*/
func (cd *CallDescriptor) getPresentSecondCost() (cost float64, err error) {
- _, err = cd.RestoreFromStorage()
+ _, err = cd.SearchStorageForPrefix()
now := time.Now()
oneSecond, _ := time.ParseDuration("1s")
ts := &TimeSpan{TimeStart: now, TimeEnd: now.Add(oneSecond)}
@@ -245,7 +245,7 @@ and will decrease it by 10% for nine times. So if the user has little credit it
If the user has no credit then it will return 0.
*/
func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
- _, err = cd.RestoreFromStorage()
+ _, err = cd.SearchStorageForPrefix()
now := time.Now()
availableCredit, availableSeconds := 0.0, 0.0
if userBudget, err := cd.getUserBudget(); err == nil && userBudget != nil {
diff --git a/timespans/calldesc_test.go b/timespans/calldesc_test.go
index 04f5f6ddf..6ec227f8a 100644
--- a/timespans/calldesc_test.go
+++ b/timespans/calldesc_test.go
@@ -24,7 +24,7 @@ import (
)
func TestKyotoStoreRestore(t *testing.T) {
- getter, _ := NewKyotoStorage("test.kch")
+ getter, _ := NewRedisStorage("tcp:127.0.0.1:6379", 10)
defer getter.Close()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Month: time.February,
@@ -54,7 +54,7 @@ func TestKyotoSplitSpans(t *testing.T) {
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.RestoreFromStorage()
+ cd.SearchStorageForPrefix()
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Log(cd.ActivationPeriods)
@@ -70,7 +70,7 @@ func TestRedisSplitSpans(t *testing.T) {
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.RestoreFromStorage()
+ cd.SearchStorageForPrefix()
timespans := cd.splitInTimeSpans()
if len(timespans) != 2 {
t.Log(cd.ActivationPeriods)
@@ -136,6 +136,7 @@ func TestFullDestNotFound(t *testing.T) {
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.Log(cd.ActivationPeriods)
t.Errorf("Expected %v was %v", expected, result)
}
}
@@ -150,8 +151,7 @@ func TestMultipleActivationPeriods(t *testing.T) {
result, _ := cd.GetCost()
expected := &CallCost{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0257", Cost: 330, ConnectFee: 0}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
- t.Log(result.Timespans[0].ActivationPeriod)
- t.Log(result.Timespans[1].ActivationPeriod)
+ t.Log(result.Timespans)
t.Errorf("Expected %v was %v", expected, result)
}
}
@@ -298,7 +298,7 @@ func BenchmarkRedisRestoring(b *testing.B) {
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()
+ cd.SearchStorageForPrefix()
}
}
@@ -341,7 +341,7 @@ func BenchmarkKyotoRestoring(b *testing.B) {
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()
+ cd.SearchStorageForPrefix()
}
}
@@ -353,7 +353,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.RestoreFromStorage()
+ cd.SearchStorageForPrefix()
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.splitInTimeSpans()
diff --git a/timespans/kyoto_storage.go b/timespans/kyoto_storage.go
index eef3eb6c5..6a9d299ca 100644
--- a/timespans/kyoto_storage.go
+++ b/timespans/kyoto_storage.go
@@ -18,45 +18,52 @@ along with this program. If not, see
package timespans
import (
+ "bytes"
+ "encoding/gob"
"github.com/fsouza/gokabinet/kc"
- //"log"
- "strings"
+ // "log"
+ "sync"
)
type KyotoStorage struct {
- db *kc.DB
+ db *kc.DB
+ buf bytes.Buffer
+ enc *gob.Encoder
+ dec *gob.Decoder
+ mux sync.Mutex // we need norma lock because we reset the buf variable
}
func NewKyotoStorage(filaName string) (*KyotoStorage, error) {
ndb, err := kc.Open(filaName, kc.WRITE)
- return &KyotoStorage{db: ndb}, err
+ ks := &KyotoStorage{db: ndb}
+ ks.enc = gob.NewEncoder(&ks.buf)
+ ks.dec = gob.NewDecoder(&ks.buf)
+ return ks, err
}
func (ks *KyotoStorage) Close() {
ks.db.Close()
}
-func (ks *KyotoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
- values, err := ks.db.Get(key)
+func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
+ ks.mux.Lock()
+ defer ks.mux.Unlock()
- if err == nil {
- for _, ap_string := range strings.Split(values, "\n") {
- if len(ap_string) > 0 {
- ap := &ActivationPeriod{}
- ap.restore(ap_string)
- aps = append(aps, ap)
- }
- }
- }
- return aps, err
+ ks.buf.Reset()
+ ks.enc.Encode(aps)
+ return ks.db.Set(key, ks.buf.String())
}
-func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
- result := ""
- for _, ap := range aps {
- result += ap.store() + "\n"
- }
- return ks.db.Set(key, result)
+func (ks *KyotoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
+ ks.mux.Lock()
+ defer ks.mux.Unlock()
+
+ values, err := ks.db.Get(key)
+
+ ks.buf.Reset()
+ ks.buf.WriteString(values)
+ ks.dec.Decode(&aps)
+ return
}
func (ks *KyotoStorage) GetDestination(key string) (dest *Destination, err error) {
diff --git a/timespans/redis_storage.go b/timespans/redis_storage.go
index c70d5e0d1..8987d9865 100644
--- a/timespans/redis_storage.go
+++ b/timespans/redis_storage.go
@@ -18,47 +18,57 @@ along with this program. If not, see
package timespans
import (
+ "bytes"
+ "encoding/gob"
"github.com/simonz05/godis"
- "strings"
+ "log"
+ "sync"
)
type RedisStorage struct {
dbNb int
db *godis.Client
+ buf bytes.Buffer
+ enc *gob.Encoder
+ dec *gob.Decoder
+ mux sync.Mutex
}
func NewRedisStorage(address string, db int) (*RedisStorage, error) {
ndb := godis.New(address, db, "")
- return &RedisStorage{db: ndb, dbNb: db}, nil
+ rs := &RedisStorage{db: ndb, dbNb: db}
+
+ rs.enc = gob.NewEncoder(&rs.buf)
+ rs.dec = gob.NewDecoder(&rs.buf)
+ return rs, nil
}
func (rs *RedisStorage) Close() {
rs.db.Quit()
}
-func (rs *RedisStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, 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 len(ap_string) > 0 {
- ap := &ActivationPeriod{}
- ap.restore(ap_string)
- aps = append(aps, ap)
- }
- }
- }
- return aps, err
-}
-
func (rs *RedisStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
//.db.Select(rs.dbNb)
- result := ""
- for _, ap := range aps {
- result += ap.store() + "\n"
- }
- return rs.db.Set(key, result)
+ rs.mux.Lock()
+ defer rs.mux.Unlock()
+
+ rs.buf.Reset()
+ rs.enc.Encode(aps)
+ return rs.db.Set(key, rs.buf.Bytes())
+}
+
+func (rs *RedisStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
+ //rs.db.Select(rs.dbNb)
+ rs.mux.Lock()
+ defer rs.mux.Unlock()
+
+ elem, err := rs.db.Get(key)
+ rs.buf.Reset()
+ rs.buf.Write(elem.Bytes())
+
+ e := rs.dec.Decode(&aps)
+ log.Print(e)
+ return
}
func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error) {
diff --git a/timespans/test.kch b/timespans/test.kch
index 99f1e2ebf..4c956cefe 100644
Binary files a/timespans/test.kch and b/timespans/test.kch differ