diff --git a/apier/v1/filter_indexes.go b/apier/v1/filter_indexes.go index 6ff4b56d9..db8e5b038 100644 --- a/apier/v1/filter_indexes.go +++ b/apier/v1/filter_indexes.go @@ -24,6 +24,7 @@ import ( "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/ltcache" ) type AttrGetFilterIndexes struct { @@ -592,7 +593,7 @@ func (apierSv1 *APIerSv1) ComputeFilterIndexIDs(args *utils.ArgsComputeFilterInd return nil } -func (apierSv1 *APIerSv1) GetAccountActionPlansIndexHealth(args *engine.IndexHealthArgs, reply *engine.AccountActionPlanIHReply) error { +func (apierSv1 *APIerSv1) GetAccountActionPlansIndexHealth(args *engine.IndexHealthArgsWith2Ch, reply *engine.AccountActionPlanIHReply) error { rp, err := engine.GetAccountActionPlansIndexHealth(apierSv1.DataManager, args.ObjectCacheLimit, args.IndexCacheLimit, args.ObjectCacheTTL, args.IndexCacheTTL, args.ObjectCacheStaticTTL, args.IndexCacheStaticTTL) @@ -603,7 +604,7 @@ func (apierSv1 *APIerSv1) GetAccountActionPlansIndexHealth(args *engine.IndexHea return nil } -func (apierSv1 *APIerSv1) GetReverseDestinationsIndexHealth(args *engine.IndexHealthArgs, reply *engine.ReverseDestinationsIHReply) error { +func (apierSv1 *APIerSv1) GetReverseDestinationsIndexHealth(args *engine.IndexHealthArgsWith2Ch, reply *engine.ReverseDestinationsIHReply) error { rp, err := engine.GetReverseDestinationsIndexHealth(apierSv1.DataManager, args.ObjectCacheLimit, args.IndexCacheLimit, args.ObjectCacheTTL, args.IndexCacheTTL, args.ObjectCacheStaticTTL, args.IndexCacheStaticTTL) @@ -613,3 +614,110 @@ func (apierSv1 *APIerSv1) GetReverseDestinationsIndexHealth(args *engine.IndexHe *reply = *rp return nil } + +func (apierSv1 *APIerSv1) GetReverseFilterHealth(args *engine.IndexHealthArgsWith3Ch, reply *map[string]*engine.ReverseFilterIHReply) (err error) { + *reply, err = engine.GetRevFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + ) + return +} + +func (apierSv1 *APIerSv1) GetThresholdsIndexesHealth(args *engine.IndexHealthArgsWith3Ch, reply *engine.FilterIHReply) error { + rp, err := engine.GetFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + utils.CacheThresholdFilterIndexes, + ) + if err != nil { + return err + } + *reply = *rp + return nil +} + +func (apierSv1 *APIerSv1) GetResourcesIndexesHealth(args *engine.IndexHealthArgsWith3Ch, reply *engine.FilterIHReply) error { + rp, err := engine.GetFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + utils.CacheResourceFilterIndexes, + ) + if err != nil { + return err + } + *reply = *rp + return nil +} + +func (apierSv1 *APIerSv1) GetStatsIndexesHealth(args *engine.IndexHealthArgsWith3Ch, reply *engine.FilterIHReply) error { + rp, err := engine.GetFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + utils.CacheStatFilterIndexes, + ) + if err != nil { + return err + } + *reply = *rp + return nil +} + +func (apierSv1 *APIerSv1) GetRoutesIndexesHealth(args *engine.IndexHealthArgsWith3Ch, reply *engine.FilterIHReply) error { + rp, err := engine.GetFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + utils.CacheRouteFilterIndexes, + ) + if err != nil { + return err + } + *reply = *rp + return nil +} + +func (apierSv1 *APIerSv1) GetAttributesIndexesHealth(args *engine.IndexHealthArgsWith3Ch, reply *engine.FilterIHReply) error { + rp, err := engine.GetFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + utils.CacheAttributeFilterIndexes, + ) + if err != nil { + return err + } + *reply = *rp + return nil +} + +func (apierSv1 *APIerSv1) GetChargersIndexesHealth(args *engine.IndexHealthArgsWith3Ch, reply *engine.FilterIHReply) error { + rp, err := engine.GetFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + utils.CacheChargerFilterIndexes, + ) + if err != nil { + return err + } + *reply = *rp + return nil +} + +func (apierSv1 *APIerSv1) GetDispatchersIndexesHealth(args *engine.IndexHealthArgsWith3Ch, reply *engine.FilterIHReply) error { + rp, err := engine.GetFltrIdxHealth(apierSv1.DataManager, + ltcache.NewCache(args.FilterCacheLimit, args.FilterCacheTTL, args.FilterCacheStaticTTL, nil), + ltcache.NewCache(args.IndexCacheLimit, args.IndexCacheTTL, args.IndexCacheStaticTTL, nil), + ltcache.NewCache(args.ObjectCacheLimit, args.ObjectCacheTTL, args.ObjectCacheStaticTTL, nil), + utils.CacheDispatcherFilterIndexes, + ) + if err != nil { + return err + } + *reply = *rp + return nil +} diff --git a/apier/v1/filter_indexes_health_it_test.go b/apier/v1/filter_indexes_health_it_test.go index 6dfb31198..ab5e56870 100644 --- a/apier/v1/filter_indexes_health_it_test.go +++ b/apier/v1/filter_indexes_health_it_test.go @@ -113,7 +113,7 @@ func testV1FIdxHLoadFromFolder(t *testing.T) { func testV1FIdxHAccountActionPlansHealth(t *testing.T) { var reply engine.AccountActionPlanIHReply - if err := tFIdxHRpc.Call(utils.APIerSv1GetAccountActionPlansIndexHealth, engine.IndexHealthArgs{ + if err := tFIdxHRpc.Call(utils.APIerSv1GetAccountActionPlansIndexHealth, engine.IndexHealthArgsWith2Ch{ IndexCacheLimit: -1, ObjectCacheLimit: -1, }, &reply); err != nil { @@ -130,7 +130,7 @@ func testV1FIdxHAccountActionPlansHealth(t *testing.T) { func testV1FIdxHReverseDestinationHealth(t *testing.T) { var reply engine.ReverseDestinationsIHReply - if err := tFIdxHRpc.Call(utils.APIerSv1GetReverseDestinationsIndexHealth, engine.IndexHealthArgs{ + if err := tFIdxHRpc.Call(utils.APIerSv1GetReverseDestinationsIndexHealth, engine.IndexHealthArgsWith2Ch{ IndexCacheLimit: -1, ObjectCacheLimit: -1, }, &reply); err != nil { diff --git a/engine/libindex_health.go b/engine/libindex_health.go index 13aae04ab..87c8a0e62 100644 --- a/engine/libindex_health.go +++ b/engine/libindex_health.go @@ -27,7 +27,7 @@ import ( "github.com/cgrates/ltcache" ) -type IndexHealthArgs struct { +type IndexHealthArgsWith2Ch struct { IndexCacheLimit int IndexCacheTTL time.Duration IndexCacheStaticTTL bool @@ -37,6 +37,20 @@ type IndexHealthArgs struct { ObjectCacheStaticTTL bool } +type IndexHealthArgsWith3Ch struct { + IndexCacheLimit int + IndexCacheTTL time.Duration + IndexCacheStaticTTL bool + + ObjectCacheLimit int + ObjectCacheTTL time.Duration + ObjectCacheStaticTTL bool + + FilterCacheLimit int + FilterCacheTTL time.Duration + FilterCacheStaticTTL bool +} + type AccountActionPlanIHReply struct { MissingAccountActionPlans map[string][]string // list of missing indexes for each object (the map has the key as the indexKey and a list of objects) BrokenReferences map[string][]string // list of broken references (the map has the key as the objectID and a list of indexes) @@ -271,9 +285,14 @@ func GetReverseDestinationsIndexHealth(dm *DataManager, objLimit, indexLimit int } type FilterIHReply struct { + MissingObjects []string // list of object that are referenced in indexes but are not found in the dataDB + MissingIndexes map[string][]string // list of missing indexes for each object (the map has the key as the objectID and a list of indexes) + MissingFilters map[string][]string // list of broken references (the map has the key as the filterID and a list of objectIDs) +} + +type ReverseFilterIHReply struct { MissingObjects []string // list of object that are referenced in indexes but are not found in the dataDB - MissingIndexes map[string][]string // list of missing indexes for each object (the map has the key as the objectID and a list of indexes) - MissingReverseIndexes map[string][]string // list of missing reverse indexes for each object (the map has the key as the objectID and a list of filters) + MissingReverseIndexes map[string][]string // list of missing indexes for each object (the map has the key as the objectID and a list of indexes) MissingFilters map[string][]string // list of broken references (the map has the key as the filterID and a list of objectIDs) } @@ -472,6 +491,7 @@ func updateFilterIHMisingIndx(dm *DataManager, fltrCache, fltrIdxCache *ltcache. return } rply.MissingFilters[fltrID] = append(rply.MissingFilters[fltrID], itmID) + continue } var indexes map[string]utils.StringSet if indexes, err = getFilterAsIndexSet(dm, fltrIdxCache, indxType, tntCtx, fltr); err != nil { @@ -487,9 +507,13 @@ func updateFilterIHMisingIndx(dm *DataManager, fltrCache, fltrIdxCache *ltcache. return rply, nil } -// getFltrIdxHealth returns the missing indexes for all objects -func getFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, revFltrIdxCache, objCache *ltcache.Cache, indxType string) (rply *FilterIHReply, err error) { +// GetFltrIdxHealth returns the missing indexes for all objects +func GetFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, objCache *ltcache.Cache, indxType string) (rply *FilterIHReply, err error) { // check the objects ( obj->filter->index relation) + rply = &FilterIHReply{ + MissingIndexes: make(map[string][]string), + MissingFilters: make(map[string][]string), + } objPrfx := utils.CacheIndexesToPrefix[indxType] var ids []string if ids, err = dm.dataDB.GetKeysForPrefix(objPrfx); err != nil { @@ -507,48 +531,12 @@ func getFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, revFltrIdxCache, if rply, err = updateFilterIHMisingIndx(dm, fltrCache, fltrIdxCache, obj.filterIDs, indxType, tntID.Tenant, tntID.Tenant, id, rply); err != nil { return } - for _, fltrID := range obj.filterIDs { - if strings.HasPrefix(fltrID, utils.Meta) { - continue - } - var revIdx utils.StringSet - if revIdx, err = getIHFltrIdxFromCache(dm, revFltrIdxCache, utils.CacheReverseFilterIndexes, utils.ConcatenatedKey(tntID.Tenant, fltrID), indxType); err != nil { - if err == utils.ErrNotFound { - rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], fltrID) - continue - } - return - } - if !revIdx.Has(tntID.ID) { - rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], fltrID) - } - } } else { for _, ctx := range *obj.contexts { if rply, err = updateFilterIHMisingIndx(dm, fltrCache, fltrIdxCache, obj.filterIDs, indxType, tntID.Tenant, utils.ConcatenatedKey(tntID.Tenant, ctx), id, rply); err != nil { return } } - for _, fltrID := range obj.filterIDs { - if strings.HasPrefix(fltrID, utils.Meta) { - continue - } - var revIdx utils.StringSet - if revIdx, err = getIHFltrIdxFromCache(dm, revFltrIdxCache, utils.CacheReverseFilterIndexes, utils.ConcatenatedKey(tntID.Tenant, fltrID), indxType); err != nil { - if err == utils.ErrNotFound { - for _, ctx := range *obj.contexts { - rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], utils.ConcatenatedKey(fltrID, ctx)) - } - continue - } - return - } - for _, ctx := range *obj.contexts { - if !revIdx.Has(utils.ConcatenatedKey(id, ctx)) { - rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], utils.ConcatenatedKey(fltrID, ctx)) - } - } - } } } @@ -586,6 +574,7 @@ func getFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, revFltrIdxCache, return } rply.MissingObjects = append(rply.MissingObjects, utils.ConcatenatedKey(tnt, itmID)) + err = nil continue } if ctx != nil || @@ -603,6 +592,7 @@ func getFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, revFltrIdxCache, } key := utils.ConcatenatedKey(tntCtx, idxKey) rply.MissingIndexes[key] = append(rply.MissingIndexes[key], itmID) + err = nil } else if !rcvIndx.Has(itmID) { key := utils.ConcatenatedKey(tntCtx, idxKey) rply.MissingIndexes[key] = append(rply.MissingIndexes[key], itmID) @@ -617,6 +607,7 @@ func getFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, revFltrIdxCache, } fltrID = utils.ConcatenatedKey(tnt, fltrID) rply.MissingFilters[fltrID] = append(rply.MissingFilters[fltrID], itmID) + err = nil continue } var indexes map[string]utils.StringSet @@ -631,14 +622,90 @@ func getFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, revFltrIdxCache, } } - revIdxPrfx := utils.CacheInstanceToPrefix[indxType] + return +} + +// GetRevFltrIdxHealth returns the missing reverse indexes for all objects +func getRevFltrIdxHealthFromObj(dm *DataManager, fltrCache, revFltrIdxCache, objCache *ltcache.Cache, indxType string) (rply *ReverseFilterIHReply, err error) { + // check the objects ( obj->filter->index relation) + rply = &ReverseFilterIHReply{ + MissingReverseIndexes: make(map[string][]string), + MissingFilters: make(map[string][]string), + } + objPrfx := utils.CacheIndexesToPrefix[indxType] + var ids []string + if ids, err = dm.dataDB.GetKeysForPrefix(objPrfx); err != nil { + return + } + for _, id := range ids { + id = strings.TrimPrefix(id, objPrfx) + tntID := utils.NewTenantID(id) + var obj *objFIH + if obj, err = getIHObjFromCache(dm, objCache, indxType, tntID.Tenant, tntID.ID); err != nil { + return + } + + if obj.contexts == nil { + for _, fltrID := range obj.filterIDs { + if strings.HasPrefix(fltrID, utils.Meta) { + continue + } + var revIdx utils.StringSet + if revIdx, err = getIHFltrIdxFromCache(dm, revFltrIdxCache, utils.CacheReverseFilterIndexes, utils.ConcatenatedKey(tntID.Tenant, fltrID), indxType); err != nil { + if err == utils.ErrNotFound { + rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], fltrID) + err = nil + continue + } + return + } + if !revIdx.Has(tntID.ID) { + rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], fltrID) + } + } + } else { + for _, fltrID := range obj.filterIDs { + if strings.HasPrefix(fltrID, utils.Meta) { + continue + } + var revIdx utils.StringSet + if revIdx, err = getIHFltrIdxFromCache(dm, revFltrIdxCache, utils.CacheReverseFilterIndexes, utils.ConcatenatedKey(tntID.Tenant, fltrID), indxType); err != nil { + if err == utils.ErrNotFound { + for _, ctx := range *obj.contexts { + rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], utils.ConcatenatedKey(fltrID, ctx)) + } + err = nil + continue + } + return + } + for _, ctx := range *obj.contexts { + if !revIdx.Has(utils.ConcatenatedKey(id, ctx)) { + rply.MissingReverseIndexes[id] = append(rply.MissingReverseIndexes[id], utils.ConcatenatedKey(fltrID, ctx)) + } + } + } + } + } + return +} +func getRevFltrIdxHealthFromReverse(dm *DataManager, fltrCache, revFltrIdxCache, objCache *ltcache.Cache, rply map[string]*ReverseFilterIHReply) (_ map[string]*ReverseFilterIHReply, err error) { var revIndexKeys []string - if revIndexKeys, err = dm.dataDB.GetKeysForPrefix(revIdxPrfx); err != nil { + if revIndexKeys, err = dm.dataDB.GetKeysForPrefix(utils.FilterIndexPrfx); err != nil { return } for _, revIdxKey := range revIndexKeys { - revIdxKey = strings.TrimPrefix(revIdxKey, revIdxPrfx) - fltrID := utils.NewTenantID(revIdxKey) + revIdxKey = strings.TrimPrefix(revIdxKey, utils.FilterIndexPrfx) + revIDxSplit := strings.SplitN(revIdxKey, utils.ConcatenatedKeySep, 3) + tnt, fltrID, indxType := revIDxSplit[0], revIDxSplit[1], revIDxSplit[2] + revIdxKey = utils.ConcatenatedKey(tnt, fltrID) + + if _, has := rply[indxType]; !has { + rply[indxType] = &ReverseFilterIHReply{ + MissingReverseIndexes: make(map[string][]string), + MissingFilters: make(map[string][]string), + } + } var revIdx utils.StringSet if revIdx, err = getIHFltrIdxFromCache(dm, revFltrIdxCache, utils.CacheReverseFilterIndexes, revIdxKey, indxType); err != nil { @@ -654,20 +721,42 @@ func getFltrIdxHealth(dm *DataManager, fltrCache, fltrIdxCache, revFltrIdxCache, ctx = spl[1] } var obj *objFIH - if obj, err = getIHObjFromCache(dm, objCache, indxType, fltrID.ID, id); err != nil { + if obj, err = getIHObjFromCache(dm, objCache, indxType, tnt, id); err != nil { if err == utils.ErrNotFound { - rply.MissingObjects = append(rply.MissingObjects, utils.ConcatenatedKey(fltrID.Tenant, id)) + rply[indxType].MissingObjects = append(rply[indxType].MissingObjects, utils.ConcatenatedKey(tnt, id)) + err = nil continue } return } - if !utils.IsSliceMember(obj.filterIDs, fltrID.ID) { - rply.MissingFilters[revIdxKey] = append(rply.MissingFilters[revIdxKey], id) + if !utils.IsSliceMember(obj.filterIDs, fltrID) { + rply[indxType].MissingFilters[revIdxKey] = append(rply[indxType].MissingFilters[revIdxKey], id) } else if obj.contexts != nil && !utils.IsSliceMember(*obj.contexts, ctx) { - rply.MissingFilters[revIdxKey] = append(rply.MissingFilters[revIdxKey], itemIDCtx) + rply[indxType].MissingFilters[revIdxKey] = append(rply[indxType].MissingFilters[revIdxKey], itemIDCtx) } } } + return rply, nil +} + +func GetRevFltrIdxHealth(dm *DataManager, fltrCache, revFltrIdxCache, objCache *ltcache.Cache) (rply map[string]*ReverseFilterIHReply, err error) { + rply = make(map[string]*ReverseFilterIHReply) + for indxType := range utils.CacheIndexesToPrefix { + if indxType == utils.CacheReverseFilterIndexes { + continue + } + if rply[indxType], err = getRevFltrIdxHealthFromObj(dm, fltrCache, revFltrIdxCache, objCache, indxType); err != nil { + return + } + } + rply, err = getRevFltrIdxHealthFromReverse(dm, fltrCache, revFltrIdxCache, objCache, rply) + for k, v := range rply { // shpuld be a safe for (even on rply==nil) + if len(v.MissingFilters) == 0 && + len(v.MissingObjects) == 0 && + len(v.MissingReverseIndexes) == 0 { + delete(rply, k) + } + } return } diff --git a/engine/z_libindex_health_test.go b/engine/z_libindex_health_test.go index 5e1529260..36819a2b0 100644 --- a/engine/z_libindex_health_test.go +++ b/engine/z_libindex_health_test.go @@ -24,6 +24,7 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/ltcache" ) func TestHealthAccountAction(t *testing.T) { @@ -283,3 +284,94 @@ func TestHealthReverseDestination4(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) } } + +func TestHealthFilter(t *testing.T) { + Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dm := NewDataManager(db, cfg.CacheCfg(), nil) + + if err := dm.SetAttributeProfile(&AttributeProfile{ + Tenant: "cgrates.org", + ID: "ATTR1", + Contexts: []string{utils.MetaAny}, + FilterIDs: []string{"*string:~*req.Account:1001", "Fltr1"}, + }, false); err != nil { + t.Fatal(err) + } + + if err := dm.SetIndexes(utils.CacheAttributeFilterIndexes, "cgrates.org:*any", + map[string]utils.StringSet{"*string:*req.Account:1002": {"ATTR1": {}, "ATTR2": {}}}, + true, utils.NonTransactional); err != nil { + t.Fatal(err) + } + exp := &FilterIHReply{ + MissingIndexes: map[string][]string{ + "cgrates.org:*any:*string:*req.Account:1001": {"cgrates.org:ATTR1"}, + "cgrates.org:*any:*string:*req.Account:1002": {"ATTR1"}, + }, + MissingFilters: map[string][]string{ + "Fltr1": {"cgrates.org:ATTR1"}, + }, + MissingObjects: []string{"cgrates.org:ATTR2"}, + } + + if rply, err := GetFltrIdxHealth(dm, + ltcache.NewCache(-1, 0, false, nil), + ltcache.NewCache(-1, 0, false, nil), + ltcache.NewCache(-1, 0, false, nil), + utils.CacheAttributeFilterIndexes); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(exp, rply) { + t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) + } +} + +func TestHealthReverseFilter(t *testing.T) { + Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dm := NewDataManager(db, cfg.CacheCfg(), nil) + + if err := dm.SetAttributeProfile(&AttributeProfile{ + Tenant: "cgrates.org", + ID: "ATTR1", + Contexts: []string{utils.MetaAny}, + FilterIDs: []string{"*string:~*req.Account:1001", "Fltr1"}, + }, false); err != nil { + t.Fatal(err) + } + + if err := dm.SetIndexes(utils.CacheReverseFilterIndexes, "cgrates.org:Fltr2", + map[string]utils.StringSet{utils.CacheAttributeFilterIndexes: {"ATTR1:*cdrs": {}, "ATTR2:*any": {}}}, + true, utils.NonTransactional); err != nil { + t.Fatal(err) + } + + if err := dm.SetIndexes(utils.CacheReverseFilterIndexes, "cgrates.org:Fltr1", + map[string]utils.StringSet{utils.CacheAttributeFilterIndexes: {"ATTR1:*cdrs": {}}}, + true, utils.NonTransactional); err != nil { + t.Fatal(err) + } + exp := map[string]*ReverseFilterIHReply{ + utils.CacheAttributeFilterIndexes: { + MissingReverseIndexes: map[string][]string{ + "cgrates.org:ATTR1": {"Fltr1:*any"}, + }, + MissingFilters: map[string][]string{ + "cgrates.org:Fltr2": {"ATTR1"}, + "cgrates.org:Fltr1": {"ATTR1:*cdrs"}, + }, + MissingObjects: []string{"cgrates.org:ATTR2"}, + }, + } + + if rply, err := GetRevFltrIdxHealth(dm, + ltcache.NewCache(-1, 0, false, nil), + ltcache.NewCache(-1, 0, false, nil), + ltcache.NewCache(-1, 0, false, nil)); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(exp, rply) { + t.Errorf("Expecting: %+v,\n received: %+v", utils.ToJSON(exp), utils.ToJSON(rply)) + } +}