From b176511212ab4c1233fb250bd419e4dca0d247c4 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 7 Apr 2016 20:20:34 +0300 Subject: [PATCH 1/3] updated dependecies --- glide.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/glide.lock b/glide.lock index 540f03ecf..6f3370ee8 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 5c488630d1d32687b7a3c3b22c47ceaf7eb3cffb764799706728a6accbcd3ff5 -updated: 2016-04-04T18:52:04.065644968+03:00 +updated: 2016-04-07T20:19:19.216414077+03:00 imports: - name: github.com/cenkalti/hub version: b864404b5f990410d56858a1b0a6fac23a85443f @@ -12,7 +12,7 @@ imports: - name: github.com/cgrates/osipsdagram version: 3d6beed663452471dec3ca194137a30d379d9e8f - name: github.com/cgrates/rpcclient - version: 79661b1e514823a9ac93b2b9e97e037ee190ba47 + version: 9302178591f1b3b5460c36a4ddae395f0f5397c5 - name: github.com/DisposaBoy/JsonConfigReader version: 33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4 - name: github.com/fiorix/go-diameter @@ -26,17 +26,17 @@ imports: - diam/sm/smparser - diam/sm/smpeer - name: github.com/go-sql-driver/mysql - version: 0f2db9e6c9cff80a97ca5c2c5096242cc1554e16 + version: 1421caf44f6464fd2ee8de694c7508ee13f92964 - name: github.com/gorhill/cronexpr - version: a557574d6c024ed6e36acc8b610f5f211c91568a + version: f0984319b44273e83de132089ae42b1810f4933b - name: github.com/jinzhu/gorm - version: 2530dcbccd9c9ff7ce5a903e01bbf8601b726112 + version: 5174cc5c242a728b435ea2be8a2f7f998e15429b - name: github.com/jinzhu/inflection version: 3272df6c21d04180007eb3349844c89a3856bc25 - name: github.com/kr/pty version: f7ee69f31298ecbe5d2b349c711e2547a617d398 - name: github.com/lib/pq - version: 165a3529e799da61ab10faed1fabff3662d6193f + version: 3cd0097429be7d611bb644ef85b42bfb102ceea4 subpackages: - oid - name: github.com/mediocregopher/radix.v2 @@ -45,20 +45,20 @@ imports: - pool - redis - name: github.com/peterh/liner - version: ad1edfd30321d8f006ccf05f1e0524adeb943060 + version: 49ca65981c3cd7db64145977af1d186e9d317afa - name: github.com/ugorji/go - version: 187fa0f8af224437e08ecb3f208c4d1a94859a61 + version: a396ed22fc049df733440d90efe17475e3929ccb subpackages: - codec - name: golang.org/x/net - version: a4bbce9fcae005b22ae5443f6af064d80a6f5a55 + version: e45385e9b226f570b1f086bf287b25d3d4117776 subpackages: - websocket - context - name: gopkg.in/fsnotify.v1 version: 875cf421b32f8f1b31bd43776297876d01542279 - name: gopkg.in/mgo.v2 - version: d90005c5262a3463800497ea5a89aed5fe22c886 + version: b6e2fa371e64216a45e61072a96d4e3859f169da subpackages: - bson - internal/sasl From 155168406ccb0b6a92d0208c36da9c4da29dd985 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 8 Apr 2016 00:33:21 +0300 Subject: [PATCH 2/3] added session ttl to smgeneric --- config/config_defaults.go | 1 + config/config_json_test.go | 1 + config/libconfig_json.go | 1 + config/smconfig.go | 6 +++ data/conf/cgrates/cgrates.json | 1 + data/conf/samples/smg/cgrates.json | 1 + sessionmanager/data_it_test.go | 49 ++++++++++++++++++++++++ sessionmanager/smgeneric.go | 61 ++++++++++++++++++++++++------ 8 files changed, 109 insertions(+), 12 deletions(-) diff --git a/config/config_defaults.go b/config/config_defaults.go index a0caa38b5..9d1cc2d87 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -210,6 +210,7 @@ const CGRATES_CFG_JSON = ` "debit_interval": "0s", // interval to perform debits on. "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 }, diff --git a/config/config_json_test.go b/config/config_json_test.go index e80cb6aaa..b7bc3c0c8 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -338,6 +338,7 @@ func TestSmGenericJsonCfg(t *testing.T) { Debit_interval: utils.StringPointer("0s"), Min_call_duration: utils.StringPointer("0s"), Max_call_duration: utils.StringPointer("3h"), + Session_ttl: utils.StringPointer("0s"), } if cfg, err := dfCgrJsonCfg.SmGenericJsonCfg(); err != nil { t.Error(err) diff --git a/config/libconfig_json.go b/config/libconfig_json.go index e40dedcdd..2dca787a4 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -174,6 +174,7 @@ type SmGenericJsonCfg struct { Debit_interval *string Min_call_duration *string Max_call_duration *string + Session_ttl *string } // SM-FreeSWITCH config section diff --git a/config/smconfig.go b/config/smconfig.go index 50e503bce..e828c76a0 100644 --- a/config/smconfig.go +++ b/config/smconfig.go @@ -79,6 +79,7 @@ type SmGenericConfig struct { DebitInterval time.Duration MinCallDuration time.Duration MaxCallDuration time.Duration + SessionTTL time.Duration } func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error { @@ -113,6 +114,11 @@ func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error { return err } } + if jsnCfg.Session_ttl != nil { + if self.SessionTTL, err = utils.ParseDurationWithSecs(*jsnCfg.Session_ttl); err != nil { + return err + } + } return nil } diff --git a/data/conf/cgrates/cgrates.json b/data/conf/cgrates/cgrates.json index 4e7790c06..87cc9a860 100644 --- a/data/conf/cgrates/cgrates.json +++ b/data/conf/cgrates/cgrates.json @@ -187,6 +187,7 @@ // "debit_interval": "0s", // interval to perform debits on. // "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 //}, diff --git a/data/conf/samples/smg/cgrates.json b/data/conf/samples/smg/cgrates.json index 0cbfc28c2..d6aaf2a6e 100644 --- a/data/conf/samples/smg/cgrates.json +++ b/data/conf/samples/smg/cgrates.json @@ -27,6 +27,7 @@ "enabled": true, "rater": "internal", "cdrs": "internal", + "session_ttl": "10ms", }, } diff --git a/sessionmanager/data_it_test.go b/sessionmanager/data_it_test.go index 08b701df8..75e927719 100644 --- a/sessionmanager/data_it_test.go +++ b/sessionmanager/data_it_test.go @@ -384,3 +384,52 @@ func TestSMGDataDerivedChargingNoCredit(t *testing.T) { t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) } } + +func TestSMGDataTTLExpired(t *testing.T) { + if !*testIntegration { + return + } + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} + eAcntVal := 49999897600.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + smgEv := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1010", + utils.SUBJECT: "1010", + utils.DESTINATION: "222", + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:49", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "1048576", + } + var maxUsage float64 + if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 1.048576e+06 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 49998842880.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + time.Sleep(50 * time.Millisecond) + eAcntVal = 49999897600.000000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } +} diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index dd5e27fd5..1dcbe7508 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -35,24 +35,47 @@ var ErrPartiallyExecuted = errors.New("Partially executed") func NewSMGeneric(cgrCfg *config.CGRConfig, rater engine.Connector, cdrsrv engine.Connector, timezone string, extconns *SMGExternalConnections) *SMGeneric { gsm := &SMGeneric{cgrCfg: cgrCfg, rater: rater, cdrsrv: cdrsrv, extconns: extconns, timezone: timezone, - sessions: make(map[string][]*SMGSession), sessionsMux: new(sync.Mutex), guard: engine.Guardian} + sessions: make(map[string][]*SMGSession), sessionTerminators: make(map[string]*smgSessionTerminator), sessionsMux: new(sync.RWMutex), guard: engine.Guardian} return gsm } type SMGeneric struct { - cgrCfg *config.CGRConfig // Separate from smCfg since there can be multiple - rater engine.Connector - cdrsrv engine.Connector - timezone string - sessions map[string][]*SMGSession //Group sessions per sessionId, multiple runs based on derived charging - extconns *SMGExternalConnections // Reference towards external connections manager - sessionsMux *sync.Mutex // Locks sessions map - guard *engine.GuardianLock // Used to lock on uuid + cgrCfg *config.CGRConfig // Separate from smCfg since there can be multiple + rater engine.Connector + cdrsrv engine.Connector + timezone string + sessions map[string][]*SMGSession //Group sessions per sessionId, multiple runs based on derived charging + sessionTerminators map[string]*smgSessionTerminator // terminate and cleanup the session if timer expires + extconns *SMGExternalConnections // Reference towards external connections manager + sessionsMux *sync.RWMutex // Locks sessions map + guard *engine.GuardianLock // Used to lock on uuid +} +type smgSessionTerminator struct { + timer *time.Timer + endChan chan bool } func (self *SMGeneric) indexSession(uuid string, s *SMGSession) { self.sessionsMux.Lock() self.sessions[uuid] = append(self.sessions[uuid], s) + if self.cgrCfg.SmGenericConfig.SessionTTL > 0 { + if _, found := self.sessionTerminators[uuid]; !found { + timer := time.NewTimer(self.cgrCfg.SmGenericConfig.SessionTTL) + endChan := make(chan bool, 1) + go func() { + select { + case <-timer.C: + self.sessionEnd(uuid, 0) + case <-endChan: + timer.Stop() + } + }() + self.sessionTerminators[uuid] = &smgSessionTerminator{ + timer: timer, + endChan: endChan, + } + } + } self.sessionsMux.Unlock() } @@ -60,10 +83,14 @@ func (self *SMGeneric) indexSession(uuid string, s *SMGSession) { func (self *SMGeneric) unindexSession(uuid string) bool { self.sessionsMux.Lock() defer self.sessionsMux.Unlock() - if _, hasIt := self.sessions[uuid]; !hasIt { + if _, found := self.sessions[uuid]; !found { return false } delete(self.sessions, uuid) + if st, found := self.sessionTerminators[uuid]; found { + st.endChan <- true + delete(self.sessionTerminators, uuid) + } return true } @@ -88,11 +115,20 @@ func (self *SMGeneric) getSessionIDsForPrefix(prefix string) []string { // Returns sessions/derived for a specific uuid func (self *SMGeneric) getSession(uuid string) []*SMGSession { - self.sessionsMux.Lock() - defer self.sessionsMux.Unlock() + self.sessionsMux.RLock() + defer self.sessionsMux.RUnlock() return self.sessions[uuid] } +// Updates the timer for the session to a new ttl +func (self *SMGeneric) resetTerminatorTimer(uuid string) { + self.sessionsMux.RLock() + defer self.sessionsMux.RUnlock() + if st, found := self.sessionTerminators[uuid]; found { + st.timer.Reset(self.cgrCfg.SmGenericConfig.SessionTTL) + } +} + // Handle a new session, pass the connectionId so we can communicate on disconnect request func (self *SMGeneric) sessionStart(evStart SMGenericEvent, connId string) error { sessionId := evStart.GetUUID() @@ -221,6 +257,7 @@ func (self *SMGeneric) SessionStart(gev SMGenericEvent, clnt *rpc2.Client) (time // Execute debits for usage/maxUsage func (self *SMGeneric) SessionUpdate(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) { + self.resetTerminatorTimer(gev.GetUUID()) if initialID, err := gev.GetFieldAsString(utils.InitialOriginID); err == nil { err := self.sessionRelocate(gev.GetUUID(), initialID) if err == utils.ErrNotFound { // Session was already relocated, create a new session with this update From 7afc0de334f5e8210fcddda9c4787458e66ff9e7 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 8 Apr 2016 00:49:00 +0300 Subject: [PATCH 3/3] possible quick fix for #422 --- apier/v1/accounts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index c6c161331..add9e9b97 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -567,7 +567,7 @@ func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error { at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) a := &engine.Action{ - ActionType: engine.SET_BALANCE, + ActionType: engine.REMOVE_BALANCE, Balance: &engine.BalanceFilter{ Uuid: attr.BalanceUUID, ID: attr.BalanceID,