diff --git a/config/config_defaults.go b/config/config_defaults.go index 55d9faf70..acafdc063 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -272,8 +272,7 @@ const CGRATES_CFG_JSON = ` "min_call_duration": "0s", // only authorize calls with allowed duration higher than this "max_call_duration": "3h", // maximum call duration a prepaid call can last "session_ttl": "0s", // time after a session with no updates is terminated, not defined by default - //"min_session_ttl": "", // activates session_ttl randomization as minimum possible session_ttl - //"max_session_ttl": "", // used in case of session_ttl randomization as maximum possible session_ttl, unlimited if not defined + //"session_ttl_max_delay": "", // activates session_ttl randomization and limits the maximum possible delay //"session_ttl_last_used": "", // tweak LastUsed for sessions timing-out, not defined by default //"session_ttl_usage": "", // tweak Usage for sessions timing-out, not defined by default "session_indexes": [], // index sessions based on these fields for GetActiveSessions API diff --git a/config/libconfig_json.go b/config/libconfig_json.go index f6eb8017f..e2d88d541 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -196,8 +196,7 @@ type SmGenericJsonCfg struct { Min_call_duration *string Max_call_duration *string Session_ttl *string - Min_session_ttl *string - Max_session_ttl *string + Session_ttl_max_delay *string Session_ttl_last_used *string Session_ttl_usage *string Session_indexes *[]string diff --git a/config/smconfig.go b/config/smconfig.go index 19c13db63..e29a0e3eb 100644 --- a/config/smconfig.go +++ b/config/smconfig.go @@ -97,8 +97,7 @@ type SmGenericConfig struct { MinCallDuration time.Duration MaxCallDuration time.Duration SessionTTL time.Duration - MinSessionTTL *time.Duration - MaxSessionTTL *time.Duration + SessionTTLMaxDelay *time.Duration SessionTTLLastUsed *time.Duration SessionTTLUsage *time.Duration SessionIndexes utils.StringMap @@ -156,18 +155,11 @@ func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error { return err } } - if jsnCfg.Min_session_ttl != nil { - if minSesTTL, err := utils.ParseDurationWithSecs(*jsnCfg.Min_session_ttl); err != nil { + if jsnCfg.Session_ttl_max_delay != nil { + if maxTTLDelay, err := utils.ParseDurationWithSecs(*jsnCfg.Session_ttl_max_delay); err != nil { return err } else { - self.MinSessionTTL = &minSesTTL - } - } - if jsnCfg.Max_session_ttl != nil { - if maxSesTTL, err := utils.ParseDurationWithSecs(*jsnCfg.Max_session_ttl); err != nil { - return err - } else { - self.MaxSessionTTL = &maxSesTTL + self.SessionTTLMaxDelay = &maxTTLDelay } } if jsnCfg.Session_ttl_last_used != nil { diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index a429aed32..c339f33b5 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -20,6 +20,7 @@ package sessionmanager import ( "encoding/json" "fmt" + "math/rand" "strconv" "time" @@ -185,17 +186,47 @@ func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) } // GetSessionTTL retrieves SessionTTL setting out of SMGenericEvent -func (self SMGenericEvent) GetSessionTTL() time.Duration { +func (self SMGenericEvent) GetSessionTTL(sesTTL time.Duration, cfgSessionTTLMaxDelay *time.Duration) time.Duration { valIf, hasVal := self[utils.SessionTTL] - if !hasVal { - return time.Duration(0) + if hasVal { + ttlStr, converted := utils.ConvertIfaceToString(valIf) + if !converted { + utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot convert SessionTTL, disabling functionality for event: <%s>", + self.GetCGRID(utils.META_DEFAULT))) + return time.Duration(0) + } + var err error + if sesTTL, err = utils.ParseDurationWithSecs(ttlStr); err != nil { + utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot parse SessionTTL, disabling functionality for event: <%s>", + self.GetCGRID(utils.META_DEFAULT))) + return time.Duration(0) + } } - ttlStr, converted := utils.ConvertIfaceToString(valIf) - if !converted { - return time.Duration(0) + // Variable sessionTTL + var sessionTTLMaxDelay int64 + if cfgSessionTTLMaxDelay != nil { + sessionTTLMaxDelay = cfgSessionTTLMaxDelay.Nanoseconds() / 1000000 // Milliseconds precision } - ttl, _ := utils.ParseDurationWithSecs(ttlStr) - return ttl + if sesTTLMaxDelayIf, hasVal := self[utils.SessionTTLMaxDelay]; hasVal { + maxTTLDelaxStr, converted := utils.ConvertIfaceToString(sesTTLMaxDelayIf) + if !converted { + utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot convert SessionTTLMaxDelay, disabling functionality for event: <%s>", + self.GetCGRID(utils.META_DEFAULT))) + return time.Duration(0) + } + if maxTTLDelay, err := utils.ParseDurationWithSecs(maxTTLDelaxStr); err != nil { + utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot parse SessionTTLMaxDelay, disabling functionality for event: <%s>", + self.GetCGRID(utils.META_DEFAULT))) + return time.Duration(0) + } else { + sessionTTLMaxDelay = maxTTLDelay.Nanoseconds() / 1000000 + } + } + if sessionTTLMaxDelay != 0 { + rand.Seed(time.Now().Unix()) + sesTTL += time.Duration(rand.Int63n(sessionTTLMaxDelay) * 1000000) + } + return sesTTL } // GetSessionTTLLastUsed retrieves SessionTTLLastUsed setting out of SMGenericEvent diff --git a/sessionmanager/smg_event_test.go b/sessionmanager/smg_event_test.go index 226944b69..2b2143d0b 100644 --- a/sessionmanager/smg_event_test.go +++ b/sessionmanager/smg_event_test.go @@ -18,6 +18,7 @@ along with this program. If not, see package sessionmanager import ( + "fmt" "reflect" "testing" "time" @@ -132,6 +133,26 @@ func TestSMGenericEventParseFields(t *testing.T) { } } +func TestSMGenericEventGetSessionTTL(t *testing.T) { + smGev := SMGenericEvent{} + smGev[utils.EVENT_NAME] = "TEST_SESSION_TTL" + cfgSesTTL := time.Duration(5 * time.Second) + if sTTL := smGev.GetSessionTTL(time.Duration(5*time.Second), nil); sTTL != cfgSesTTL { + t.Errorf("Expecting: %v, received: %v", cfgSesTTL, sTTL) + } + smGev[utils.SessionTTL] = "6s" + eSesTTL := time.Duration(6 * time.Second) + if sTTL := smGev.GetSessionTTL(time.Duration(5*time.Second), nil); sTTL != eSesTTL { + t.Errorf("Expecting: %v, received: %v", eSesTTL, sTTL) + } + sesTTLMaxDelay := time.Duration(10 * time.Second) + if sTTL := smGev.GetSessionTTL(time.Duration(5*time.Second), &sesTTLMaxDelay); sTTL == eSesTTL || sTTL > eSesTTL+sesTTLMaxDelay { + t.Errorf("Received: %v", sTTL) + } else { + fmt.Println(sTTL) + } +} + func TestSMGenericEventAsStoredCdr(t *testing.T) { smGev := SMGenericEvent{} smGev[utils.EVENT_NAME] = "TEST_EVENT" diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 0bc255a3e..733a93b69 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -35,6 +35,7 @@ import ( const ( MaxTimespansInCost = 50 + MaxSessionTTL = 10000 // maximum session TTL in miliseconds ) var ( @@ -118,10 +119,8 @@ type smgSessionTerminator struct { // setSessionTerminator installs a new terminator for a session func (smg *SMGeneric) setSessionTerminator(s *SMGSession) { - ttl := smg.cgrCfg.SmGenericConfig.SessionTTL - if ttlEv := s.EventStart.GetSessionTTL(); ttlEv != 0 { - ttl = ttlEv - } + ttl := s.EventStart.GetSessionTTL(smg.cgrCfg.SmGenericConfig.SessionTTL, + smg.cgrCfg.SmGenericConfig.SessionTTLMaxDelay) if ttl == 0 { return } @@ -730,7 +729,9 @@ func (smg *SMGeneric) UpdateSession(gev SMGenericEvent, clnt rpcclient.RpcClient } smg.replicateSessionsWithID(initialCGRID, false, smg.smgReplConns) } - smg.resetTerminatorTimer(cgrID, gev.GetSessionTTL(), gev.GetSessionTTLLastUsed(), gev.GetSessionTTLUsage()) + smg.resetTerminatorTimer(cgrID, + gev.GetSessionTTL(smg.cgrCfg.SmGenericConfig.SessionTTL, smg.cgrCfg.SmGenericConfig.SessionTTLMaxDelay), + gev.GetSessionTTLLastUsed(), gev.GetSessionTTLUsage()) var lastUsed *time.Duration var evLastUsed time.Duration if evLastUsed, err = gev.GetLastUsed(utils.META_DEFAULT); err == nil { diff --git a/utils/consts.go b/utils/consts.go index fc3d3587f..37fa2bc19 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -309,6 +309,7 @@ const ( InstanceID = "InstanceID" ActiveGoroutines = "ActiveGoroutines" SessionTTL = "SessionTTL" + SessionTTLMaxDelay = "SessionTTLMaxDelay" SessionTTLLastUsed = "SessionTTLLastUsed" SessionTTLUsage = "SessionTTLUsage" HandlerSubstractUsage = "*substract_usage"