From c8750261ebf3e73d4aa1bcfe4fb9ba5d7eb9b6ec Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 9 Nov 2016 16:53:06 +0100 Subject: [PATCH] SMGeneric - improved session indexing considering runID, passive sessions with session prefix implementation --- sessionmanager/smgeneric.go | 220 ++++++++++---------- sessionmanager/smgeneric_test.go | 341 +++++++++++++++++++++++++------ utils/map.go | 8 + 3 files changed, 391 insertions(+), 178 deletions(-) diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index dcbd399d5..5e37954d5 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -58,11 +58,11 @@ func NewSMGeneric(cgrCfg *config.CGRConfig, rals rpcclient.RpcClientConnection, Timezone: timezone, activeSessions: make(map[string][]*SMGSession), aSessionsIdxCfg: aSessIdxCfg, - aSessionsIndex: make(map[string]map[string]utils.StringMap), + aSessionsIndex: make(map[string]map[string]map[string]utils.StringMap), aSessionsRIndex: make(map[string][]*riFieldNameVal), passiveSessions: make(map[string][]*SMGSession), pSessionsIdxCfg: utils.StringMap{utils.ACCID: true}, - pSessionsIndex: make(map[string]map[string]utils.StringMap), + pSessionsIndex: make(map[string]map[string]map[string]utils.StringMap), pSessionsRIndex: make(map[string][]*riFieldNameVal), sessionTerminators: make(map[string]*smgSessionTerminator), responseCache: cache.NewResponseCache(cgrCfg.ResponseCacheTTL)} @@ -76,24 +76,24 @@ type SMGeneric struct { Timezone string activeSessions map[string][]*SMGSession // group sessions per sessionId, multiple runs based on derived charging aSessionsMux sync.RWMutex - aSessionsIdxCfg utils.StringMap // index configuration - aSessionsIndex map[string]map[string]utils.StringMap // map[fieldName]map[fieldValue]utils.StringMap[cgrID] - aSessionsRIndex map[string][]*riFieldNameVal // reverse indexes for active sessions, used on remove - aSIMux sync.RWMutex // protects aSessionsIndex - passiveSessions map[string][]*SMGSession // group passive sessions + aSessionsIdxCfg utils.StringMap // index configuration + aSessionsIndex map[string]map[string]map[string]utils.StringMap // map[fieldName]map[fieldValue][runID]utils.StringMap[cgrID] + aSessionsRIndex map[string][]*riFieldNameVal // reverse indexes for active sessions, used on remove + aSIMux sync.RWMutex // protects aSessionsIndex + passiveSessions map[string][]*SMGSession // group passive sessions pSessionsMux sync.RWMutex pSessionsIdxCfg utils.StringMap - pSessionsIndex map[string]map[string]utils.StringMap // map[fieldName]map[fieldValue]utils.StringMap[cgrID] - pSessionsRIndex map[string][]*riFieldNameVal // reverse indexes for active sessions, used on remove - pSIMux sync.RWMutex // protects pSessionsIndex - sessionTerminators map[string]*smgSessionTerminator // terminate and cleanup the session if timer expires - sTsMux sync.Mutex // protects sessionTerminators - responseCache *cache.ResponseCache // cache replies here + pSessionsIndex map[string]map[string]map[string]utils.StringMap // map[fieldName]map[fieldValue][runID]utils.StringMap[cgrID] + pSessionsRIndex map[string][]*riFieldNameVal // reverse indexes for active sessions, used on remove + pSIMux sync.RWMutex // protects pSessionsIndex + sessionTerminators map[string]*smgSessionTerminator // terminate and cleanup the session if timer expires + sTsMux sync.Mutex // protects sessionTerminators + responseCache *cache.ResponseCache // cache replies here } // riFieldNameVal is a reverse index entry type riFieldNameVal struct { - fieldName, fieldValue string + runID, fieldName, fieldValue string } type smgSessionTerminator struct { @@ -184,7 +184,7 @@ func (smg *SMGeneric) recordASession(s *SMGSession) { smg.aSessionsMux.Lock() smg.activeSessions[s.CGRID] = append(smg.activeSessions[s.CGRID], s) smg.setSessionTerminator(s) - smg.indexASession(s) + smg.indexSession(s, false) smg.aSessionsMux.Unlock() } @@ -199,15 +199,26 @@ func (smg *SMGeneric) unrecordASession(cgrID string) bool { if st, found := smg.sessionTerminators[cgrID]; found { st.endChan <- true } - smg.unindexASession(cgrID) + smg.unindexSession(cgrID, false) return true } -// indexASession explores settings and builds smg.aSessionsIndex based on that -func (smg *SMGeneric) indexASession(s *SMGSession) bool { - smg.aSIMux.Lock() - defer smg.aSIMux.Unlock() - for fieldName := range smg.aSessionsIdxCfg { +// indexSession explores settings and builds SessionsIndex +// uses different tables and mutex-es depending on active/passive session +func (smg *SMGeneric) indexSession(s *SMGSession, passiveSessions bool) bool { + idxMux := smg.aSIMux + idxCfg := smg.aSessionsIdxCfg + ssIndx := smg.aSessionsIndex + ssRIdx := smg.aSessionsRIndex + if passiveSessions { + idxMux = smg.pSIMux + idxCfg = smg.pSessionsIdxCfg + ssIndx = smg.pSessionsIndex + ssRIdx = smg.pSessionsRIndex + } + idxMux.Lock() + defer idxMux.Unlock() + for fieldName := range idxCfg { fieldVal, err := utils.ReflectFieldAsString(s.EventStart, fieldName, "") if err != nil { if err == utils.ErrNotFound { @@ -220,115 +231,93 @@ func (smg *SMGeneric) indexASession(s *SMGSession) bool { if fieldVal == "" { fieldVal = utils.MetaEmpty } - if _, hasFieldName := smg.aSessionsIndex[fieldName]; !hasFieldName { // Init it here so we can minimize - smg.aSessionsIndex[fieldName] = make(map[string]utils.StringMap) + if _, hasFieldName := ssIndx[fieldName]; !hasFieldName { // Init it here + ssIndx[fieldName] = make(map[string]map[string]utils.StringMap) } - if _, hasFieldVal := smg.aSessionsIndex[fieldName][fieldVal]; !hasFieldVal { - smg.aSessionsIndex[fieldName][fieldVal] = make(utils.StringMap) + if _, hasFieldVal := ssIndx[fieldName][fieldVal]; !hasFieldVal { + ssIndx[fieldName][fieldVal] = make(map[string]utils.StringMap) } - smg.aSessionsIndex[fieldName][fieldVal][s.CGRID] = true - if _, hasIt := smg.aSessionsRIndex[s.CGRID]; !hasIt { - smg.aSessionsRIndex[s.CGRID] = make([]*riFieldNameVal, 0) + if _, hasFieldVal := ssIndx[fieldName][fieldVal][s.RunID]; !hasFieldVal { + ssIndx[fieldName][fieldVal][s.RunID] = make(utils.StringMap) } - smg.aSessionsRIndex[s.CGRID] = append(smg.aSessionsRIndex[s.CGRID], &riFieldNameVal{fieldName: fieldName, fieldValue: fieldVal}) + ssIndx[fieldName][fieldVal][s.RunID][s.CGRID] = true + if _, hasIt := ssRIdx[s.CGRID]; !hasIt { + ssRIdx[s.CGRID] = make([]*riFieldNameVal, 0) + } + ssRIdx[s.CGRID] = append(ssRIdx[s.CGRID], &riFieldNameVal{runID: s.RunID, fieldName: fieldName, fieldValue: fieldVal}) } return true } // unindexASession removes a session from indexes -func (smg *SMGeneric) unindexASession(cgrID string) bool { - smg.aSIMux.Lock() - defer smg.aSIMux.Unlock() - if _, hasIt := smg.aSessionsRIndex[cgrID]; !hasIt { +func (smg *SMGeneric) unindexSession(cgrID string, passiveSessions bool) bool { + idxMux := smg.aSIMux + ssRIdx := smg.aSessionsRIndex + ssIndx := smg.aSessionsIndex + if passiveSessions { + idxMux = smg.pSIMux + ssRIdx = smg.pSessionsRIndex + ssIndx = smg.pSessionsIndex + } + + if _, hasIt := ssRIdx[cgrID]; !hasIt { return false } - for _, riFNV := range smg.aSessionsRIndex[cgrID] { - delete(smg.aSessionsIndex[riFNV.fieldName][riFNV.fieldValue], cgrID) - if len(smg.aSessionsIndex[riFNV.fieldName][riFNV.fieldValue]) == 0 { - delete(smg.aSessionsIndex[riFNV.fieldName], riFNV.fieldValue) + idxMux.Lock() + defer idxMux.Unlock() + for _, riFNV := range ssRIdx[cgrID] { + delete(ssIndx[riFNV.fieldName][riFNV.fieldValue][riFNV.runID], cgrID) + if len(ssIndx[riFNV.fieldName][riFNV.fieldValue][riFNV.runID]) == 0 { + delete(ssIndx[riFNV.fieldName][riFNV.fieldValue], riFNV.runID) } - if len(smg.aSessionsIndex[riFNV.fieldName]) == 0 { - delete(smg.aSessionsIndex, riFNV.fieldName) + if len(ssIndx[riFNV.fieldName][riFNV.fieldValue]) == 0 { + delete(ssIndx[riFNV.fieldName], riFNV.fieldValue) + } + if len(ssIndx[riFNV.fieldName]) == 0 { + delete(ssIndx, riFNV.fieldName) } } - delete(smg.aSessionsRIndex, cgrID) - return true -} - -// indexASession explores settings and builds smg.pSessionsIndex based on that -func (smg *SMGeneric) indexPSession(s *SMGSession) bool { - smg.pSIMux.Lock() - defer smg.pSIMux.Unlock() - for fieldName := range smg.pSessionsIdxCfg { - fieldVal, err := utils.ReflectFieldAsString(s.EventStart, fieldName, "") - if err != nil { - if err == utils.ErrNotFound { - fieldVal = utils.NOT_AVAILABLE - } else { - utils.Logger.Err(fmt.Sprintf(" Error retrieving field: %s from event: %+v", fieldName, s.EventStart)) - continue - } - } - if fieldVal == "" { - fieldVal = utils.MetaEmpty - } - if _, hasFieldName := smg.pSessionsIndex[fieldName]; !hasFieldName { // Init it here so we can minimize - smg.pSessionsIndex[fieldName] = make(map[string]utils.StringMap) - } - if _, hasFieldVal := smg.pSessionsIndex[fieldName][fieldVal]; !hasFieldVal { - smg.pSessionsIndex[fieldName][fieldVal] = make(utils.StringMap) - } - smg.pSessionsIndex[fieldName][fieldVal][s.CGRID] = true - smg.pSessionsRIndex[s.CGRID] = append(smg.pSessionsRIndex[s.CGRID], &riFieldNameVal{fieldName: fieldName, fieldValue: fieldVal}) - } - return true -} - -// unindexASession removes a session from indexes -func (smg *SMGeneric) unindexPSession(cgrID string) bool { - smg.pSIMux.Lock() - defer smg.pSIMux.Unlock() - if _, hasIt := smg.pSessionsRIndex[cgrID]; !hasIt { - return false - } - for _, riFNV := range smg.pSessionsRIndex[cgrID] { - delete(smg.pSessionsIndex[riFNV.fieldName][riFNV.fieldValue], cgrID) - if len(smg.pSessionsIndex[riFNV.fieldName][riFNV.fieldValue]) == 0 { - delete(smg.pSessionsIndex[riFNV.fieldName], riFNV.fieldValue) - } - if len(smg.pSessionsIndex[riFNV.fieldName]) == 0 { - delete(smg.pSessionsIndex, riFNV.fieldName) - } - } - delete(smg.pSessionsRIndex, cgrID) + delete(ssRIdx, cgrID) return true } // getSessionIDsMatchingIndexes will check inside indexes if it can find sessionIDs matching all filters // matchedIndexes returns map[matchedFieldName]possibleMatchedFieldVal so we optimize further to avoid checking them -func (smg *SMGeneric) getSessionIDsMatchingIndexes(fltrs map[string]string) (utils.StringMap, map[string]string) { - smg.aSIMux.RLock() - defer smg.aSIMux.RUnlock() - sessionIDxes := smg.aSessionsIndex // Clone here and unlock sooner if getting slow +func (smg *SMGeneric) getSessionIDsMatchingIndexes(fltrs map[string]string, passiveSessions bool) (utils.StringMap, map[string]string) { + idxMux := smg.aSIMux + ssIndx := smg.aSessionsIndex + if passiveSessions { + idxMux = smg.pSIMux + ssIndx = smg.pSessionsIndex + } + idxMux.RLock() + defer idxMux.RUnlock() matchedIndexes := make(map[string]string) - var matchingSessions utils.StringMap + matchingSessions := make(utils.StringMap) + runID := fltrs[utils.MEDI_RUNID] checkNr := 0 for fltrName, fltrVal := range fltrs { checkNr += 1 - if _, hasFldName := sessionIDxes[fltrName]; !hasFldName { + if _, hasFldName := ssIndx[fltrName]; !hasFldName { continue } - if _, hasFldVal := sessionIDxes[fltrName][fltrVal]; !hasFldVal { + if _, hasFldVal := ssIndx[fltrName][fltrVal]; !hasFldVal { matchedIndexes[fltrName] = utils.META_NONE continue } matchedIndexes[fltrName] = fltrVal if checkNr == 1 { // First run will init the MatchingSessions - matchingSessions = sessionIDxes[fltrName][fltrVal] + if runID == "" { + for runID := range ssIndx[fltrName][fltrVal] { + matchingSessions.Join(ssIndx[fltrName][fltrVal][runID]) + } + } else { // We know the RunID + matchingSessions = ssIndx[fltrName][fltrVal][runID] + } continue } // Higher run, takes out non matching indexes - for cgrID := range sessionIDxes[fltrName][fltrVal] { + for cgrID := range ssIndx[fltrName][fltrVal] { if _, hasCGRID := matchingSessions[cgrID]; !hasCGRID { delete(matchingSessions, cgrID) } @@ -338,14 +327,20 @@ func (smg *SMGeneric) getSessionIDsMatchingIndexes(fltrs map[string]string) (uti } // getSessionIDsForPrefix works with session relocation returning list of sessions with ID matching prefix for OriginID field -func (smg *SMGeneric) getSessionIDsForPrefix(prefix string) (cgrIDs []string) { - smg.aSessionsMux.Lock() - defer smg.aSessionsMux.Unlock() - for originID := range smg.aSessionsIndex[utils.ACCID] { +func (smg *SMGeneric) getSessionIDsForPrefix(prefix string, passiveSessions bool) (cgrIDs []string) { + idxMux := smg.aSIMux + ssIndx := smg.aSessionsIndex + if passiveSessions { + idxMux = smg.pSIMux + ssIndx = smg.pSessionsIndex + } + idxMux.RLock() + for originID := range ssIndx[utils.ACCID][utils.META_DEFAULT] { if strings.HasPrefix(originID, prefix) { - cgrIDs = append(cgrIDs, smg.aSessionsIndex[utils.ACCID][originID].Slice()...) + cgrIDs = append(cgrIDs, ssIndx[utils.ACCID][originID][utils.META_DEFAULT].Slice()...) } } + idxMux.RUnlock() return } @@ -526,10 +521,10 @@ func (smg *SMGeneric) setPassiveSessions(cgrID string, ss []*SMGSession) (err er smg.unrecordASession(cgrID) smg.pSessionsMux.Lock() smg.passiveSessions[cgrID] = ss - for _, s := range ss { - smg.indexPSession(s) - } smg.pSessionsMux.Unlock() + for _, s := range ss { + smg.indexSession(s, true) + } return } @@ -544,7 +539,7 @@ func (smg *SMGeneric) removePassiveSessions(cgrID string) (err error) { return ErrActiveSession } } - smg.unrecordASession(cgrID) + smg.unrecordASession(cgrID) // just in case there is an active session smg.deletePassiveSessions(cgrID) return } @@ -552,8 +547,8 @@ func (smg *SMGeneric) removePassiveSessions(cgrID string) (err error) { // deletePassiveSessions is used to remove a reference from the passiveSessions table // ToDo: test it func (smg *SMGeneric) deletePassiveSessions(cgrID string) { + smg.unindexSession(cgrID, true) smg.pSessionsMux.Lock() - smg.unindexPSession(cgrID) delete(smg.passiveSessions, cgrID) smg.pSessionsMux.Unlock() } @@ -727,7 +722,12 @@ func (smg *SMGeneric) TerminateSession(gev SMGenericEvent, clnt rpcclient.RpcCli sessionIDs := []string{cgrID} if gev.HasField(utils.OriginIDPrefix) { // OriginIDPrefix is present, OriginID will not be anymore considered if sessionIDPrefix, errPrefix := gev.GetFieldAsString(utils.OriginIDPrefix); errPrefix == nil { - sessionIDs = smg.getSessionIDsForPrefix(sessionIDPrefix) + if sessionIDs = smg.getSessionIDsForPrefix(sessionIDPrefix, false); len(sessionIDs) == 0 { + sessionIDs = smg.getSessionIDsForPrefix(sessionIDPrefix, true) + for _, sessionID := range sessionIDs { // activate sessions for prefix + smg.passiveToActive(sessionID) + } + } } } usage, errUsage := gev.GetUsage(utils.META_DEFAULT) @@ -905,7 +905,7 @@ func (smg *SMGeneric) getASessions() map[string][]*SMGSession { func (smg *SMGeneric) ActiveSessions(fltrs map[string]string, count bool) (aSessions []*ActiveSession, counter int, err error) { aSessions = make([]*ActiveSession, 0) // Make sure we return at least empty list and not nil // Check first based on indexes so we can downsize the list of matching sessions - matchingSessionIDs, checkedFilters := smg.getSessionIDsMatchingIndexes(fltrs) + matchingSessionIDs, checkedFilters := smg.getSessionIDsMatchingIndexes(fltrs, false) if len(matchingSessionIDs) == 0 && len(checkedFilters) != 0 { return } diff --git a/sessionmanager/smgeneric_test.go b/sessionmanager/smgeneric_test.go index 8bdd6fddf..84f24c53b 100644 --- a/sessionmanager/smgeneric_test.go +++ b/sessionmanager/smgeneric_test.go @@ -60,40 +60,63 @@ func TestSMGSessionIndexing(t *testing.T) { "Extra3": "", } // Index first session - smgSession := &SMGSession{CGRID: smGev.GetCGRID(utils.META_DEFAULT), EventStart: smGev} + smgSession := &SMGSession{CGRID: smGev.GetCGRID(utils.META_DEFAULT), RunID: utils.META_DEFAULT, EventStart: smGev} cgrID := smGev.GetCGRID(utils.META_DEFAULT) - smg.indexASession(smgSession) - eIndexes := map[string]map[string]utils.StringMap{ - "OriginID": map[string]utils.StringMap{ - "12345": utils.StringMap{ - cgrID: true, + smg.indexSession(smgSession, false) + eIndexes := map[string]map[string]map[string]utils.StringMap{ + "OriginID": map[string]map[string]utils.StringMap{ + "12345": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, }, }, - "Tenant": map[string]utils.StringMap{ - "cgrates.org": utils.StringMap{ - cgrID: true, + "Tenant": map[string]map[string]utils.StringMap{ + "cgrates.org": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, }, }, - "Account": map[string]utils.StringMap{ - "account1": utils.StringMap{ - cgrID: true, + "Account": map[string]map[string]utils.StringMap{ + "account1": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, }, }, - "Extra3": map[string]utils.StringMap{ - utils.MetaEmpty: utils.StringMap{ - cgrID: true, + "Extra3": map[string]map[string]utils.StringMap{ + utils.MetaEmpty: map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, }, }, - "Extra4": map[string]utils.StringMap{ - utils.NOT_AVAILABLE: utils.StringMap{ - cgrID: true, + "Extra4": map[string]map[string]utils.StringMap{ + utils.NOT_AVAILABLE: map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, }, }, } if !reflect.DeepEqual(eIndexes, smg.aSessionsIndex) { t.Errorf("Expecting: %+v, received: %+v", eIndexes, smg.aSessionsIndex) } - // Index seccond session + eRIdxes := map[string][]*riFieldNameVal{ + cgrID: []*riFieldNameVal{ + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Tenant", fieldValue: "cgrates.org"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Account", fieldValue: "account1"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra3", fieldValue: utils.MetaEmpty}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra4", fieldValue: utils.NOT_AVAILABLE}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "OriginID", fieldValue: "12345"}, + }, + } + if len(eRIdxes) != len(smg.aSessionsRIndex) || + len(eRIdxes[cgrID]) != len(smg.aSessionsRIndex[cgrID]) { // cannot keep order here due to field names coming from map + t.Errorf("Expecting: %+v, received: %+v", eRIdxes, smg.aSessionsRIndex) + } + // Index second session smGev2 := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT2", utils.ACCID: "12346", @@ -105,83 +128,265 @@ func TestSMGSessionIndexing(t *testing.T) { "Extra4": "info2", } cgrID2 := smGev2.GetCGRID(utils.META_DEFAULT) - smgSession2 := &SMGSession{CGRID: smGev2.GetCGRID(utils.META_DEFAULT), EventStart: smGev2} - smg.indexASession(smgSession2) - eIndexes = map[string]map[string]utils.StringMap{ - "OriginID": map[string]utils.StringMap{ - "12345": utils.StringMap{ - cgrID: true, + smgSession2 := &SMGSession{CGRID: smGev2.GetCGRID(utils.META_DEFAULT), RunID: utils.META_DEFAULT, EventStart: smGev2} + smg.indexSession(smgSession2, false) + smGev3 := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT3", + utils.TENANT: "cgrates.org", + utils.ACCID: "12347", + utils.ACCOUNT: "account2", + "Extra5": "info5", + } + cgrID3 := smGev3.GetCGRID(utils.META_DEFAULT) + smgSession3 := &SMGSession{CGRID: smGev3.GetCGRID(utils.META_DEFAULT), RunID: "secondRun", EventStart: smGev3} + smg.indexSession(smgSession3, false) + eIndexes = map[string]map[string]map[string]utils.StringMap{ + "OriginID": map[string]map[string]utils.StringMap{ + "12345": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, }, - "12346": utils.StringMap{ - cgrID2: true, + "12346": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + "12347": map[string]utils.StringMap{ + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, }, - "Tenant": map[string]utils.StringMap{ - "cgrates.org": utils.StringMap{ - cgrID: true, + "Tenant": map[string]map[string]utils.StringMap{ + "cgrates.org": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, - "itsyscom.com": utils.StringMap{ - cgrID2: true, + "itsyscom.com": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, }, }, - "Account": map[string]utils.StringMap{ - "account1": utils.StringMap{ - cgrID: true, + "Account": map[string]map[string]utils.StringMap{ + "account1": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, }, - "account2": utils.StringMap{ - cgrID2: true, + "account2": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, }, - "Extra3": map[string]utils.StringMap{ - utils.MetaEmpty: utils.StringMap{ - cgrID: true, - cgrID2: true, + "Extra3": map[string]map[string]utils.StringMap{ + utils.MetaEmpty: map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + cgrID2: true, + }, + }, + utils.NOT_AVAILABLE: map[string]utils.StringMap{ + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, }, - "Extra4": map[string]utils.StringMap{ - utils.NOT_AVAILABLE: utils.StringMap{ - cgrID: true, + "Extra4": map[string]map[string]utils.StringMap{ + utils.NOT_AVAILABLE: map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID: true, + }, + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, - "info2": utils.StringMap{ - cgrID2: true, + "info2": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, }, }, } if !reflect.DeepEqual(eIndexes, smg.aSessionsIndex) { t.Errorf("Expecting: %+v, received: %+v", eIndexes, smg.aSessionsIndex) } + eRIdxes = map[string][]*riFieldNameVal{ + cgrID: []*riFieldNameVal{ + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Tenant", fieldValue: "cgrates.org"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Account", fieldValue: "account1"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra3", fieldValue: utils.MetaEmpty}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra4", fieldValue: utils.NOT_AVAILABLE}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "OriginID", fieldValue: "12345"}, + }, + cgrID2: []*riFieldNameVal{ + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Tenant", fieldValue: "itsyscom.com"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Account", fieldValue: "account2"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra3", fieldValue: utils.MetaEmpty}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra4", fieldValue: "info2"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "OriginID", fieldValue: "12346"}, + }, + cgrID3: []*riFieldNameVal{ + &riFieldNameVal{runID: "secondRun", fieldName: "Tenant", fieldValue: "cgrates.org"}, + &riFieldNameVal{runID: "secondRun", fieldName: "Account", fieldValue: "account2"}, + &riFieldNameVal{runID: "secondRun", fieldName: "Extra3", fieldValue: utils.NOT_AVAILABLE}, + &riFieldNameVal{runID: "secondRun", fieldName: "Extra4", fieldValue: utils.NOT_AVAILABLE}, + &riFieldNameVal{runID: "secondRun", fieldName: "OriginID", fieldValue: "12347"}, + }, + } + if len(eRIdxes) != len(smg.aSessionsRIndex) || + len(eRIdxes[cgrID]) != len(smg.aSessionsRIndex[cgrID]) || + len(eRIdxes[cgrID2]) != len(smg.aSessionsRIndex[cgrID2]) { // cannot keep order here due to field names coming from map + t.Errorf("Expecting: %+v, received: %+v", eRIdxes, smg.aSessionsRIndex) + } // Unidex first session - smg.unindexASession(cgrID) - eIndexes = map[string]map[string]utils.StringMap{ - "OriginID": map[string]utils.StringMap{ - "12346": utils.StringMap{ - cgrID2: true, + smg.unindexSession(cgrID, false) + eIndexes = map[string]map[string]map[string]utils.StringMap{ + "OriginID": map[string]map[string]utils.StringMap{ + "12346": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + "12347": map[string]utils.StringMap{ + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, }, - "Tenant": map[string]utils.StringMap{ - "itsyscom.com": utils.StringMap{ - cgrID2: true, + "Tenant": map[string]map[string]utils.StringMap{ + "cgrates.org": map[string]utils.StringMap{ + "secondRun": utils.StringMap{ + cgrID3: true, + }, + }, + "itsyscom.com": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, }, }, - "Account": map[string]utils.StringMap{ - "account2": utils.StringMap{ - cgrID2: true, + "Account": map[string]map[string]utils.StringMap{ + "account2": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, }, - "Extra3": map[string]utils.StringMap{ - utils.MetaEmpty: utils.StringMap{ - cgrID2: true, + "Extra3": map[string]map[string]utils.StringMap{ + utils.MetaEmpty: map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + utils.NOT_AVAILABLE: map[string]utils.StringMap{ + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, }, - "Extra4": map[string]utils.StringMap{ - "info2": utils.StringMap{ - cgrID2: true, + "Extra4": map[string]map[string]utils.StringMap{ + "info2": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + utils.NOT_AVAILABLE: map[string]utils.StringMap{ + "secondRun": utils.StringMap{ + cgrID3: true, + }, }, }, } if !reflect.DeepEqual(eIndexes, smg.aSessionsIndex) { t.Errorf("Expecting: %+v, received: %+v", eIndexes, smg.aSessionsIndex) } + eRIdxes = map[string][]*riFieldNameVal{ + cgrID2: []*riFieldNameVal{ + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Tenant", fieldValue: "itsyscom.com"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Account", fieldValue: "account2"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra3", fieldValue: utils.MetaEmpty}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra4", fieldValue: "info2"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "OriginID", fieldValue: "12346"}, + }, + cgrID3: []*riFieldNameVal{ + &riFieldNameVal{runID: "secondRun", fieldName: "Tenant", fieldValue: "cgrates.org"}, + &riFieldNameVal{runID: "secondRun", fieldName: "Account", fieldValue: "account2"}, + &riFieldNameVal{runID: "secondRun", fieldName: "Extra3", fieldValue: utils.NOT_AVAILABLE}, + &riFieldNameVal{runID: "secondRun", fieldName: "Extra4", fieldValue: utils.NOT_AVAILABLE}, + &riFieldNameVal{runID: "secondRun", fieldName: "OriginID", fieldValue: "12347"}, + }, + } + if len(eRIdxes) != len(smg.aSessionsRIndex) || + len(eRIdxes[cgrID2]) != len(smg.aSessionsRIndex[cgrID2]) { // cannot keep order here due to field names coming from map + t.Errorf("Expecting: %+v, received: %+v", eRIdxes, smg.aSessionsRIndex) + } + smg.unindexSession(cgrID3, false) + eIndexes = map[string]map[string]map[string]utils.StringMap{ + "OriginID": map[string]map[string]utils.StringMap{ + "12346": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + }, + "Tenant": map[string]map[string]utils.StringMap{ + "itsyscom.com": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + }, + "Account": map[string]map[string]utils.StringMap{ + "account2": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + }, + "Extra3": map[string]map[string]utils.StringMap{ + utils.MetaEmpty: map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + }, + "Extra4": map[string]map[string]utils.StringMap{ + "info2": map[string]utils.StringMap{ + utils.META_DEFAULT: utils.StringMap{ + cgrID2: true, + }, + }, + }, + } + if !reflect.DeepEqual(eIndexes, smg.aSessionsIndex) { + t.Errorf("Expecting: %+v, received: %+v", eIndexes, smg.aSessionsIndex) + } + eRIdxes = map[string][]*riFieldNameVal{ + cgrID2: []*riFieldNameVal{ + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Tenant", fieldValue: "itsyscom.com"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Account", fieldValue: "account2"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra3", fieldValue: utils.MetaEmpty}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "Extra4", fieldValue: "info2"}, + &riFieldNameVal{runID: utils.META_DEFAULT, fieldName: "OriginID", fieldValue: "12346"}, + }, + } + if len(eRIdxes) != len(smg.aSessionsRIndex) || + len(eRIdxes[cgrID2]) != len(smg.aSessionsRIndex[cgrID2]) { // cannot keep order here due to field names coming from map + t.Errorf("Expecting: %+v, received: %+v", eRIdxes, smg.aSessionsRIndex) + } } func TestSMGActiveSessions(t *testing.T) { @@ -209,7 +414,7 @@ func TestSMGActiveSessions(t *testing.T) { "Extra2": 5, "Extra3": "", } - smg.recordASession(&SMGSession{CGRID: smGev1.GetCGRID(utils.META_DEFAULT), EventStart: smGev1}) + smg.recordASession(&SMGSession{CGRID: smGev1.GetCGRID(utils.META_DEFAULT), RunID: utils.META_DEFAULT, EventStart: smGev1}) smGev2 := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: "*voice", @@ -230,21 +435,21 @@ func TestSMGActiveSessions(t *testing.T) { "Extra1": "Value1", "Extra3": "extra3", } - smg.recordASession(&SMGSession{CGRID: smGev2.GetCGRID(utils.META_DEFAULT), EventStart: smGev2}) + smg.recordASession(&SMGSession{CGRID: smGev2.GetCGRID(utils.META_DEFAULT), RunID: utils.META_DEFAULT, EventStart: smGev2}) if aSessions, _, err := smg.ActiveSessions(nil, false); err != nil { t.Error(err) } else if len(aSessions) != 2 { - t.Errorf("Received sessions: %%+v", aSessions) + t.Errorf("Received sessions: %+v", aSessions) } if aSessions, _, err := smg.ActiveSessions(map[string]string{"Tenant": "itsyscom.com"}, false); err != nil { t.Error(err) } else if len(aSessions) != 1 { - t.Errorf("Received sessions: %%+v", aSessions) + t.Errorf("Received sessions: %+v", aSessions) } if aSessions, _, err := smg.ActiveSessions(map[string]string{utils.TOR: "*voice"}, false); err != nil { t.Error(err) } else if len(aSessions) != 2 { - t.Errorf("Received sessions: %%+v", aSessions) + t.Errorf("Received sessions: %+v", aSessions) } if aSessions, _, err := smg.ActiveSessions(map[string]string{"Extra3": utils.MetaEmpty}, false); err != nil { t.Error(err) diff --git a/utils/map.go b/utils/map.go index 6dd073a36..878a4dc54 100644 --- a/utils/map.go +++ b/utils/map.go @@ -164,6 +164,14 @@ func (sm StringMap) GetOne() string { return "" } +func (sm StringMap) Join(mps ...StringMap) { + for _, mp := range mps { + for k, v := range mp { + sm[k] = v + } + } +} + /* func NoDots(m map[string]struct{}) map[string]struct{} { return MapKeysReplace(m, ".", ".")