diff --git a/engine/activationperiod_test.go b/engine/activationperiod_test.go index 9c619c9cb..c1aff6236 100644 --- a/engine/activationperiod_test.go +++ b/engine/activationperiod_test.go @@ -205,177 +205,3 @@ func BenchmarkActivationPeriodStoreRestore(b *testing.B) { marsh.Unmarshal(result, ap1) } } - -func GetUB() *UserBalance { - uc := &UnitsCounter{ - Direction: OUTBOUND, - BalanceId: SMS, - Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, - } - at := &ActionTrigger{ - Id: "some_uuid", - BalanceId: CREDIT, - Direction: OUTBOUND, - ThresholdValue: 100.0, - DestinationId: "NAT", - Weight: 10.0, - ActionsId: "Commando", - } - var zeroTime time.Time - zeroTime = zeroTime.UTC() // for deep equal to find location - ub := &UserBalance{ - Id: "rif", - Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, - UnitCounters: []*UnitsCounter{uc, uc}, - ActionTriggers: ActionTriggerPriotityList{at, at, at}, - } - return ub -} - -func BenchmarkMarshallerJSONStoreRestore(b *testing.B) { - b.StopTimer() - d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) - i := &Interval{Months: []time.Month{time.February}, - MonthDays: []int{1}, - WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, - StartTime: "14:30:00", - EndTime: "15:00:00"} - ap := &ActivationPeriod{ActivationTime: d} - ap.AddInterval(i) - ub := GetUB() - - ap1 := ActivationPeriod{} - ub1 := &UserBalance{} - b.StartTimer() - ms := new(JSONMarshaler) - for i := 0; i < b.N; i++ { - result, _ := ms.Marshal(ap) - ms.Unmarshal(result, ap1) - result, _ = ms.Marshal(ub) - ms.Unmarshal(result, ub1) - } -} - -func BenchmarkMarshallerBSONStoreRestore(b *testing.B) { - b.StopTimer() - d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) - i := &Interval{Months: []time.Month{time.February}, - MonthDays: []int{1}, - WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, - StartTime: "14:30:00", - EndTime: "15:00:00"} - ap := &ActivationPeriod{ActivationTime: d} - ap.AddInterval(i) - ub := GetUB() - - ap1 := ActivationPeriod{} - ub1 := &UserBalance{} - b.StartTimer() - ms := new(BSONMarshaler) - for i := 0; i < b.N; i++ { - result, _ := ms.Marshal(ap) - ms.Unmarshal(result, ap1) - result, _ = ms.Marshal(ub) - ms.Unmarshal(result, ub1) - } -} - -func BenchmarkMarshallerJSONBufStoreRestore(b *testing.B) { - b.StopTimer() - d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) - i := &Interval{Months: []time.Month{time.February}, - MonthDays: []int{1}, - WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, - StartTime: "14:30:00", - EndTime: "15:00:00"} - ap := &ActivationPeriod{ActivationTime: d} - ap.AddInterval(i) - ub := GetUB() - - ap1 := ActivationPeriod{} - ub1 := &UserBalance{} - b.StartTimer() - ms := new(JSONBufMarshaler) - for i := 0; i < b.N; i++ { - result, _ := ms.Marshal(ap) - ms.Unmarshal(result, ap1) - result, _ = ms.Marshal(ub) - ms.Unmarshal(result, ub1) - } -} - -func BenchmarkMarshallerGOBStoreRestore(b *testing.B) { - b.StopTimer() - d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) - i := &Interval{Months: []time.Month{time.February}, - MonthDays: []int{1}, - WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, - StartTime: "14:30:00", - EndTime: "15:00:00"} - ap := &ActivationPeriod{ActivationTime: d} - ap.AddInterval(i) - ub := GetUB() - - ap1 := ActivationPeriod{} - ub1 := &UserBalance{} - b.StartTimer() - ms := new(GOBMarshaler) - for i := 0; i < b.N; i++ { - result, _ := ms.Marshal(ap) - ms.Unmarshal(result, ap1) - result, _ = ms.Marshal(ub) - ms.Unmarshal(result, ub1) - } -} - -func BenchmarkMarshallerMsgpackStoreRestore(b *testing.B) { - b.StopTimer() - d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) - i := &Interval{Months: []time.Month{time.February}, - MonthDays: []int{1}, - WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, - StartTime: "14:30:00", - EndTime: "15:00:00"} - ap := &ActivationPeriod{ActivationTime: d} - ap.AddInterval(i) - ub := GetUB() - - ap1 := ActivationPeriod{} - ub1 := &UserBalance{} - b.StartTimer() - ms := new(MsgpackMarshaler) - for i := 0; i < b.N; i++ { - result, _ := ms.Marshal(ap) - ms.Unmarshal(result, ap1) - - result, _ = ms.Marshal(ub) - ms.Unmarshal(result, ub1) - } -} - -func BenchmarkMarshallerBincStoreRestore(b *testing.B) { - b.StopTimer() - d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) - i := &Interval{Months: []time.Month{time.February}, - MonthDays: []int{1}, - WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, - StartTime: "14:30:00", - EndTime: "15:00:00"} - ap := &ActivationPeriod{ActivationTime: d} - ap.AddInterval(i) - ub := GetUB() - - ap1 := ActivationPeriod{} - ub1 := &UserBalance{} - b.StartTimer() - ms := NewBincMarshaler() - for i := 0; i < b.N; i++ { - result, _ := ms.Marshal(ap) - ms.Unmarshal(result, ap1) - result, _ = ms.Marshal(ub) - ms.Unmarshal(result, ub1) - } -} diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index cffa333d5..57964414e 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -26,7 +26,7 @@ import ( ) var ( - marsh = new(MsgpackMarshaler) + marsh = NewCodecMsgpackMarshaler() ) func init() { diff --git a/engine/storage_interface.go b/engine/storage_interface.go index cdcdd24da..6fd6d740b 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -26,6 +26,8 @@ import ( "github.com/ugorji/go/codec" "github.com/vmihailenco/msgpack" "labix.org/v2/mgo/bson" + "reflect" + "time" ) const ( @@ -47,6 +49,13 @@ const ( RATER_SOURCE = "RAT" ) +var ( + // for codec msgpack + mapStrIntfTyp = reflect.TypeOf(map[string]interface{}(nil)) + sliceByteTyp = reflect.TypeOf([]byte(nil)) + timeTyp = reflect.TypeOf(time.Time{}) +) + /* Interface for storage providers. */ @@ -175,16 +184,38 @@ func (jm *MsgpackMarshaler) Unmarshal(data []byte, v interface{}) error { return msgpack.Unmarshal(data, v) } -type GoMsgpackMarshaler struct { +type CodecMsgpackMarshaler struct { mh *codec.MsgpackHandle } +func NewCodecMsgpackMarshaler() *CodecMsgpackMarshaler { + cmm := &CodecMsgpackMarshaler{new(codec.MsgpackHandle)} + mh := cmm.mh + mh.MapType = mapStrIntfTyp + + // configure extensions for msgpack, to enable Binary and Time support for tags 0 and 1 + mh.AddExt(sliceByteTyp, 0, mh.BinaryEncodeExt, mh.BinaryDecodeExt) + mh.AddExt(timeTyp, 1, mh.TimeEncodeExt, mh.TimeDecodeExt) + return cmm +} + +func (cmm *CodecMsgpackMarshaler) Marshal(v interface{}) (b []byte, err error) { + enc := codec.NewEncoderBytes(&b, cmm.mh) + err = enc.Encode(v) + return +} + +func (cmm *CodecMsgpackMarshaler) Unmarshal(data []byte, v interface{}) error { + dec := codec.NewDecoderBytes(data, cmm.mh) + return dec.Decode(&v) +} + type BincMarshaler struct { bh *codec.BincHandle } func NewBincMarshaler() *BincMarshaler { - return &BincMarshaler{&codec.BincHandle{}} + return &BincMarshaler{new(codec.BincHandle)} } func (bm *BincMarshaler) Marshal(v interface{}) (b []byte, err error) { diff --git a/engine/storage_map.go b/engine/storage_map.go index 40192bbc5..62245e231 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -33,7 +33,7 @@ type MapStorage struct { } func NewMapStorage() (DataStorage, error) { - return &MapStorage{dict: make(map[string][]byte), ms: new(MsgpackMarshaler)}, nil + return &MapStorage{dict: make(map[string][]byte), ms: NewCodecMsgpackMarshaler()}, nil } func (ms *MapStorage) Close() {} diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 9f1d107d7..863520407 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -57,7 +57,7 @@ func NewRedisStorage(address string, db int, pass string) (DataStorage, error) { return nil, err } } - return &RedisStorage{db: ndb, dbNb: db, ms: new(MsgpackMarshaler)}, nil + return &RedisStorage{db: ndb, dbNb: db, ms: NewCodecMsgpackMarshaler()}, nil } func (rs *RedisStorage) Close() { diff --git a/engine/storage_test.go b/engine/storage_test.go new file mode 100644 index 000000000..eb15087b8 --- /dev/null +++ b/engine/storage_test.go @@ -0,0 +1,255 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2013 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITH*out ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package engine + +import ( + "testing" + "time" +) + +func TestMsgpackStructs(t *testing.T) { + var a = struct{ First string }{"test"} + var b = struct { + First string + Second string + }{} + m := NewCodecMsgpackMarshaler() + buf, err := m.Marshal(&a) + if err != nil { + t.Error("error marshaling structure: ", err) + } + err = m.Unmarshal(buf, &b) + if err != nil || b.First != "test" || b.Second != "" { + t.Error("error unmarshalling structure: ", b, err) + } +} + +func TestMsgpackTime(t *testing.T) { + t1 := time.Date(2013, 8, 28, 22, 27, 0, 0, time.UTC) + m := NewCodecMsgpackMarshaler() + buf, err := m.Marshal(&t1) + if err != nil { + t.Error("error marshaling structure: ", err) + } + var t2 time.Time + err = m.Unmarshal(buf, &t2) + if err != nil || t1 != t2 || !t1.Equal(t2) { + t.Errorf("error unmarshalling structure: %#v %#v %v", t1, t2, err) + } +} + +/************************** Benchmarks *****************************/ + +func GetUB() *UserBalance { + uc := &UnitsCounter{ + Direction: OUTBOUND, + BalanceId: SMS, + Units: 100, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, + } + at := &ActionTrigger{ + Id: "some_uuid", + BalanceId: CREDIT, + Direction: OUTBOUND, + ThresholdValue: 100.0, + DestinationId: "NAT", + Weight: 10.0, + ActionsId: "Commando", + } + var zeroTime time.Time + zeroTime = zeroTime.UTC() // for deep equal to find location + ub := &UserBalance{ + Id: "rif", + Type: UB_TYPE_POSTPAID, + BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}}, + MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}}, + UnitCounters: []*UnitsCounter{uc, uc}, + ActionTriggers: ActionTriggerPriotityList{at, at, at}, + } + return ub +} + +func BenchmarkMarshallerJSONStoreRestore(b *testing.B) { + b.StopTimer() + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{Months: []time.Month{time.February}, + MonthDays: []int{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + ub := GetUB() + + ap1 := ActivationPeriod{} + ub1 := &UserBalance{} + b.StartTimer() + ms := new(JSONMarshaler) + for i := 0; i < b.N; i++ { + result, _ := ms.Marshal(ap) + ms.Unmarshal(result, ap1) + result, _ = ms.Marshal(ub) + ms.Unmarshal(result, ub1) + } +} + +func BenchmarkMarshallerBSONStoreRestore(b *testing.B) { + b.StopTimer() + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{Months: []time.Month{time.February}, + MonthDays: []int{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + ub := GetUB() + + ap1 := ActivationPeriod{} + ub1 := &UserBalance{} + b.StartTimer() + ms := new(BSONMarshaler) + for i := 0; i < b.N; i++ { + result, _ := ms.Marshal(ap) + ms.Unmarshal(result, ap1) + result, _ = ms.Marshal(ub) + ms.Unmarshal(result, ub1) + } +} + +func BenchmarkMarshallerJSONBufStoreRestore(b *testing.B) { + b.StopTimer() + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{Months: []time.Month{time.February}, + MonthDays: []int{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + ub := GetUB() + + ap1 := ActivationPeriod{} + ub1 := &UserBalance{} + b.StartTimer() + ms := new(JSONBufMarshaler) + for i := 0; i < b.N; i++ { + result, _ := ms.Marshal(ap) + ms.Unmarshal(result, ap1) + result, _ = ms.Marshal(ub) + ms.Unmarshal(result, ub1) + } +} + +func BenchmarkMarshallerGOBStoreRestore(b *testing.B) { + b.StopTimer() + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{Months: []time.Month{time.February}, + MonthDays: []int{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + ub := GetUB() + + ap1 := ActivationPeriod{} + ub1 := &UserBalance{} + b.StartTimer() + ms := new(GOBMarshaler) + for i := 0; i < b.N; i++ { + result, _ := ms.Marshal(ap) + ms.Unmarshal(result, ap1) + result, _ = ms.Marshal(ub) + ms.Unmarshal(result, ub1) + } +} + +func BenchmarkMarshallerMsgpackStoreRestore(b *testing.B) { + b.StopTimer() + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{Months: []time.Month{time.February}, + MonthDays: []int{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + ub := GetUB() + + ap1 := ActivationPeriod{} + ub1 := &UserBalance{} + b.StartTimer() + ms := new(MsgpackMarshaler) + for i := 0; i < b.N; i++ { + result, _ := ms.Marshal(ap) + ms.Unmarshal(result, ap1) + + result, _ = ms.Marshal(ub) + ms.Unmarshal(result, ub1) + } +} + +func BenchmarkMarshallerCodecMsgpackStoreRestore(b *testing.B) { + b.StopTimer() + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{Months: []time.Month{time.February}, + MonthDays: []int{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + ub := GetUB() + + ap1 := ActivationPeriod{} + ub1 := &UserBalance{} + b.StartTimer() + ms := NewCodecMsgpackMarshaler() + for i := 0; i < b.N; i++ { + result, _ := ms.Marshal(ap) + ms.Unmarshal(result, ap1) + result, _ = ms.Marshal(ub) + ms.Unmarshal(result, ub1) + } +} + +func BenchmarkMarshallerBincStoreRestore(b *testing.B) { + b.StopTimer() + d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC) + i := &Interval{Months: []time.Month{time.February}, + MonthDays: []int{1}, + WeekDays: []time.Weekday{time.Wednesday, time.Thursday}, + StartTime: "14:30:00", + EndTime: "15:00:00"} + ap := &ActivationPeriod{ActivationTime: d} + ap.AddInterval(i) + ub := GetUB() + + ap1 := ActivationPeriod{} + ub1 := &UserBalance{} + b.StartTimer() + ms := NewBincMarshaler() + for i := 0; i < b.N; i++ { + result, _ := ms.Marshal(ap) + ms.Unmarshal(result, ap1) + result, _ = ms.Marshal(ub) + ms.Unmarshal(result, ub1) + } +} diff --git a/engine/userbalance.go b/engine/userbalance.go index d65846fd0..33414838a 100644 --- a/engine/userbalance.go +++ b/engine/userbalance.go @@ -197,6 +197,7 @@ If the amount is bigger than the sum of all seconds in the minute buckets than n debited and an error will be returned. */ func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string, count bool) error { + // amount should be rounded at whole minutes?? if count { ub.countUnits(&Action{BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Seconds: amount, DestinationId: prefix}}) } diff --git a/engine/userbalance_test.go b/engine/userbalance_test.go index 661d8a279..2c59df69d 100644 --- a/engine/userbalance_test.go +++ b/engine/userbalance_test.go @@ -48,7 +48,7 @@ func populateTestActionsForTriggers() { func TestBalanceStoreRestore(t *testing.T) { b := &Balance{Value: 14, Weight: 1, Id: "test", ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)} - marsh := new(MsgpackMarshaler) + marsh := NewCodecMsgpackMarshaler() output, err := marsh.Marshal(b) if err != nil { t.Error("Error storing balance: ", err)