From 73309262638a800a2ff65aff186de539a5e6615e Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 29 Sep 2017 08:46:20 +0300 Subject: [PATCH 1/5] Add methods for Threshold (get/set/remove) in Mongo Redis and StorageMap (DataDB) + test for them (onstor_it_test.go) --- engine/onstor_it_test.go | 32 ++++++++++++++++++++ engine/storage_interface.go | 3 ++ engine/storage_map.go | 45 +++++++++++++++++++++++++++++ engine/storage_mongo_datadb.go | 53 ++++++++++++++++++++++++++++++++-- engine/storage_redis.go | 42 +++++++++++++++++++++++++++ 5 files changed, 172 insertions(+), 3 deletions(-) diff --git a/engine/onstor_it_test.go b/engine/onstor_it_test.go index 3d6c3c6ea..4a69c302b 100644 --- a/engine/onstor_it_test.go +++ b/engine/onstor_it_test.go @@ -93,6 +93,7 @@ var sTestsOnStorIT = []func(t *testing.T){ testOnStorITCRUDStatQueueProfile, testOnStorITCRUDStoredStatQueue, testOnStorITCRUDThresholdProfile, + testOnStorITCRUDThreshold, } func TestOnStorITRedisConnect(t *testing.T) { @@ -2088,3 +2089,34 @@ func testOnStorITCRUDThresholdProfile(t *testing.T) { t.Error(rcvErr) } } + +func testOnStorITCRUDThreshold(t *testing.T) { + res := &Threshold{ + Tenant: "cgrates.org", + ID: "TH1", + LastExecuted: time.Date(2016, 10, 1, 0, 0, 0, 0, time.UTC).Local(), + WakeupTime: time.Date(2016, 10, 1, 0, 0, 0, 0, time.UTC).Local(), + } + if _, rcvErr := onStor.GetThreshold("cgrates.org", "TH1", true, utils.NonTransactional); rcvErr != nil && rcvErr != utils.ErrNotFound { + t.Error(rcvErr) + } + if err := onStor.SetThreshold(res); err != nil { + t.Error(err) + } + if rcv, err := onStor.GetThreshold("cgrates.org", "TH1", true, utils.NonTransactional); err != nil { + t.Error(err) + } else if !(reflect.DeepEqual(res, rcv)) { + t.Errorf("Expecting: %v, received: %v", res, rcv) + } + if rcv, err := onStor.GetThreshold("cgrates.org", "TH1", false, utils.NonTransactional); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(res, rcv) { + t.Errorf("Expecting: %v, received: %v", res, rcv) + } + if err := onStor.RemoveThreshold(res.Tenant, res.ID, utils.NonTransactional); err != nil { + t.Error(err) + } + if _, rcvErr := onStor.GetThreshold(res.Tenant, res.ID, true, utils.NonTransactional); rcvErr != utils.ErrNotFound { + t.Error(rcvErr) + } +} diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 4c4fc8028..4dc6ed658 100755 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -123,6 +123,9 @@ type DataDB interface { GetThresholdProfile(tenant string, ID string, skipCache bool, transID string) (tp *ThresholdProfile, err error) SetThresholdProfile(tp *ThresholdProfile) (err error) RemThresholdProfile(tenant, id, transactionID string) (err error) + GetThreshold(string, string, bool, string) (*Threshold, error) + SetThreshold(*Threshold) error + RemoveThreshold(string, string, string) error // CacheDataFromDB loads data to cache, prefix represents the cache prefix, IDs should be nil if all available data should be loaded CacheDataFromDB(prefix string, IDs []string, mustBeCached bool) error // ToDo: Move this to dataManager } diff --git a/engine/storage_map.go b/engine/storage_map.go index 92e30e05e..a97a70357 100755 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -1624,6 +1624,51 @@ func (ms *MapStorage) RemThresholdProfile(tenant, id, transactionID string) (err return } +func (ms *MapStorage) GetThreshold(tenant, id string, skipCache bool, transactionID string) (r *Threshold, err error) { + ms.mu.RLock() + defer ms.mu.RUnlock() + key := utils.ThresholdsPrefix + utils.ConcatenatedKey(tenant, id) + if !skipCache { + if x, ok := cache.Get(key); ok { + if x != nil { + return x.(*Threshold), nil + } + return nil, utils.ErrNotFound + } + } + values, ok := ms.dict[key] + if !ok { + cache.Set(key, nil, cacheCommit(transactionID), transactionID) + return nil, utils.ErrNotFound + } + err = ms.ms.Unmarshal(values, r) + if err != nil { + return nil, err + } + cache.Set(key, r, cacheCommit(transactionID), transactionID) + return +} + +func (ms *MapStorage) SetThreshold(r *Threshold) (err error) { + ms.mu.Lock() + defer ms.mu.Unlock() + result, err := ms.ms.Marshal(r) + if err != nil { + return err + } + ms.dict[utils.ThresholdsPrefix+utils.ConcatenatedKey(r.Tenant, r.ID)] = result + return +} + +func (ms *MapStorage) RemoveThreshold(tenant, id string, transactionID string) (err error) { + ms.mu.Lock() + defer ms.mu.Unlock() + key := utils.ThresholdsPrefix + utils.ConcatenatedKey(tenant, id) + delete(ms.dict, key) + cache.RemKey(key, cacheCommit(transactionID), transactionID) + return +} + func (ms *MapStorage) GetVersions(itm string) (vrs Versions, err error) { ms.mu.Lock() defer ms.mu.Unlock() diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index bcdca5bcc..2bc2a1ed4 100755 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -61,7 +61,8 @@ const ( colRes = "resources" colSqs = "statqueues" colSqp = "statqueue_profiles" - colTlds = "thresholds" + colTlds = "threshold_profiles" + colThs = "thresholds" ) var ( @@ -330,6 +331,7 @@ func (ms *MongoStorage) getColNameForPrefix(prefix string) (name string, ok bool utils.ResourcesPrefix: colRes, utils.ResourceProfilesPrefix: colRsP, utils.ThresholdProfilePrefix: colTlds, + utils.ThresholdsPrefix: colThs, } name, ok = colMap[prefix] return @@ -537,6 +539,9 @@ func (ms *MongoStorage) CacheDataFromDB(prfx string, ids []string, mustBeCached case utils.ThresholdProfilePrefix: tntID := utils.NewTenantID(dataID) _, err = ms.GetThresholdProfile(tntID.Tenant, tntID.ID, true, utils.NonTransactional) + case utils.ThresholdsPrefix: + tntID := utils.NewTenantID(dataID) + _, err = ms.GetThreshold(tntID.Tenant, tntID.ID, true, utils.NonTransactional) } if err != nil { return utils.NewCGRError(utils.MONGO, @@ -713,8 +718,8 @@ func (ms *MongoStorage) HasData(category, subject string) (has bool, err error) case utils.StatQueuePrefix: count, err = db.C(colRes).Find(bson.M{"id": subject}).Count() has = count > 0 - case utils.ThresholdProfilePrefix: - count, err = db.C(colTlds).Find(bson.M{"id": subject}).Count() + case utils.ThresholdsPrefix: + count, err = db.C(colThs).Find(bson.M{"id": subject}).Count() has = count > 0 default: err = fmt.Errorf("unsupported category in HasData: %s", category) @@ -2181,3 +2186,45 @@ func (ms *MongoStorage) RemThresholdProfile(tenant, id, transactionID string) (e cacheCommit(transactionID), transactionID) return } + +func (ms *MongoStorage) GetThreshold(tenant, id string, skipCache bool, transactionID string) (r *Threshold, err error) { + key := utils.ThresholdsPrefix + utils.ConcatenatedKey(tenant, id) + if !skipCache { + if x, ok := cache.Get(key); ok { + if x == nil { + return nil, utils.ErrNotFound + } + return x.(*Threshold), nil + } + } + session, col := ms.conn(colThs) + defer session.Close() + r = new(Threshold) + if err = col.Find(bson.M{"tenant": tenant, "id": id}).One(r); err != nil { + if err == mgo.ErrNotFound { + err = utils.ErrNotFound + cache.Set(key, nil, cacheCommit(transactionID), transactionID) + } + return nil, err + } + cache.Set(key, r, cacheCommit(transactionID), transactionID) + return +} + +func (ms *MongoStorage) SetThreshold(r *Threshold) (err error) { + session, col := ms.conn(colThs) + defer session.Close() + _, err = col.Upsert(bson.M{"tenant": r.Tenant, "id": r.ID}, r) + return +} + +func (ms *MongoStorage) RemoveThreshold(tenant, id string, transactionID string) (err error) { + session, col := ms.conn(colThs) + defer session.Close() + if err = col.Remove(bson.M{"tenant": tenant, "id": id}); err != nil { + return + } + cache.RemKey(utils.ThresholdsPrefix+utils.ConcatenatedKey(tenant, id), + cacheCommit(transactionID), transactionID) + return nil +} diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 450d6f940..710fe5442 100755 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -1734,6 +1734,48 @@ func (rs *RedisStorage) RemThresholdProfile(tenant, id, transactionID string) (e return } +func (rs *RedisStorage) GetThreshold(tenant, id string, skipCache bool, transactionID string) (r *Threshold, err error) { + key := utils.ThresholdsPrefix + utils.ConcatenatedKey(tenant, id) + if !skipCache { + if x, ok := cache.Get(key); ok { + if x == nil { + return nil, utils.ErrNotFound + } + return x.(*Threshold), nil + } + } + var values []byte + if values, err = rs.Cmd("GET", key).Bytes(); err != nil { + if err == redis.ErrRespNil { // did not find the destination + cache.Set(key, nil, cacheCommit(transactionID), transactionID) + err = utils.ErrNotFound + } + return + } + if err = rs.ms.Unmarshal(values, &r); err != nil { + return + } + cache.Set(key, r, cacheCommit(transactionID), transactionID) + return +} + +func (rs *RedisStorage) SetThreshold(r *Threshold) (err error) { + result, err := rs.ms.Marshal(r) + if err != nil { + return err + } + return rs.Cmd("SET", utils.ThresholdsPrefix+utils.ConcatenatedKey(r.Tenant, r.ID), result).Err +} + +func (rs *RedisStorage) RemoveThreshold(tenant, id string, transactionID string) (err error) { + key := utils.ThresholdsPrefix + utils.ConcatenatedKey(tenant, id) + if err = rs.Cmd("DEL", key).Err; err != nil { + return + } + cache.RemKey(key, cacheCommit(transactionID), transactionID) + return +} + func (rs *RedisStorage) GetStorageType() string { return utils.REDIS } From 7b0f535d65f5747119b0365b33168e54eacaa2e3 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 29 Sep 2017 09:32:27 +0300 Subject: [PATCH 2/5] Remove MinItems from ThresholdProfiles and add/remote time.Sleep from resourcesv1_it_test.go and tp_it_test.go --- apier/v1/resourcesv1_it_test.go | 2 +- apier/v1/tp_it_test.go | 3 ++- data/storage/mysql/create_tariffplan_tables.sql | 5 +---- data/storage/postgres/create_tariffplan_tables.sql | 5 +---- data/tariffplans/testtp/Thresholds.csv | 4 ++-- data/tariffplans/tutorial/Thresholds.csv | 4 ++-- engine/loader_csv_test.go | 5 ++--- engine/model_helpers.go | 5 ----- engine/model_helpers_test.go | 4 ---- engine/models.go | 11 +++++------ engine/onstor_it_test.go | 1 - engine/storage_mongo_datadb.go | 2 +- engine/thresholds.go | 1 - utils/apitpdata.go | 1 - 14 files changed, 17 insertions(+), 36 deletions(-) diff --git a/apier/v1/resourcesv1_it_test.go b/apier/v1/resourcesv1_it_test.go index 42a6b03ad..96860e325 100644 --- a/apier/v1/resourcesv1_it_test.go +++ b/apier/v1/resourcesv1_it_test.go @@ -122,7 +122,6 @@ func testV1RsRpcConn(t *testing.T) { func testV1RsFromFolder(t *testing.T) { var reply string - time.Sleep(time.Duration(2000) * time.Millisecond) attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")} if err := rlsV1Rpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { t.Error(err) @@ -132,6 +131,7 @@ func testV1RsFromFolder(t *testing.T) { } func testV1RsGetResourcesForEvent(t *testing.T) { + time.Sleep(time.Duration(1000) * time.Millisecond) var reply *[]*engine.ResourceProfile args := &utils.ArgRSv1ResourceUsage{ Tenant: "cgrates.org", diff --git a/apier/v1/tp_it_test.go b/apier/v1/tp_it_test.go index a5d7c4f7e..b4c5c628e 100644 --- a/apier/v1/tp_it_test.go +++ b/apier/v1/tp_it_test.go @@ -84,7 +84,7 @@ func testTPInitCfg(t *testing.T) { config.SetCgrConfig(tpCfg) switch tpConfigDIR { case "tutmongo": // Mongo needs more time to reset db, need to investigate - tpDelay = 2000 + tpDelay = 4000 default: tpDelay = 2000 } @@ -114,6 +114,7 @@ func testTPRpcConn(t *testing.T) { } func testTPImportTPFromFolderPath(t *testing.T) { + time.Sleep(time.Duration(1 * time.Second)) var reply string if err := tpRPC.Call("ApierV1.ImportTariffPlanFromFolder", utils.AttrImportTPFromFolder{TPid: "TEST_TPID2", FolderPath: path.Join(tpDataDir, "tariffplans", "tutorial")}, &reply); err != nil { t.Error("Got error on ApierV1.ImportTarrifPlanFromFolder: ", err.Error()) diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 8bd959e31..777109c9d 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -453,18 +453,15 @@ DROP TABLE IF EXISTS tp_thresholds; CREATE TABLE tp_thresholds ( `id` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, + `tenant` varchar(64) NOT NULL, `tag` varchar(64) NOT NULL, `filter_type` varchar(16) NOT NULL, `filter_field_name` varchar(64) NOT NULL, `filter_field_values` varchar(256) NOT NULL, `activation_interval` varchar(64) NOT NULL, - `threshold_type` char(64) NOT NULL, - `threshold_value` DECIMAL(20,4) NOT NULL, - `min_items` int(11) NOT NULL, `recurrent` BOOLEAN NOT NULL, `min_sleep` varchar(16) NOT NULL, `blocker` BOOLEAN NOT NULL, - `stored` BOOLEAN NOT NULL, `weight` decimal(8,2) NOT NULL, `action_ids` varchar(64) NOT NULL, `created_at` TIMESTAMP, diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index c2e9ef515..b9dbd986d 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -448,18 +448,15 @@ DROP TABLE IF EXISTS tp_thresholds; CREATE TABLE tp_thresholds ( "id" SERIAL PRIMARY KEY, "tpid" varchar(64) NOT NULL, + "tenant"varchar(64) NOT NULL, "tag" varchar(64) NOT NULL, "filter_type" varchar(16) NOT NULL, "filter_field_name" varchar(64) NOT NULL, "filter_field_values" varchar(256) NOT NULL, "activation_interval" varchar(64) NOT NULL, - "threshold_type" VARCHAR(64) NOT NULL, - "threshold_value" NUMERIC(20,4) NOT NULL, - "min_items" INTEGER NOT NULL, "recurrent" BOOLEAN NOT NULL, "min_sleep" varchar(16) NOT NULL, "blocker" BOOLEAN NOT NULL, - "stored" BOOLEAN NOT NULL, "weight" decimal(8,2) NOT NULL, "action_ids" varchar(64) NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE diff --git a/data/tariffplans/testtp/Thresholds.csv b/data/tariffplans/testtp/Thresholds.csv index 9e629d669..a422ddaa6 100644 --- a/data/tariffplans/testtp/Thresholds.csv +++ b/data/tariffplans/testtp/Thresholds.csv @@ -1,2 +1,2 @@ -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],MinItems[6],Recurrent[7],MinSleep[8],Blocker[9],Weight[10],ActionIDs[11] -cgrates.org,Threshold1,*string,Account,1001;1002,2014-07-29T15:00:00Z,10,true,1s,true,10,THRESH1;THRESH2 +#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],Recurrent[6],MinSleep[7],Blocker[8],Weight[9],ActionIDs[10] +cgrates.org,Threshold1,*string,Account,1001;1002,2014-07-29T15:00:00Z,true,1s,true,10,THRESH1;THRESH2 diff --git a/data/tariffplans/tutorial/Thresholds.csv b/data/tariffplans/tutorial/Thresholds.csv index 374a0342e..a422ddaa6 100644 --- a/data/tariffplans/tutorial/Thresholds.csv +++ b/data/tariffplans/tutorial/Thresholds.csv @@ -1,2 +1,2 @@ -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],MinItems[6],Recurrent[7],MinSleep[8],Blocker[9],Weight[10],MinItems[11],ActionIDs[12] -cgrates.org,Threshold1,*string,Account,1001;1002,2014-07-29T15:00:00Z,10,true,1s,true,10,THRESH1;THRESH2 +#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],Recurrent[6],MinSleep[7],Blocker[8],Weight[9],ActionIDs[10] +cgrates.org,Threshold1,*string,Account,1001;1002,2014-07-29T15:00:00Z,true,1s,true,10,THRESH1;THRESH2 diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 471030ccf..aedcc7624 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -278,8 +278,8 @@ cgrates.org,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*a ` thresholds = ` -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],MinItems[6],Recurrent[7],MinSleep[8],Blocker[9],Weight[10],ActionIDs[11] -cgrates.org,Threshold1,*string,Account,1001;1002,2014-07-29T15:00:00Z,10,true,1s,true,10,THRESH1;THRESH2 +#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],Recurrent[6],MinSleep[7],Blocker[8],Weight[9],ActionIDs[10] +cgrates.org,Threshold1,*string,Account,1001;1002,2014-07-29T15:00:00Z,true,1s,true,10,THRESH1;THRESH2 ` ) @@ -1482,7 +1482,6 @@ func TestLoadThresholds(t *testing.T) { ActivationInterval: &utils.TPActivationInterval{ ActivationTime: "2014-07-29T15:00:00Z", }, - MinItems: 10, Recurrent: true, MinSleep: "1s", Blocker: true, diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 235b98ea9..bc586d383 100755 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -2137,9 +2137,6 @@ func (tps TpThresholdS) AsTPThreshold() (result []*utils.TPThreshold) { if tp.ActionIDs != "" { th.ActionIDs = append(th.ActionIDs, strings.Split(tp.ActionIDs, utils.INFIELD_SEP)...) } - if tp.MinItems != 0 { - th.MinItems = tp.MinItems - } if tp.Weight != 0 { th.Weight = tp.Weight } @@ -2182,7 +2179,6 @@ func APItoModelTPThreshold(th *utils.TPThreshold) (mdls TpThresholdS) { if i == 0 { mdl.Blocker = th.Blocker mdl.Weight = th.Weight - mdl.MinItems = th.MinItems mdl.Recurrent = th.Recurrent mdl.MinSleep = th.MinSleep if th.ActivationInterval != nil { @@ -2220,7 +2216,6 @@ func APItoThresholdProfile(tpTH *utils.TPThreshold, timezone string) (th *Thresh th = &ThresholdProfile{ Tenant: tpTH.Tenant, ID: tpTH.ID, - MinItems: tpTH.MinItems, Recurrent: tpTH.Recurrent, Weight: tpTH.Weight, Blocker: tpTH.Blocker, diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index d126748b7..937f79f85 100755 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -940,7 +940,6 @@ func TestAsTPThresholdAsAsTPThreshold(t *testing.T) { FilterFieldName: "Account", FilterFieldValues: "1001;1002", ActivationInterval: "2014-07-29T15:00:00Z", - MinItems: 100, Recurrent: false, MinSleep: "1s", Blocker: false, @@ -962,7 +961,6 @@ func TestAsTPThresholdAsAsTPThreshold(t *testing.T) { ActivationInterval: &utils.TPActivationInterval{ ActivationTime: tps[0].ActivationInterval, }, - MinItems: tps[0].MinItems, MinSleep: tps[0].MinSleep, Recurrent: tps[0].Recurrent, Blocker: tps[0].Blocker, @@ -984,7 +982,6 @@ func TestAPItoTPThreshold(t *testing.T) { &utils.TPRequestFilter{Type: MetaString, FieldName: "Account", Values: []string{"1001", "1002"}}, }, ActivationInterval: &utils.TPActivationInterval{ActivationTime: "2014-07-29T15:00:00Z"}, - MinItems: 100, Recurrent: false, MinSleep: "1s", Blocker: false, @@ -995,7 +992,6 @@ func TestAPItoTPThreshold(t *testing.T) { eTPs := &ThresholdProfile{ ID: tps.ID, Filters: make([]*RequestFilter, len(tps.Filters)), - MinItems: tps.MinItems, Recurrent: tps.Recurrent, Blocker: tps.Blocker, Weight: tps.Weight, diff --git a/engine/models.go b/engine/models.go index 78ed32857..dad305048 100755 --- a/engine/models.go +++ b/engine/models.go @@ -508,11 +508,10 @@ type TpThreshold struct { FilterFieldName string `index:"3" re:""` FilterFieldValues string `index:"4" re:""` ActivationInterval string `index:"5" re:""` - MinItems int `index:"6" re:""` - Recurrent bool `index:"7" re:""` - MinSleep string `index:"8" re:""` - Blocker bool `index:"9" re:""` - Weight float64 `index:"10" re:"\d+\.?\d*"` - ActionIDs string `index:"11" re:""` + Recurrent bool `index:"6" re:""` + MinSleep string `index:"7" re:""` + Blocker bool `index:"8" re:""` + Weight float64 `index:"9" re:"\d+\.?\d*"` + ActionIDs string `index:"10" re:""` CreatedAt time.Time } diff --git a/engine/onstor_it_test.go b/engine/onstor_it_test.go index 4a69c302b..1703fab4d 100644 --- a/engine/onstor_it_test.go +++ b/engine/onstor_it_test.go @@ -2055,7 +2055,6 @@ func testOnStorITCRUDThresholdProfile(t *testing.T) { ID: "test", ActivationInterval: &utils.ActivationInterval{}, Filters: []*RequestFilter{}, - MinItems: 10, Recurrent: true, MinSleep: timeMinSleep, Blocker: true, diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 2bc2a1ed4..903899e3e 100755 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -718,7 +718,7 @@ func (ms *MongoStorage) HasData(category, subject string) (has bool, err error) case utils.StatQueuePrefix: count, err = db.C(colRes).Find(bson.M{"id": subject}).Count() has = count > 0 - case utils.ThresholdsPrefix: + case utils.ThresholdProfilePrefix: count, err = db.C(colThs).Find(bson.M{"id": subject}).Count() has = count > 0 default: diff --git a/engine/thresholds.go b/engine/thresholds.go index 80469e372..176565119 100644 --- a/engine/thresholds.go +++ b/engine/thresholds.go @@ -37,7 +37,6 @@ type ThresholdProfile struct { ID string Filters []*RequestFilter // Filters for the request ActivationInterval *utils.ActivationInterval // Time when this limit becomes active and expires - MinItems int // number of items agregated for the threshold to match Recurrent bool MinSleep time.Duration Blocker bool // blocker flag to stop processing on filters matched diff --git a/utils/apitpdata.go b/utils/apitpdata.go index cf7e209a8..48d5894b2 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1360,7 +1360,6 @@ type TPThreshold struct { ID string Filters []*TPRequestFilter // Filters for the request ActivationInterval *TPActivationInterval // Time when this limit becomes active and expires - MinItems int // number of items agregated for the threshold to match Recurrent bool MinSleep string Blocker bool // blocker flag to stop processing on filters matched From 05a009c5e18287647d0857d1505b575d4a7129b3 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 29 Sep 2017 15:15:38 +0300 Subject: [PATCH 3/5] Add MinItems in Stat and update stats_it_test.go --- apier/v1/stats_it_test.go | 45 ++-- apier/v1/tpstats_it_test.go | 1 + .../mysql/create_tariffplan_tables.sql | 1 + .../postgres/create_tariffplan_tables.sql | 1 + data/tariffplans/testtp/Stats.csv | 4 +- data/tariffplans/tutorial/Stats.csv | 3 +- engine/libstats.go | 7 +- engine/loader_csv_test.go | 7 +- engine/model_helpers.go | 24 +- engine/model_helpers_test.go | 4 + engine/models.go | 3 +- engine/statmetrics.go | 211 ++++++++++-------- engine/statmetrics_test.go | 125 ++++++----- engine/stats_metrics.go | 2 +- engine/stordb_it_test.go | 1 + engine/tp_reader.go | 2 +- utils/apitpdata.go | 1 + 17 files changed, 257 insertions(+), 185 deletions(-) diff --git a/apier/v1/stats_it_test.go b/apier/v1/stats_it_test.go index a25483f9a..d7e383521 100644 --- a/apier/v1/stats_it_test.go +++ b/apier/v1/stats_it_test.go @@ -145,7 +145,7 @@ func testV1STSFromFolder(t *testing.T) { func testV1STSGetStats(t *testing.T) { var reply []string - expectedIDs := []string{"STATS_1"} + expectedIDs := []string{"Stats1"} if err := stsV1Rpc.Call("StatSV1.GetQueueIDs", "cgrates.org", &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedIDs, reply) { @@ -184,6 +184,21 @@ func testV1STSProcessEvent(t *testing.T) { } else if reply != utils.OK { t.Errorf("received reply: %s", reply) } + //add an event (should be N/A becaus MinItems is 2) + expectedMetrics := map[string]string{ + utils.MetaASR: utils.NOT_AVAILABLE, + utils.MetaACD: utils.NOT_AVAILABLE, + utils.MetaTCC: utils.NOT_AVAILABLE, + utils.MetaTCD: utils.NOT_AVAILABLE, + utils.MetaACC: utils.NOT_AVAILABLE, + utils.MetaPDD: utils.NOT_AVAILABLE, + } + var metrics map[string]string + if err := stsV1Rpc.Call("StatSV1.GetQueueStringMetrics", &utils.TenantID{Tenant: "cgrates.org", ID: "Stats1"}, &metrics); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedMetrics, metrics) { + t.Errorf("expecting: %+v, received reply: %s", expectedMetrics, metrics) + } ev2 := engine.StatEvent{ Tenant: "cgrates.org", ID: "event2", @@ -208,7 +223,7 @@ func testV1STSProcessEvent(t *testing.T) { } else if reply != utils.OK { t.Errorf("received reply: %s", reply) } - expectedMetrics := map[string]string{ + expectedMetrics2 := map[string]string{ utils.MetaASR: "66.66667%", utils.MetaACD: "1m30s", utils.MetaACC: "61.5", @@ -216,11 +231,11 @@ func testV1STSProcessEvent(t *testing.T) { utils.MetaTCC: "123", utils.MetaPDD: "4s", } - var metrics map[string]string - if err := stsV1Rpc.Call("StatSV1.GetQueueStringMetrics", &utils.TenantID{Tenant: "cgrates.org", ID: "STATS_1"}, &metrics); err != nil { + var metrics2 map[string]string + if err := stsV1Rpc.Call("StatSV1.GetQueueStringMetrics", &utils.TenantID{Tenant: "cgrates.org", ID: "Stats1"}, &metrics2); err != nil { t.Error(err) - } else if !reflect.DeepEqual(expectedMetrics, metrics) { - t.Errorf("expecting: %+v, received reply: %s", expectedMetrics, metrics) + } else if !reflect.DeepEqual(expectedMetrics2, metrics2) { + t.Errorf("expecting: %+v, received reply: %s", expectedMetrics2, metrics2) } } @@ -235,7 +250,7 @@ func testV1STSGetStatsAfterRestart(t *testing.T) { } var metrics map[string]string //get stats metrics before restart - if err := stsV1Rpc.Call("StatSV1.GetQueueStringMetrics", &utils.TenantID{Tenant: "cgrates.org", ID: "STATS_1"}, &metrics); err != nil { + if err := stsV1Rpc.Call("StatSV1.GetQueueStringMetrics", &utils.TenantID{Tenant: "cgrates.org", ID: "Stats1"}, &metrics); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedMetrics, metrics) { t.Errorf("expecting: %+v, received reply: %s", expectedMetrics, metrics) @@ -258,10 +273,10 @@ func testV1STSGetStatsAfterRestart(t *testing.T) { utils.MetaPDD: "4s", } var metrics2 map[string]string - if err := stsV1Rpc.Call("StatSV1.GetQueueStringMetrics", &utils.TenantID{Tenant: "cgrates.org", ID: "STATS_1"}, &metrics2); err != nil { + if err := stsV1Rpc.Call("StatSV1.GetQueueStringMetrics", &utils.TenantID{Tenant: "cgrates.org", ID: "Stats1"}, &metrics2); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectedMetrics2, metrics2) { - t.Errorf("expecting: %+v, received reply: %s", expectedMetrics2, metrics2) + t.Errorf("After restat expecting: %+v, received reply: %s", expectedMetrics2, metrics2) } time.Sleep(time.Duration(1 * time.Second)) } @@ -269,7 +284,7 @@ func testV1STSGetStatsAfterRestart(t *testing.T) { func testV1STSSetStatQueueProfile(t *testing.T) { var reply *engine.StatQueueProfile if err := stsV1Rpc.Call("ApierV1.GetStatQueueProfile", - &utils.TenantID{"cgrates.org", "TEST_PROFILE1"}, &reply); err == nil || + &utils.TenantID{Tenant: "cgrates.org", ID: "TEST_PROFILE1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } @@ -294,6 +309,7 @@ func testV1STSSetStatQueueProfile(t *testing.T) { Blocker: true, Stored: true, Weight: 20, + MinItems: 1, } var result string if err := stsV1Rpc.Call("ApierV1.SetStatQueueProfile", statConfig, &result); err != nil { @@ -302,7 +318,7 @@ func testV1STSSetStatQueueProfile(t *testing.T) { t.Error("Unexpected reply returned", result) } if err := stsV1Rpc.Call("ApierV1.GetStatQueueProfile", - &utils.TenantID{"cgrates.org", "TEST_PROFILE1"}, &reply); err != nil { + &utils.TenantID{Tenant: "cgrates.org", ID: "TEST_PROFILE1"}, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(statConfig, reply) { t.Errorf("Expecting: %+v, received: %+v", statConfig, reply) @@ -333,9 +349,10 @@ func testV1STSUpdateStatQueueProfile(t *testing.T) { } else if result != utils.OK { t.Error("Unexpected reply returned", result) } + time.Sleep(time.Duration(1 * time.Second)) var reply *engine.StatQueueProfile if err := stsV1Rpc.Call("ApierV1.GetStatQueueProfile", - &utils.TenantID{statConfig.Tenant, statConfig.ID}, &reply); err != nil { + &utils.TenantID{Tenant: "cgrates.org", ID: "TEST_PROFILE1"}, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(statConfig, reply) { t.Errorf("Expecting: %+v, received: %+v", statConfig, reply) @@ -345,14 +362,14 @@ func testV1STSUpdateStatQueueProfile(t *testing.T) { func testV1STSRemoveStatQueueProfile(t *testing.T) { var resp string if err := stsV1Rpc.Call("ApierV1.RemStatQueueProfile", - &utils.TenantID{statConfig.Tenant, statConfig.ID}, &resp); err != nil { + &utils.TenantID{Tenant: "cgrates.org", ID: "TEST_PROFILE1"}, &resp); err != nil { t.Error(err) } else if resp != utils.OK { t.Error("Unexpected reply returned", resp) } var sqp *engine.StatQueueProfile if err := stsV1Rpc.Call("ApierV1.GetStatQueueProfile", - &utils.TenantID{statConfig.Tenant, statConfig.ID}, sqp); err == nil || err.Error() != utils.ErrNotFound.Error() { + &utils.TenantID{Tenant: "cgrates.org", ID: "TEST_PROFILE1"}, &sqp); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } } diff --git a/apier/v1/tpstats_it_test.go b/apier/v1/tpstats_it_test.go index 2b04d26b5..b4639edd7 100644 --- a/apier/v1/tpstats_it_test.go +++ b/apier/v1/tpstats_it_test.go @@ -146,6 +146,7 @@ func testTPStatsSetTPStat(t *testing.T) { Blocker: false, Stored: false, Weight: 20, + MinItems: 1, Thresholds: []string{"ThreshValue", "ThreshValueTwo"}, } var result string diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 777109c9d..371add7f2 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -438,6 +438,7 @@ CREATE TABLE tp_stats ( `blocker` BOOLEAN NOT NULL, `stored` BOOLEAN NOT NULL, `weight` decimal(8,2) NOT NULL, + `min_items` int(11) NOT NULL, `thresholds` varchar(64) NOT NULL, `created_at` TIMESTAMP, PRIMARY KEY (`id`), diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index b9dbd986d..55a71e5c9 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -434,6 +434,7 @@ CREATE TABLE tp_stats ( "blocker" BOOLEAN NOT NULL, "stored" BOOLEAN NOT NULL, "weight" decimal(8,2) NOT NULL, + "min_items" INTEGER NOT NULL, "thresholds" varchar(64) NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE ); diff --git a/data/tariffplans/testtp/Stats.csv b/data/tariffplans/testtp/Stats.csv index 20fd89d20..6b5853d14 100755 --- a/data/tariffplans/testtp/Stats.csv +++ b/data/tariffplans/testtp/Stats.csv @@ -1,2 +1,2 @@ -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -cgrates.org,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acd;*acc;*tcd;*tcc;*pdd,true,true,20,THRESH1;THRESH2 +#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],MinItems[12],Thresholds[13] +cgrates.org,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 diff --git a/data/tariffplans/tutorial/Stats.csv b/data/tariffplans/tutorial/Stats.csv index dabce2a2b..6b5853d14 100755 --- a/data/tariffplans/tutorial/Stats.csv +++ b/data/tariffplans/tutorial/Stats.csv @@ -1,3 +1,2 @@ #Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],MinItems[12],Thresholds[13] -cgrates.org,STATS_1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,THRESH1;THRESH2 - +cgrates.org,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 diff --git a/engine/libstats.go b/engine/libstats.go index 538e3a80d..2bb680486 100755 --- a/engine/libstats.go +++ b/engine/libstats.go @@ -40,6 +40,7 @@ type StatQueueProfile struct { Blocker bool // blocker flag to stop processing on filters matched Stored bool Weight float64 + MinItems int } func (sqp *StatQueueProfile) TenantID() string { @@ -154,6 +155,7 @@ func NewStoredStatQueue(sq *StatQueue, ms Marshaler) (sSQ *StoredStatQueue, err ExpiryTime *time.Time }, len(sq.SQItems)), SQMetrics: make(map[string][]byte, len(sq.SQMetrics)), + MinItems: sq.MinItems, } for i, sqItm := range sq.SQItems { sSQ.SQItems[i] = sqItm @@ -177,6 +179,7 @@ type StoredStatQueue struct { ExpiryTime *time.Time // Used to auto-expire events } SQMetrics map[string][]byte + MinItems int } // SqID will compose the unique identifier for the StatQueue out of Tenant and ID @@ -194,12 +197,13 @@ func (ssq *StoredStatQueue) AsStatQueue(ms Marshaler) (sq *StatQueue, err error) ExpiryTime *time.Time }, len(ssq.SQItems)), SQMetrics: make(map[string]StatMetric, len(ssq.SQMetrics)), + MinItems: ssq.MinItems, } for i, sqItm := range ssq.SQItems { sq.SQItems[i] = sqItm } for metricID, marshaled := range ssq.SQMetrics { - if metric, err := NewStatMetric(metricID); err != nil { + if metric, err := NewStatMetric(metricID, ssq.MinItems); err != nil { return nil, err } else if err := metric.LoadMarshaled(ms, marshaled); err != nil { return nil, err @@ -219,6 +223,7 @@ type StatQueue struct { ExpiryTime *time.Time // Used to auto-expire events } SQMetrics map[string]StatMetric + MinItems int sqPrfl *StatQueueProfile dirty *bool // needs save ttl *time.Duration // timeToLeave, picked on each init diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index aedcc7624..700eb640e 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -273,8 +273,8 @@ cgrates.org,ResGroup21,*rsr_fields,,HdrSubject(~^1.*1$);HdrDestination(1002),,,, cgrates.org,ResGroup22,*destinations,HdrDestination,DST_FS,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, ` stats = ` -#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],Thresholds[12] -cgrates.org,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acd,true,true,20,THRESH1;THRESH2 +#Tenant[0],Id[1],FilterType[2],FilterFieldName[3],FilterFieldValues[4],ActivationInterval[5],QueueLength[6],TTL[7],Metrics[8],Blocker[9],Stored[10],Weight[11],MinItems[12],Thresholds[13] +cgrates.org,Stats1,*string,Account,1001;1002,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 ` thresholds = ` @@ -1452,11 +1452,12 @@ func TestLoadStats(t *testing.T) { }, QueueLength: 100, TTL: "1s", - Metrics: []string{"*asr", "*acd"}, + Metrics: []string{"*asr", "*acc", "*tcc", "*acd", "*tcd", "*pdd"}, Thresholds: []string{"THRESH1", "THRESH2"}, Blocker: true, Stored: true, Weight: 20, + MinItems: 2, }, }, } diff --git a/engine/model_helpers.go b/engine/model_helpers.go index bc586d383..524dd4a06 100755 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -1969,11 +1969,12 @@ func (tps TpStatsS) AsTPStats() (result []*utils.TPStats) { st, found := mst[tp.Tag] if !found { st = &utils.TPStats{ - Tenant: tp.Tenant, - TPid: tp.Tpid, - ID: tp.Tag, - Blocker: tp.Blocker, - Stored: tp.Stored, + Tenant: tp.Tenant, + TPid: tp.Tpid, + ID: tp.Tag, + Blocker: tp.Blocker, + Stored: tp.Stored, + MinItems: tp.MinItems, } } if tp.Blocker == false || tp.Blocker == true { @@ -1982,7 +1983,9 @@ func (tps TpStatsS) AsTPStats() (result []*utils.TPStats) { if tp.Stored == false || tp.Stored == true { st.Stored = tp.Stored } - + if tp.MinItems != 0 { + st.MinItems = tp.MinItems + } if tp.QueueLength != 0 { st.QueueLength = tp.QueueLength } @@ -2037,9 +2040,10 @@ func APItoModelStats(st *utils.TPStats) (mdls TpStatsS) { } for i, fltr := range st.Filters { mdl := &TpStats{ - Tenant: st.Tenant, - Tpid: st.TPid, - Tag: st.ID, + Tenant: st.Tenant, + Tpid: st.TPid, + Tag: st.ID, + MinItems: st.MinItems, } if i == 0 { mdl.TTL = st.TTL @@ -2047,6 +2051,7 @@ func APItoModelStats(st *utils.TPStats) (mdls TpStatsS) { mdl.Stored = st.Stored mdl.Weight = st.Weight mdl.QueueLength = st.QueueLength + mdl.MinItems = st.MinItems for i, val := range st.Metrics { if i != 0 { mdl.Metrics += utils.INFIELD_SEP @@ -2089,6 +2094,7 @@ func APItoStats(tpST *utils.TPStats, timezone string) (st *StatQueueProfile, err Weight: tpST.Weight, Blocker: tpST.Blocker, Stored: tpST.Stored, + MinItems: tpST.MinItems, Filters: make([]*RequestFilter, len(tpST.Filters)), } if tpST.TTL != "" { diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 937f79f85..8ece5f838 100755 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -853,6 +853,7 @@ func TestTPStatsAsTPStats(t *testing.T) { ActivationInterval: "2014-07-29T15:00:00Z", QueueLength: 100, TTL: "1s", + MinItems: 1, Metrics: "*asr;*acd;*acc", Thresholds: "THRESH1;THRESH2", Stored: false, @@ -877,6 +878,7 @@ func TestTPStatsAsTPStats(t *testing.T) { QueueLength: tps[0].QueueLength, TTL: tps[0].TTL, Metrics: []string{"*asr", "*acd", "*acc"}, + MinItems: tps[0].MinItems, Thresholds: []string{"THRESH1", "THRESH2"}, Stored: tps[0].Stored, Blocker: tps[0].Blocker, @@ -900,6 +902,7 @@ func TestAPItoTPStats(t *testing.T) { QueueLength: 100, TTL: "1s", Metrics: []string{"*asr", "*acd", "*acc"}, + MinItems: 1, Thresholds: []string{"THRESH1", "THRESH2"}, Stored: false, Blocker: false, @@ -914,6 +917,7 @@ func TestAPItoTPStats(t *testing.T) { Stored: tps.Stored, Blocker: tps.Blocker, Weight: 20.0, + MinItems: tps.MinItems, } if eTPs.TTL, err = utils.ParseDurationWithSecs(tps.TTL); err != nil { t.Errorf("Got error: %+v", err) diff --git a/engine/models.go b/engine/models.go index dad305048..e50a9c613 100755 --- a/engine/models.go +++ b/engine/models.go @@ -495,7 +495,8 @@ type TpStats struct { Blocker bool `index:"9" re:""` Stored bool `index:"10" re:""` Weight float64 `index:"11" re:"\d+\.?\d*"` - Thresholds string `index:"12" re:""` + MinItems int `index:"12" re:""` + Thresholds string `index:"13" re:""` CreatedAt time.Time } diff --git a/engine/statmetrics.go b/engine/statmetrics.go index a67dafa38..f41e38bdb 100644 --- a/engine/statmetrics.go +++ b/engine/statmetrics.go @@ -28,8 +28,8 @@ import ( // NewStatMetric instantiates the StatMetric // cfg serves as general purpose container to pass config options to metric -func NewStatMetric(metricID string) (sm StatMetric, err error) { - metrics := map[string]func() (StatMetric, error){ +func NewStatMetric(metricID string, minItems int) (sm StatMetric, err error) { + metrics := map[string]func(int) (StatMetric, error){ utils.MetaASR: NewASR, utils.MetaACD: NewACD, utils.MetaTCD: NewTCD, @@ -41,7 +41,7 @@ func NewStatMetric(metricID string) (sm StatMetric, err error) { if _, has := metrics[metricID]; !has { return nil, fmt.Errorf("unsupported metric: %s", metricID) } - return metrics[metricID]() + return metrics[metricID](minItems) } // StatMetric is the interface which a metric should implement @@ -55,8 +55,8 @@ type StatMetric interface { LoadMarshaled(ms Marshaler, marshaled []byte) (err error) } -func NewASR() (StatMetric, error) { - return &StatASR{Events: make(map[string]bool)}, nil +func NewASR(minItems int) (StatMetric, error) { + return &StatASR{Events: make(map[string]bool), MinItems: minItems}, nil } // ASR implements AverageSuccessRatio metric @@ -64,14 +64,15 @@ type StatASR struct { Answered float64 Count float64 Events map[string]bool // map[EventTenantID]Answered - val *float64 // cached ASR value + MinItems int + val *float64 // cached ASR value } // getValue returns asr.val func (asr *StatASR) getValue() float64 { if asr.val == nil { - if asr.Count == 0 { - asr.val = utils.Float64Pointer(float64(STATS_NA)) + if (asr.MinItems > 0 && len(asr.Events) < asr.MinItems) || (asr.Count == 0) { + asr.val = utils.Float64Pointer(STATS_NA) } else { asr.val = utils.Float64Pointer(utils.Round((asr.Answered / asr.Count * 100), config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)) @@ -86,10 +87,12 @@ func (asr *StatASR) GetValue() (v interface{}) { } func (asr *StatASR) GetStringValue(fmtOpts string) (valStr string) { - if asr.Count == 0 { - return utils.NOT_AVAILABLE + if val := asr.getValue(); val == STATS_NA { + valStr = utils.NOT_AVAILABLE + } else { + valStr = fmt.Sprintf("%v%%", asr.getValue()) } - return fmt.Sprintf("%v%%", asr.getValue()) // %v will automatically limit the number of decimals printed + return } // GetFloat64Value is part of StatMetric interface @@ -139,22 +142,23 @@ func (asr *StatASR) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, asr) } -func NewACD() (StatMetric, error) { - return &StatACD{Events: make(map[string]time.Duration)}, nil +func NewACD(minItems int) (StatMetric, error) { + return &StatACD{Events: make(map[string]time.Duration), MinItems: minItems}, nil } // ACD implements AverageCallDuration metric type StatACD struct { - Sum time.Duration - Count int64 - Events map[string]time.Duration // map[EventTenantID]Duration - val *time.Duration // cached ACD value + Sum time.Duration + Count int64 + Events map[string]time.Duration // map[EventTenantID]Duration + MinItems int + val *time.Duration // cached ACD value } // getValue returns acr.val func (acd *StatACD) getValue() time.Duration { if acd.val == nil { - if acd.Count == 0 { + if (acd.MinItems > 0 && len(acd.Events) < acd.MinItems) || (acd.Count == 0) { acd.val = utils.DurationPointer(time.Duration((-1) * time.Nanosecond)) } else { acd.val = utils.DurationPointer(time.Duration(acd.Sum.Nanoseconds() / acd.Count)) @@ -163,11 +167,13 @@ func (acd *StatACD) getValue() time.Duration { return *acd.val } -func (acd *StatACD) GetStringValue(fmtOpts string) (val string) { - if acd.Count == 0 { - return utils.NOT_AVAILABLE +func (acd *StatACD) GetStringValue(fmtOpts string) (valStr string) { + if val := acd.getValue(); val == time.Duration((-1)*time.Nanosecond) { + valStr = utils.NOT_AVAILABLE + } else { + valStr = fmt.Sprintf("%+v", acd.getValue()) } - return fmt.Sprintf("%+v", acd.getValue()) + return } func (acd *StatACD) GetValue() (v interface{}) { @@ -175,10 +181,12 @@ func (acd *StatACD) GetValue() (v interface{}) { } func (acd *StatACD) GetFloat64Value() (v float64) { - if acd.Count == 0 { - return -1.0 + if val := acd.getValue(); val == time.Duration((-1)*time.Nanosecond) { + v = -1.0 + } else { + v = acd.getValue().Seconds() } - return acd.getValue().Seconds() + return } func (acd *StatACD) AddEvent(ev *StatEvent) (err error) { @@ -221,22 +229,23 @@ func (acd *StatACD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, acd) } -func NewTCD() (StatMetric, error) { - return &StatTCD{Events: make(map[string]time.Duration)}, nil +func NewTCD(minItems int) (StatMetric, error) { + return &StatTCD{Events: make(map[string]time.Duration), MinItems: minItems}, nil } // TCD implements TotalCallDuration metric type StatTCD struct { - Sum time.Duration - Count int64 - Events map[string]time.Duration // map[EventTenantID]Duration - val *time.Duration // cached TCD value + Sum time.Duration + Count int64 + Events map[string]time.Duration // map[EventTenantID]Duration + MinItems int + val *time.Duration // cached TCD value } // getValue returns tcd.val func (tcd *StatTCD) getValue() time.Duration { if tcd.val == nil { - if tcd.Count == 0 { + if (tcd.MinItems > 0 && len(tcd.Events) < tcd.MinItems) || (tcd.Count == 0) { tcd.val = utils.DurationPointer(time.Duration((-1) * time.Nanosecond)) } else { tcd.val = utils.DurationPointer(time.Duration(tcd.Sum.Nanoseconds())) @@ -245,11 +254,13 @@ func (tcd *StatTCD) getValue() time.Duration { return *tcd.val } -func (tcd *StatTCD) GetStringValue(fmtOpts string) (val string) { - if tcd.Count == 0 { - return utils.NOT_AVAILABLE +func (tcd *StatTCD) GetStringValue(fmtOpts string) (valStr string) { + if val := tcd.getValue(); val == time.Duration((-1)*time.Nanosecond) { + valStr = utils.NOT_AVAILABLE + } else { + valStr = fmt.Sprintf("%+v", tcd.getValue()) } - return fmt.Sprintf("%+v", tcd.getValue()) + return } func (tcd *StatTCD) GetValue() (v interface{}) { @@ -257,10 +268,12 @@ func (tcd *StatTCD) GetValue() (v interface{}) { } func (tcd *StatTCD) GetFloat64Value() (v float64) { - if tcd.Count == 0 { - return -1.0 + if val := tcd.getValue(); val == time.Duration((-1)*time.Nanosecond) { + v = -1.0 + } else { + v = tcd.getValue().Seconds() } - return tcd.getValue().Seconds() + return } func (tcd *StatTCD) AddEvent(ev *StatEvent) (err error) { @@ -305,23 +318,24 @@ func (tcd *StatTCD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, tcd) } -func NewACC() (StatMetric, error) { - return &StatACC{Events: make(map[string]float64)}, nil +func NewACC(minItems int) (StatMetric, error) { + return &StatACC{Events: make(map[string]float64), MinItems: minItems}, nil } // ACC implements AverageCallCost metric type StatACC struct { - Sum float64 - Count float64 - Events map[string]float64 // map[EventTenantID]Cost - val *float64 // cached ACC value + Sum float64 + Count float64 + Events map[string]float64 // map[EventTenantID]Cost + MinItems int + val *float64 // cached ACC value } // getValue returns tcd.val func (acc *StatACC) getValue() float64 { if acc.val == nil { - if acc.Count == 0 { - acc.val = utils.Float64Pointer(float64(STATS_NA)) + if (acc.MinItems > 0 && len(acc.Events) < acc.MinItems) || (acc.Count == 0) { + acc.val = utils.Float64Pointer(STATS_NA) } else { acc.val = utils.Float64Pointer(utils.Round((acc.Sum / acc.Count), config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)) @@ -330,11 +344,13 @@ func (acc *StatACC) getValue() float64 { return *acc.val } -func (acc *StatACC) GetStringValue(fmtOpts string) (val string) { - if acc.Count == 0 { - return utils.NOT_AVAILABLE +func (acc *StatACC) GetStringValue(fmtOpts string) (valStr string) { + if val := acc.getValue(); val == STATS_NA { + valStr = utils.NOT_AVAILABLE + } else { + valStr = strconv.FormatFloat(acc.getValue(), 'f', -1, 64) } - return strconv.FormatFloat(acc.getValue(), 'f', -1, 64) + return } @@ -387,23 +403,24 @@ func (acc *StatACC) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, acc) } -func NewTCC() (StatMetric, error) { - return &StatTCC{Events: make(map[string]float64)}, nil +func NewTCC(minItems int) (StatMetric, error) { + return &StatTCC{Events: make(map[string]float64), MinItems: minItems}, nil } // TCC implements TotalCallCost metric type StatTCC struct { - Sum float64 - Count float64 - Events map[string]float64 // map[EventTenantID]Cost - val *float64 // cached TCC value + Sum float64 + Count float64 + Events map[string]float64 // map[EventTenantID]Cost + MinItems int + val *float64 // cached TCC value } // getValue returns tcd.val func (tcc *StatTCC) getValue() float64 { if tcc.val == nil { - if tcc.Count == 0 { - tcc.val = utils.Float64Pointer(float64(STATS_NA)) + if (tcc.MinItems > 0 && len(tcc.Events) < tcc.MinItems) || (tcc.Count == 0) { + tcc.val = utils.Float64Pointer(STATS_NA) } else { tcc.val = utils.Float64Pointer(utils.Round(tcc.Sum, config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)) @@ -412,11 +429,13 @@ func (tcc *StatTCC) getValue() float64 { return *tcc.val } -func (tcc *StatTCC) GetStringValue(fmtOpts string) (val string) { - if tcc.Count == 0 { - return utils.NOT_AVAILABLE +func (tcc *StatTCC) GetStringValue(fmtOpts string) (valStr string) { + if val := tcc.getValue(); val == STATS_NA { + valStr = utils.NOT_AVAILABLE + } else { + valStr = strconv.FormatFloat(tcc.getValue(), 'f', -1, 64) } - return strconv.FormatFloat(tcc.getValue(), 'f', -1, 64) + return } func (tcc *StatTCC) GetValue() (v interface{}) { @@ -468,22 +487,23 @@ func (tcc *StatTCC) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, tcc) } -func NewPDD() (StatMetric, error) { - return &StatPDD{Events: make(map[string]time.Duration)}, nil +func NewPDD(minItems int) (StatMetric, error) { + return &StatPDD{Events: make(map[string]time.Duration), MinItems: minItems}, nil } // PDD implements Post Dial Delay (average) metric type StatPDD struct { - Sum time.Duration - Count int64 - Events map[string]time.Duration // map[EventTenantID]Duration - val *time.Duration // cached PDD value + Sum time.Duration + Count int64 + Events map[string]time.Duration // map[EventTenantID]Duration + MinItems int + val *time.Duration // cached PDD value } // getValue returns pdd.val func (pdd *StatPDD) getValue() time.Duration { if pdd.val == nil { - if pdd.Count == 0 { + if (pdd.MinItems > 0 && len(pdd.Events) < pdd.MinItems) || (pdd.Count == 0) { pdd.val = utils.DurationPointer(time.Duration((-1) * time.Nanosecond)) } else { pdd.val = utils.DurationPointer(time.Duration(pdd.Sum.Nanoseconds() / pdd.Count)) @@ -492,11 +512,13 @@ func (pdd *StatPDD) getValue() time.Duration { return *pdd.val } -func (pdd *StatPDD) GetStringValue(fmtOpts string) (val string) { - if pdd.Count == 0 { - return utils.NOT_AVAILABLE +func (pdd *StatPDD) GetStringValue(fmtOpts string) (valStr string) { + if val := pdd.getValue(); val == time.Duration((-1)*time.Nanosecond) { + valStr = utils.NOT_AVAILABLE + } else { + valStr = fmt.Sprintf("%+v", pdd.getValue()) } - return fmt.Sprintf("%+v", pdd.getValue()) + return } func (pdd *StatPDD) GetValue() (v interface{}) { @@ -504,10 +526,12 @@ func (pdd *StatPDD) GetValue() (v interface{}) { } func (pdd *StatPDD) GetFloat64Value() (v float64) { - if pdd.Count == 0 { - return -1.0 + if val := pdd.getValue(); val == time.Duration((-1)*time.Nanosecond) { + v = -1.0 + } else { + v = pdd.getValue().Seconds() } - return pdd.getValue().Seconds() + return } func (pdd *StatPDD) AddEvent(ev *StatEvent) (err error) { @@ -551,21 +575,24 @@ func (pdd *StatPDD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, pdd) } -func NewDCC() (StatMetric, error) { - return &StatDDC{Destinations: make(map[string]utils.StringMap), EventDestinations: make(map[string]string)}, nil +func NewDCC(minItems int) (StatMetric, error) { + return &StatDDC{Destinations: make(map[string]utils.StringMap), Events: make(map[string]string), MinItems: minItems}, nil } // DDC implements Destination Distinct Count metric type StatDDC struct { - Destinations map[string]utils.StringMap - EventDestinations map[string]string // map[EventTenantID]Destination + Destinations map[string]utils.StringMap + Events map[string]string // map[EventTenantID]Destination + MinItems int } -func (ddc *StatDDC) GetStringValue(fmtOpts string) (val string) { - if len(ddc.Destinations) == 0 { - return utils.NOT_AVAILABLE +func (ddc *StatDDC) GetStringValue(fmtOpts string) (valStr string) { + if val := len(ddc.Destinations); (val == 0) || (ddc.MinItems > 0 && len(ddc.Events) < ddc.MinItems) { + valStr = utils.NOT_AVAILABLE + } else { + valStr = fmt.Sprintf("%+v", len(ddc.Destinations)) } - return fmt.Sprintf("%+v", len(ddc.Destinations)) + return } func (ddc *StatDDC) GetValue() (v interface{}) { @@ -573,10 +600,12 @@ func (ddc *StatDDC) GetValue() (v interface{}) { } func (ddc *StatDDC) GetFloat64Value() (v float64) { - if len(ddc.Destinations) == 0 { - return -1.0 + if val := len(ddc.Destinations); (val == 0) || (ddc.MinItems > 0 && len(ddc.Events) < ddc.MinItems) { + v = -1.0 + } else { + v = float64(len(ddc.Destinations)) } - return float64(len(ddc.Destinations)) + return } func (ddc *StatDDC) AddEvent(ev *StatEvent) (err error) { @@ -588,16 +617,16 @@ func (ddc *StatDDC) AddEvent(ev *StatEvent) (err error) { ddc.Destinations[dest] = make(map[string]bool) } ddc.Destinations[dest][ev.TenantID()] = true - ddc.EventDestinations[ev.TenantID()] = dest + ddc.Events[ev.TenantID()] = dest return } func (ddc *StatDDC) RemEvent(evTenantID string) (err error) { - destination, has := ddc.EventDestinations[evTenantID] + destination, has := ddc.Events[evTenantID] if !has { return utils.ErrNotFound } - delete(ddc.EventDestinations, evTenantID) + delete(ddc.Events, evTenantID) if len(ddc.Destinations[destination]) == 1 { delete(ddc.Destinations, destination) return diff --git a/engine/statmetrics_test.go b/engine/statmetrics_test.go index 88bdb9896..40fa53524 100644 --- a/engine/statmetrics_test.go +++ b/engine/statmetrics_test.go @@ -24,7 +24,7 @@ import ( ) func TestASRGetStringValue(t *testing.T) { - asr, _ := NewASR() + asr, _ := NewASR(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC)}} @@ -32,12 +32,15 @@ func TestASRGetStringValue(t *testing.T) { t.Errorf("wrong asr value: %s", strVal) } asr.AddEvent(ev) - if strVal := asr.GetStringValue(""); strVal != "100%" { + if strVal := asr.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong asr value: %s", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3"} asr.AddEvent(ev2) + if strVal := asr.GetStringValue(""); strVal != "50%" { + t.Errorf("wrong asr value: %s", strVal) + } asr.AddEvent(ev3) if strVal := asr.GetStringValue(""); strVal != "33.33333%" { t.Errorf("wrong asr value: %s", strVal) @@ -70,12 +73,12 @@ func TestASRGetStringValue(t *testing.T) { } func TestASRGetValue(t *testing.T) { - asr, _ := NewASR() + asr, _ := NewASR(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC)}} asr.AddEvent(ev) - if v := asr.GetValue(); v != 100.0 { + if v := asr.GetValue(); v != -1.0 { t.Errorf("wrong asr value: %f", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} @@ -106,7 +109,7 @@ func TestASRGetValue(t *testing.T) { t.Errorf("wrong asr value: %f", v) } asr.RemEvent(ev4.TenantID()) - if v := asr.GetValue(); v != 100.0 { + if v := asr.GetValue(); v != -1.0 { t.Errorf("wrong asr value: %f", v) } asr.RemEvent(ev5.TenantID()) @@ -116,7 +119,7 @@ func TestASRGetValue(t *testing.T) { } func TestACDGetStringValue(t *testing.T) { - acd, _ := NewACD() + acd, _ := NewACD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ utils.USAGE: time.Duration(10 * time.Second), @@ -126,18 +129,17 @@ func TestACDGetStringValue(t *testing.T) { t.Errorf("wrong acd value: %s", strVal) } acd.AddEvent(ev) - if strVal := acd.GetStringValue(""); strVal != "10s" { + if strVal := acd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong acd value: %s", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3"} acd.AddEvent(ev2) acd.AddEvent(ev3) - if strVal := acd.GetStringValue(""); strVal != "10s" { + if strVal := acd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong acd value: %s", strVal) } - acd.RemEvent(ev3.TenantID()) - if strVal := acd.GetStringValue(""); strVal != "10s" { + if strVal := acd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong acd value: %s", strVal) } acd.RemEvent(ev.TenantID()) @@ -157,7 +159,7 @@ func TestACDGetStringValue(t *testing.T) { }, } acd.AddEvent(ev4) - if strVal := acd.GetStringValue(""); strVal != "1m0s" { + if strVal := acd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong acd value: %s", strVal) } acd.AddEvent(ev5) @@ -177,18 +179,18 @@ func TestACDGetStringValue(t *testing.T) { } func TestACDGetFloat64Value(t *testing.T) { - acd, _ := NewACD() + acd, _ := NewACD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), "Usage": time.Duration(10 * time.Second)}} acd.AddEvent(ev) - if v := acd.GetFloat64Value(); v != 10.0 { + if v := acd.GetFloat64Value(); v != -1.0 { t.Errorf("wrong acd value: %v", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} acd.AddEvent(ev2) - if v := acd.GetFloat64Value(); v != 10.0 { + if v := acd.GetFloat64Value(); v != -1.0 { t.Errorf("wrong acd value: %v", v) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -220,7 +222,7 @@ func TestACDGetFloat64Value(t *testing.T) { t.Errorf("wrong acd value: %v", strVal) } acd.RemEvent(ev.TenantID()) - if strVal := acd.GetFloat64Value(); strVal != 90.0 { + if strVal := acd.GetFloat64Value(); strVal != -1.0 { t.Errorf("wrong acd value: %v", strVal) } acd.RemEvent(ev5.TenantID()) @@ -230,13 +232,13 @@ func TestACDGetFloat64Value(t *testing.T) { } func TestACDGetValue(t *testing.T) { - acd, _ := NewACD() + acd, _ := NewACD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), "Usage": time.Duration(10 * time.Second)}} acd.AddEvent(ev) - if v := acd.GetValue(); v != time.Duration(10*time.Second) { + if v := acd.GetValue(); v != time.Duration((-1)*time.Nanosecond) { t.Errorf("wrong acd value: %+v", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2", @@ -250,7 +252,7 @@ func TestACDGetValue(t *testing.T) { t.Errorf("wrong acd value: %+v", v) } acd.RemEvent(ev.TenantID()) - if v := acd.GetValue(); v != time.Duration(8*time.Second) { + if v := acd.GetValue(); v != time.Duration((-1)*time.Nanosecond) { t.Errorf("wrong acd value: %+v", v) } acd.RemEvent(ev2.TenantID()) @@ -286,7 +288,7 @@ func TestACDGetValue(t *testing.T) { } func TestTCDGetStringValue(t *testing.T) { - tcd, _ := NewTCD() + tcd, _ := NewTCD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "Usage": time.Duration(10 * time.Second), @@ -296,7 +298,7 @@ func TestTCDGetStringValue(t *testing.T) { t.Errorf("wrong tcd value: %s", strVal) } tcd.AddEvent(ev) - if strVal := tcd.GetStringValue(""); strVal != "10s" { + if strVal := tcd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong tcd value: %s", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2", @@ -311,7 +313,7 @@ func TestTCDGetStringValue(t *testing.T) { t.Errorf("wrong tcd value: %s", strVal) } tcd.RemEvent(ev2.TenantID()) - if strVal := tcd.GetStringValue(""); strVal != "10s" { + if strVal := tcd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong tcd value: %s", strVal) } tcd.RemEvent(ev.TenantID()) @@ -336,7 +338,7 @@ func TestTCDGetStringValue(t *testing.T) { t.Errorf("wrong tcd value: %s", strVal) } tcd.RemEvent(ev4.TenantID()) - if strVal := tcd.GetStringValue(""); strVal != "1m30s" { + if strVal := tcd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong tcd value: %s", strVal) } tcd.RemEvent(ev5.TenantID()) @@ -347,18 +349,18 @@ func TestTCDGetStringValue(t *testing.T) { } func TestTCDGetFloat64Value(t *testing.T) { - tcd, _ := NewTCD() + tcd, _ := NewTCD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), "Usage": time.Duration(10 * time.Second)}} tcd.AddEvent(ev) - if v := tcd.GetFloat64Value(); v != 10.0 { + if v := tcd.GetFloat64Value(); v != -1.0 { t.Errorf("wrong tcd value: %f", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} tcd.AddEvent(ev2) - if v := tcd.GetFloat64Value(); v != 10.0 { + if v := tcd.GetFloat64Value(); v != -1.0 { t.Errorf("wrong tcd value: %f", v) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -390,7 +392,7 @@ func TestTCDGetFloat64Value(t *testing.T) { t.Errorf("wrong tcd value: %f", strVal) } tcd.RemEvent(ev.TenantID()) - if strVal := tcd.GetFloat64Value(); strVal != 90.0 { + if strVal := tcd.GetFloat64Value(); strVal != -1.0 { t.Errorf("wrong tcd value: %f", strVal) } tcd.RemEvent(ev5.TenantID()) @@ -400,13 +402,13 @@ func TestTCDGetFloat64Value(t *testing.T) { } func TestTCDGetValue(t *testing.T) { - tcd, _ := NewTCD() + tcd, _ := NewTCD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), "Usage": time.Duration(10 * time.Second)}} tcd.AddEvent(ev) - if v := tcd.GetValue(); v != time.Duration(10*time.Second) { + if v := tcd.GetValue(); v != time.Duration((-1)*time.Nanosecond) { t.Errorf("wrong tcd value: %+v", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2", @@ -420,7 +422,7 @@ func TestTCDGetValue(t *testing.T) { t.Errorf("wrong tcd value: %+v", v) } tcd.RemEvent(ev.TenantID()) - if v := tcd.GetValue(); v != time.Duration(5*time.Second) { + if v := tcd.GetValue(); v != time.Duration((-1)*time.Nanosecond) { t.Errorf("wrong tcd value: %+v", v) } tcd.RemEvent(ev2.TenantID()) @@ -456,7 +458,7 @@ func TestTCDGetValue(t *testing.T) { } func TestACCGetStringValue(t *testing.T) { - acc, _ := NewACC() + acc, _ := NewACC(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -465,18 +467,21 @@ func TestACCGetStringValue(t *testing.T) { t.Errorf("wrong acc value: %s", strVal) } acc.AddEvent(ev) - if strVal := acc.GetStringValue(""); strVal != "12.3" { + if strVal := acc.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong acc value: %s", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} - ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3"} + ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3", + Fields: map[string]interface{}{ + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + "Cost": 12.3}} acc.AddEvent(ev2) acc.AddEvent(ev3) if strVal := acc.GetStringValue(""); strVal != "12.3" { t.Errorf("wrong acc value: %s", strVal) } acc.RemEvent(ev3.TenantID()) - if strVal := acc.GetStringValue(""); strVal != "12.3" { + if strVal := acc.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong acc value: %s", strVal) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -505,7 +510,7 @@ func TestACCGetStringValue(t *testing.T) { } func TestACCGetValue(t *testing.T) { - acc, _ := NewACC() + acc, _ := NewACC(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -514,18 +519,18 @@ func TestACCGetValue(t *testing.T) { t.Errorf("wrong acc value: %v", strVal) } acc.AddEvent(ev) - if strVal := acc.GetValue(); strVal != 12.3 { + if strVal := acc.GetValue(); strVal != -1.0 { t.Errorf("wrong acc value: %v", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} ev3 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_3"} acc.AddEvent(ev2) acc.AddEvent(ev3) - if strVal := acc.GetValue(); strVal != 12.3 { + if strVal := acc.GetValue(); strVal != -1.0 { t.Errorf("wrong acc value: %v", strVal) } acc.RemEvent(ev3.TenantID()) - if strVal := acc.GetValue(); strVal != 12.3 { + if strVal := acc.GetValue(); strVal != -1.0 { t.Errorf("wrong acc value: %v", strVal) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -554,7 +559,7 @@ func TestACCGetValue(t *testing.T) { } func TestTCCGetStringValue(t *testing.T) { - tcc, _ := NewTCC() + tcc, _ := NewTCC(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -563,7 +568,7 @@ func TestTCCGetStringValue(t *testing.T) { t.Errorf("wrong tcc value: %s", strVal) } tcc.AddEvent(ev) - if strVal := tcc.GetStringValue(""); strVal != "12.3" { + if strVal := tcc.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong tcc value: %s", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} @@ -577,7 +582,7 @@ func TestTCCGetStringValue(t *testing.T) { t.Errorf("wrong tcc value: %s", strVal) } tcc.RemEvent(ev3.TenantID()) - if strVal := tcc.GetStringValue(""); strVal != "12.3" { + if strVal := tcc.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong tcc value: %s", strVal) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -606,7 +611,7 @@ func TestTCCGetStringValue(t *testing.T) { } func TestTCCGetValue(t *testing.T) { - tcc, _ := NewTCC() + tcc, _ := NewTCC(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -615,7 +620,7 @@ func TestTCCGetValue(t *testing.T) { t.Errorf("wrong tcc value: %v", strVal) } tcc.AddEvent(ev) - if strVal := tcc.GetValue(); strVal != 12.3 { + if strVal := tcc.GetValue(); strVal != -1.0 { t.Errorf("wrong tcc value: %v", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} @@ -629,7 +634,7 @@ func TestTCCGetValue(t *testing.T) { t.Errorf("wrong tcc value: %v", strVal) } tcc.RemEvent(ev3.TenantID()) - if strVal := tcc.GetValue(); strVal != 12.3 { + if strVal := tcc.GetValue(); strVal != -1.0 { t.Errorf("wrong tcc value: %v", strVal) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -658,7 +663,7 @@ func TestTCCGetValue(t *testing.T) { } func TestPDDGetStringValue(t *testing.T) { - pdd, _ := NewPDD() + pdd, _ := NewPDD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ utils.USAGE: time.Duration(10 * time.Second), @@ -669,7 +674,7 @@ func TestPDDGetStringValue(t *testing.T) { t.Errorf("wrong pdd value: %s", strVal) } pdd.AddEvent(ev) - if strVal := pdd.GetStringValue(""); strVal != "5s" { + if strVal := pdd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong pdd value: %s", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} @@ -684,7 +689,7 @@ func TestPDDGetStringValue(t *testing.T) { t.Errorf("wrong pdd value: %s", strVal) } pdd.RemEvent(ev.TenantID()) - if strVal := pdd.GetStringValue(""); strVal != "0s" { + if strVal := pdd.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong pdd value: %s", strVal) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -720,14 +725,14 @@ func TestPDDGetStringValue(t *testing.T) { } func TestPDDGetFloat64Value(t *testing.T) { - pdd, _ := NewPDD() + pdd, _ := NewPDD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), "Usage": time.Duration(10 * time.Second), utils.PDD: time.Duration(5 * time.Second)}} pdd.AddEvent(ev) - if v := pdd.GetFloat64Value(); v != 5.0 { + if v := pdd.GetFloat64Value(); v != -1.0 { t.Errorf("wrong pdd value: %v", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} @@ -765,7 +770,7 @@ func TestPDDGetFloat64Value(t *testing.T) { t.Errorf("wrong pdd value: %v", strVal) } pdd.RemEvent(ev.TenantID()) - if strVal := pdd.GetFloat64Value(); strVal != 0 { + if strVal := pdd.GetFloat64Value(); strVal != -1.0 { t.Errorf("wrong pdd value: %v", strVal) } pdd.RemEvent(ev5.TenantID()) @@ -775,14 +780,14 @@ func TestPDDGetFloat64Value(t *testing.T) { } func TestPDDGetValue(t *testing.T) { - pdd, _ := NewPDD() + pdd, _ := NewPDD(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), "Usage": time.Duration(10 * time.Second), utils.PDD: time.Duration(9 * time.Second)}} pdd.AddEvent(ev) - if v := pdd.GetValue(); v != time.Duration(9*time.Second) { + if v := pdd.GetValue(); v != time.Duration((-1)*time.Nanosecond) { t.Errorf("wrong pdd value: %+v", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2", @@ -801,7 +806,7 @@ func TestPDDGetValue(t *testing.T) { t.Errorf("wrong pdd value: %+v", v) } pdd.RemEvent(ev2.TenantID()) - if v := pdd.GetValue(); v != time.Duration(0*time.Second) { + if v := pdd.GetValue(); v != time.Duration((-1)*time.Nanosecond) { t.Errorf("wrong pdd value: %+v", v) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -824,7 +829,7 @@ func TestPDDGetValue(t *testing.T) { } pdd.RemEvent(ev5.TenantID()) pdd.RemEvent(ev4.TenantID()) - if v := pdd.GetValue(); v != time.Duration(0*time.Second) { + if v := pdd.GetValue(); v != time.Duration((-1)*time.Nanosecond) { t.Errorf("wrong pdd value: %+v", v) } pdd.RemEvent(ev3.TenantID()) @@ -834,7 +839,7 @@ func TestPDDGetValue(t *testing.T) { } func TestDDCGetStringValue(t *testing.T) { - ddc, _ := NewDCC() + ddc, _ := NewDCC(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -844,7 +849,7 @@ func TestDDCGetStringValue(t *testing.T) { } ddc.AddEvent(ev) - if strVal := ddc.GetStringValue(""); strVal != "1" { + if strVal := ddc.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong ddc value: %s", strVal) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2", @@ -866,7 +871,7 @@ func TestDDCGetStringValue(t *testing.T) { t.Errorf("wrong ddc value: %s", strVal) } ddc.RemEvent(ev2.TenantID()) - if strVal := ddc.GetStringValue(""); strVal != "1" { + if strVal := ddc.GetStringValue(""); strVal != utils.NOT_AVAILABLE { t.Errorf("wrong ddc value: %s", strVal) } ddc.RemEvent(ev3.TenantID()) @@ -876,7 +881,7 @@ func TestDDCGetStringValue(t *testing.T) { } func TestDDCGetFloat64Value(t *testing.T) { - ddc, _ := NewDCC() + ddc, _ := NewDCC(2) ev := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_1", Fields: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -884,12 +889,12 @@ func TestDDCGetFloat64Value(t *testing.T) { utils.PDD: time.Duration(5 * time.Second), utils.DESTINATION: "1002"}} ddc.AddEvent(ev) - if v := ddc.GetFloat64Value(); v != 1 { + if v := ddc.GetFloat64Value(); v != -1.0 { t.Errorf("wrong ddc value: %v", v) } ev2 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_2"} ddc.AddEvent(ev2) - if v := ddc.GetFloat64Value(); v != 1 { + if v := ddc.GetFloat64Value(); v != -1.0 { t.Errorf("wrong ddc value: %v", v) } ev4 := &StatEvent{Tenant: "cgrates.org", ID: "EVENT_4", @@ -924,7 +929,7 @@ func TestDDCGetFloat64Value(t *testing.T) { t.Errorf("wrong ddc value: %v", strVal) } ddc.RemEvent(ev.TenantID()) - if strVal := ddc.GetFloat64Value(); strVal != 1 { + if strVal := ddc.GetFloat64Value(); strVal != -1.0 { t.Errorf("wrong ddc value: %v", strVal) } ddc.RemEvent(ev5.TenantID()) diff --git a/engine/stats_metrics.go b/engine/stats_metrics.go index ec6b37134..ede4b453c 100644 --- a/engine/stats_metrics.go +++ b/engine/stats_metrics.go @@ -37,7 +37,7 @@ const ACC = "ACC" const TCC = "TCC" const PDD = "PDD" const DDC = "DDC" -const STATS_NA = -1 +const STATS_NA = -1.0 func CreateMetric(metric string) Metric { switch metric { diff --git a/engine/stordb_it_test.go b/engine/stordb_it_test.go index 91fecce2f..5488b3888 100755 --- a/engine/stordb_it_test.go +++ b/engine/stordb_it_test.go @@ -1578,6 +1578,7 @@ func testStorDBitCRUDTpStats(t *testing.T) { Metrics: []string{"*asr", "*acd", "*acc"}, Thresholds: []string{"THRESH1", "THRESH2"}, Weight: 20.0, + MinItems: 1, }, } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 9b1f1329b..4f658c77f 100755 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -2028,7 +2028,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err sq := &StatQueue{Tenant: sqTntID.Tenant, ID: sqTntID.ID, SQMetrics: make(map[string]StatMetric)} for _, metricID := range tpr.sqProfiles[sqTntID.Tenant][sqTntID.ID].Metrics { - if metric, err := NewStatMetric(metricID); err != nil { + if metric, err := NewStatMetric(metricID, tpr.sqProfiles[sqTntID.Tenant][sqTntID.ID].MinItems); err != nil { return err } else { sq.SQMetrics[metricID] = metric diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 48d5894b2..0460fa9e5 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1351,6 +1351,7 @@ type TPStats struct { Blocker bool // blocker flag to stop processing on filters matched Stored bool Weight float64 + MinItems int Thresholds []string } From a117db8c7b94d08f391ba601c1f3b433a4dbf2ff Mon Sep 17 00:00:00 2001 From: TeoV <30497669+TeoV@users.noreply.github.com> Date: Fri, 29 Sep 2017 15:19:49 +0300 Subject: [PATCH 4/5] Update stats_it_test.go --- apier/v1/stats_it_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apier/v1/stats_it_test.go b/apier/v1/stats_it_test.go index d7e383521..794d85f5e 100644 --- a/apier/v1/stats_it_test.go +++ b/apier/v1/stats_it_test.go @@ -184,7 +184,7 @@ func testV1STSProcessEvent(t *testing.T) { } else if reply != utils.OK { t.Errorf("received reply: %s", reply) } - //add an event (should be N/A becaus MinItems is 2) + //process with one event (should be N/A becaus MinItems is 2) expectedMetrics := map[string]string{ utils.MetaASR: utils.NOT_AVAILABLE, utils.MetaACD: utils.NOT_AVAILABLE, From 3a2269f1e7e920874763329be71a85a004a84d63 Mon Sep 17 00:00:00 2001 From: TeoV Date: Fri, 29 Sep 2017 17:33:10 +0300 Subject: [PATCH 5/5] Rename Tag with ID for TPStats and TPThresholds and set pk new primary key --- apier/v1/tpstats.go | 4 ++-- data/storage/mysql/create_tariffplan_tables.sql | 16 ++++++++-------- .../postgres/create_tariffplan_tables.sql | 12 ++++++------ engine/model_helpers.go | 16 ++++++++-------- engine/model_helpers_test.go | 8 ++++---- engine/models.go | 8 ++++---- engine/storage_sql.go | 8 ++++---- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/apier/v1/tpstats.go b/apier/v1/tpstats.go index 91c4bf414..4d287b3ca 100644 --- a/apier/v1/tpstats.go +++ b/apier/v1/tpstats.go @@ -67,7 +67,7 @@ func (self *ApierV1) GetTPStatIDs(attrs AttrGetTPStatIds, reply *[]string) error if missing := utils.MissingStructFields(&attrs, []string{"TPid"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBLTPStats, utils.TPDistinctIds{"tag"}, nil, &attrs.Paginator); err != nil { + if ids, err := self.StorDb.GetTpTableIds(attrs.TPid, utils.TBLTPStats, utils.TPDistinctIds{"id"}, nil, &attrs.Paginator); err != nil { if err.Error() != utils.ErrNotFound.Error() { err = utils.NewErrServerError(err) } @@ -83,7 +83,7 @@ func (self *ApierV1) RemTPStat(attrs AttrGetTPStat, reply *string) error { if missing := utils.MissingStructFields(&attrs, []string{"TPid", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } - if err := self.StorDb.RemTpData(utils.TBLTPStats, attrs.TPid, map[string]string{"tag": attrs.ID}); err != nil { + if err := self.StorDb.RemTpData(utils.TBLTPStats, attrs.TPid, map[string]string{"id": attrs.ID}); err != nil { return utils.NewErrServerError(err) } else { *reply = utils.OK diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 371add7f2..840f01d38 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -424,10 +424,10 @@ CREATE TABLE tp_resources ( DROP TABLE IF EXISTS tp_stats; CREATE TABLE tp_stats ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `pk` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, `tenant` varchar(64) NOT NULL, - `tag` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, `filter_type` varchar(16) NOT NULL, `filter_field_name` varchar(64) NOT NULL, `filter_field_values` varchar(256) NOT NULL, @@ -441,9 +441,9 @@ CREATE TABLE tp_stats ( `min_items` int(11) NOT NULL, `thresholds` varchar(64) NOT NULL, `created_at` TIMESTAMP, - PRIMARY KEY (`id`), + PRIMARY KEY (`pk`), KEY `tpid` (`tpid`), - UNIQUE KEY `unique_tp_stats` (`tpid`, `tenant`, `tag`, `filter_type`, `filter_field_name`) + UNIQUE KEY `unique_tp_stats` (`tpid`, `tenant`, `id`, `filter_type`, `filter_field_name`) ); -- @@ -452,10 +452,10 @@ CREATE TABLE tp_stats ( DROP TABLE IF EXISTS tp_thresholds; CREATE TABLE tp_thresholds ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `pk` int(11) NOT NULL AUTO_INCREMENT, `tpid` varchar(64) NOT NULL, `tenant` varchar(64) NOT NULL, - `tag` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, `filter_type` varchar(16) NOT NULL, `filter_field_name` varchar(64) NOT NULL, `filter_field_values` varchar(256) NOT NULL, @@ -466,9 +466,9 @@ CREATE TABLE tp_thresholds ( `weight` decimal(8,2) NOT NULL, `action_ids` varchar(64) NOT NULL, `created_at` TIMESTAMP, - PRIMARY KEY (`id`), + PRIMARY KEY (`pk`), KEY `tpid` (`tpid`), - UNIQUE KEY `unique_tp_thresholds` (`tpid`, `tag`, `filter_type`, `filter_field_name`) + UNIQUE KEY `unique_tp_thresholds` (`tpid`,`tenant`, `id`, `filter_type`, `filter_field_name`) ); -- diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index 55a71e5c9..663098a48 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -420,10 +420,10 @@ CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "tenant", "id", "fil DROP TABLE IF EXISTS tp_stats; CREATE TABLE tp_stats ( - "id" SERIAL PRIMARY KEY, + "pk" SERIAL PRIMARY KEY, "tpid" varchar(64) NOT NULL, "tenant"varchar(64) NOT NULL, - "tag" varchar(64) NOT NULL, + "id" varchar(64) NOT NULL, "filter_type" varchar(16) NOT NULL, "filter_field_name" varchar(64) NOT NULL, "filter_field_values" varchar(256) NOT NULL, @@ -439,7 +439,7 @@ CREATE TABLE tp_stats ( "created_at" TIMESTAMP WITH TIME ZONE ); CREATE INDEX tp_stats_idx ON tp_stats (tpid); -CREATE INDEX tp_stats_unique ON tp_stats ("tpid","tenant", "tag", "filter_type", "filter_field_name"); +CREATE INDEX tp_stats_unique ON tp_stats ("tpid","tenant", "id", "filter_type", "filter_field_name"); -- -- Table structure for table `tp_threshold_cfgs` @@ -447,10 +447,10 @@ CREATE INDEX tp_stats_unique ON tp_stats ("tpid","tenant", "tag", "filter_type" DROP TABLE IF EXISTS tp_thresholds; CREATE TABLE tp_thresholds ( - "id" SERIAL PRIMARY KEY, + "pk" SERIAL PRIMARY KEY, "tpid" varchar(64) NOT NULL, "tenant"varchar(64) NOT NULL, - "tag" varchar(64) NOT NULL, + "id" varchar(64) NOT NULL, "filter_type" varchar(16) NOT NULL, "filter_field_name" varchar(64) NOT NULL, "filter_field_values" varchar(256) NOT NULL, @@ -463,7 +463,7 @@ CREATE TABLE tp_thresholds ( "created_at" TIMESTAMP WITH TIME ZONE ); CREATE INDEX tp_thresholds_idx ON tp_thresholds (tpid); -CREATE INDEX tp_thresholds_unique ON tp_thresholds ("tpid", "tag", "filter_type", "filter_field_name"); +CREATE INDEX tp_thresholds_unique ON tp_thresholds ("tpid","tenant", "id", "filter_type", "filter_field_name"); -- diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 524dd4a06..3bcb03a13 100755 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -1966,12 +1966,12 @@ type TpStatsS []*TpStats func (tps TpStatsS) AsTPStats() (result []*utils.TPStats) { mst := make(map[string]*utils.TPStats) for _, tp := range tps { - st, found := mst[tp.Tag] + st, found := mst[tp.ID] if !found { st = &utils.TPStats{ Tenant: tp.Tenant, TPid: tp.Tpid, - ID: tp.Tag, + ID: tp.ID, Blocker: tp.Blocker, Stored: tp.Stored, MinItems: tp.MinItems, @@ -2023,7 +2023,7 @@ func (tps TpStatsS) AsTPStats() (result []*utils.TPStats) { FieldName: tp.FilterFieldName, Values: strings.Split(tp.FilterFieldValues, utils.INFIELD_SEP)}) } - mst[tp.Tag] = st + mst[tp.ID] = st } result = make([]*utils.TPStats, len(mst)) i := 0 @@ -2042,7 +2042,7 @@ func APItoModelStats(st *utils.TPStats) (mdls TpStatsS) { mdl := &TpStats{ Tenant: st.Tenant, Tpid: st.TPid, - Tag: st.ID, + ID: st.ID, MinItems: st.MinItems, } if i == 0 { @@ -2129,12 +2129,12 @@ type TpThresholdS []*TpThreshold func (tps TpThresholdS) AsTPThreshold() (result []*utils.TPThreshold) { mst := make(map[string]*utils.TPThreshold) for _, tp := range tps { - th, found := mst[tp.Tag] + th, found := mst[tp.ID] if !found { th = &utils.TPThreshold{ TPid: tp.Tpid, Tenant: tp.Tenant, - ID: tp.Tag, + ID: tp.ID, Blocker: tp.Blocker, Recurrent: tp.Recurrent, MinSleep: tp.MinSleep, @@ -2162,7 +2162,7 @@ func (tps TpThresholdS) AsTPThreshold() (result []*utils.TPThreshold) { FieldName: tp.FilterFieldName, Values: strings.Split(tp.FilterFieldValues, utils.INFIELD_SEP)}) } - mst[tp.Tag] = th + mst[tp.ID] = th } result = make([]*utils.TPThreshold, len(mst)) i := 0 @@ -2180,7 +2180,7 @@ func APItoModelTPThreshold(th *utils.TPThreshold) (mdls TpThresholdS) { for i, fltr := range th.Filters { mdl := &TpThreshold{ Tpid: th.TPid, - Tag: th.ID, + ID: th.ID, } if i == 0 { mdl.Blocker = th.Blocker diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 8ece5f838..3de1930a5 100755 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -846,7 +846,7 @@ func TestTPStatsAsTPStats(t *testing.T) { tps := []*TpStats{ &TpStats{ Tpid: "TEST_TPID", - Tag: "Stats1", + ID: "Stats1", FilterType: MetaStringPrefix, FilterFieldName: "Account", FilterFieldValues: "1001;1002", @@ -864,7 +864,7 @@ func TestTPStatsAsTPStats(t *testing.T) { eTPs := []*utils.TPStats{ &utils.TPStats{ TPid: tps[0].Tpid, - ID: tps[0].Tag, + ID: tps[0].ID, Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{ Type: tps[0].FilterType, @@ -939,7 +939,7 @@ func TestAsTPThresholdAsAsTPThreshold(t *testing.T) { tps := []*TpThreshold{ &TpThreshold{ Tpid: "TEST_TPID", - Tag: "Stats1", + ID: "Stats1", FilterType: MetaStringPrefix, FilterFieldName: "Account", FilterFieldValues: "1001;1002", @@ -954,7 +954,7 @@ func TestAsTPThresholdAsAsTPThreshold(t *testing.T) { eTPs := []*utils.TPThreshold{ &utils.TPThreshold{ TPid: tps[0].Tpid, - ID: tps[0].Tag, + ID: tps[0].ID, Filters: []*utils.TPRequestFilter{ &utils.TPRequestFilter{ Type: tps[0].FilterType, diff --git a/engine/models.go b/engine/models.go index e50a9c613..129b29936 100755 --- a/engine/models.go +++ b/engine/models.go @@ -481,10 +481,10 @@ func (t TBLVersion) TableName() string { } type TpStats struct { - ID int64 + PK uint `gorm:"primary_key"` Tpid string Tenant string `index:"0" re:""` - Tag string `index:"1" re:""` + ID string `index:"1" re:""` FilterType string `index:"2" re:"^\*[A-Za-z].*"` FilterFieldName string `index:"3" re:""` FilterFieldValues string `index:"4" re:""` @@ -501,10 +501,10 @@ type TpStats struct { } type TpThreshold struct { - ID int64 + PK uint `gorm:"primary_key"` Tpid string Tenant string `index:"0" re:""` - Tag string `index:"1" re:""` + ID string `index:"1" re:""` FilterType string `index:"2" re:"^\*[A-Za-z].*"` FilterFieldName string `index:"3" re:""` FilterFieldValues string `index:"4" re:""` diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 6c680fedb..3955bc962 100755 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -607,7 +607,7 @@ func (self *SQLStorage) SetTPStats(sts []*utils.TPStats) error { tx := self.db.Begin() for _, stq := range sts { // Remove previous - if err := tx.Where(&TpStats{Tpid: stq.TPid, Tag: stq.ID}).Delete(TpStats{}).Error; err != nil { + if err := tx.Where(&TpStats{Tpid: stq.TPid, ID: stq.ID}).Delete(TpStats{}).Error; err != nil { tx.Rollback() return err } @@ -629,7 +629,7 @@ func (self *SQLStorage) SetTPThreshold(ths []*utils.TPThreshold) error { tx := self.db.Begin() for _, th := range ths { // Remove previous - if err := tx.Where(&TpThreshold{Tpid: th.TPid, Tag: th.ID}).Delete(TpThreshold{}).Error; err != nil { + if err := tx.Where(&TpThreshold{Tpid: th.TPid, ID: th.ID}).Delete(TpThreshold{}).Error; err != nil { tx.Rollback() return err } @@ -1578,7 +1578,7 @@ func (self *SQLStorage) GetTPStats(tpid, id string) ([]*utils.TPStats, error) { var sts TpStatsS q := self.db.Where("tpid = ?", tpid) if len(id) != 0 { - q = q.Where("tag = ?", id) + q = q.Where("id = ?", id) } if err := q.Find(&sts).Error; err != nil { return nil, err @@ -1594,7 +1594,7 @@ func (self *SQLStorage) GetTPThreshold(tpid, id string) ([]*utils.TPThreshold, e var ths TpThresholdS q := self.db.Where("tpid = ?", tpid) if len(id) != 0 { - q = q.Where("tag = ?", id) + q = q.Where("id = ?", id) } if err := q.Find(&ths).Error; err != nil { return nil, err