mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Add SessionTLLLastUsage as option for an extra debit in case of ttl mechanism
This commit is contained in:
committed by
Dan Christian Bogos
parent
cd86b8c3ca
commit
7b01fb3918
@@ -353,6 +353,7 @@ const CGRATES_CFG_JSON = `
|
||||
//"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_last_usage": "", // tweak LastUsage for session timing-out, not defined by default
|
||||
"session_indexes": [], // index sessions based on these fields for GetActiveSessions API
|
||||
"client_protocol": 1.0, // version of protocol to use when acting as JSON-PRC client <"0","1.0">
|
||||
"channel_sync_interval": "0", // sync channels to detect stale sessions (0 to disable)
|
||||
|
||||
@@ -192,30 +192,31 @@ type EventReaderJsonCfg struct {
|
||||
|
||||
// SM-Generic config section
|
||||
type SessionSJsonCfg struct {
|
||||
Enabled *bool
|
||||
Listen_bijson *string
|
||||
Chargers_conns *[]string
|
||||
Rals_conns *[]string
|
||||
Resources_conns *[]string
|
||||
Thresholds_conns *[]string
|
||||
Stats_conns *[]string
|
||||
Suppliers_conns *[]string
|
||||
Cdrs_conns *[]string
|
||||
Replication_conns *[]string
|
||||
Attributes_conns *[]string
|
||||
Debit_interval *string
|
||||
Store_session_costs *bool
|
||||
Min_call_duration *string
|
||||
Max_call_duration *string
|
||||
Session_ttl *string
|
||||
Session_ttl_max_delay *string
|
||||
Session_ttl_last_used *string
|
||||
Session_ttl_usage *string
|
||||
Session_indexes *[]string
|
||||
Client_protocol *float64
|
||||
Channel_sync_interval *string
|
||||
Terminate_attempts *int
|
||||
Alterable_fields *[]string
|
||||
Enabled *bool
|
||||
Listen_bijson *string
|
||||
Chargers_conns *[]string
|
||||
Rals_conns *[]string
|
||||
Resources_conns *[]string
|
||||
Thresholds_conns *[]string
|
||||
Stats_conns *[]string
|
||||
Suppliers_conns *[]string
|
||||
Cdrs_conns *[]string
|
||||
Replication_conns *[]string
|
||||
Attributes_conns *[]string
|
||||
Debit_interval *string
|
||||
Store_session_costs *bool
|
||||
Min_call_duration *string
|
||||
Max_call_duration *string
|
||||
Session_ttl *string
|
||||
Session_ttl_max_delay *string
|
||||
Session_ttl_last_used *string
|
||||
Session_ttl_usage *string
|
||||
Session_ttl_last_usage *string
|
||||
Session_indexes *[]string
|
||||
Client_protocol *float64
|
||||
Channel_sync_interval *string
|
||||
Terminate_attempts *int
|
||||
Alterable_fields *[]string
|
||||
}
|
||||
|
||||
// FreeSWITCHAgent config section
|
||||
|
||||
@@ -93,6 +93,7 @@ type SessionSCfg struct {
|
||||
SessionTTLMaxDelay *time.Duration
|
||||
SessionTTLLastUsed *time.Duration
|
||||
SessionTTLUsage *time.Duration
|
||||
SessionTTLLastUsage *time.Duration
|
||||
SessionIndexes utils.StringMap
|
||||
ClientProtocol float64
|
||||
ChannelSyncInterval time.Duration
|
||||
@@ -253,6 +254,13 @@ func (scfg *SessionSCfg) loadFromJsonCfg(jsnCfg *SessionSJsonCfg) (err error) {
|
||||
scfg.SessionTTLUsage = &sessionTTLUsage
|
||||
}
|
||||
}
|
||||
if jsnCfg.Session_ttl_last_usage != nil {
|
||||
if sessionTTLLastUsage, err := utils.ParseDurationWithNanosecs(*jsnCfg.Session_ttl_last_usage); err != nil {
|
||||
return err
|
||||
} else {
|
||||
scfg.SessionTTLLastUsage = &sessionTTLLastUsage
|
||||
}
|
||||
}
|
||||
if jsnCfg.Session_indexes != nil {
|
||||
scfg.SessionIndexes = utils.StringMapFromSlice(*jsnCfg.Session_indexes)
|
||||
}
|
||||
@@ -302,6 +310,10 @@ func (scfg *SessionSCfg) AsMapInterface() map[string]interface{} {
|
||||
if scfg.SessionTTLUsage != nil {
|
||||
sessionTTLUsage = scfg.SessionTTLUsage.String()
|
||||
}
|
||||
var sessionTTLLastUsage string = "0"
|
||||
if scfg.SessionTTLLastUsage != nil {
|
||||
sessionTTLLastUsage = scfg.SessionTTLLastUsage.String()
|
||||
}
|
||||
var channelSyncInterval string = "0"
|
||||
if scfg.ChannelSyncInterval != 0 {
|
||||
channelSyncInterval = scfg.ChannelSyncInterval.String()
|
||||
@@ -400,6 +412,7 @@ func (scfg *SessionSCfg) AsMapInterface() map[string]interface{} {
|
||||
utils.SessionTTLMaxDelayCfg: sessionTTLMaxDelay,
|
||||
utils.SessionTTLLastUsedCfg: sessionTTLLastUsed,
|
||||
utils.SessionTTLUsageCfg: sessionTTLUsage,
|
||||
utils.SessionTTLLastUsageCfg: sessionTTLLastUsage,
|
||||
utils.SessionIndexesCfg: scfg.SessionIndexes.Slice(),
|
||||
utils.ClientProtocolCfg: scfg.ClientProtocol,
|
||||
utils.ChannelSyncIntervalCfg: channelSyncInterval,
|
||||
|
||||
@@ -159,30 +159,31 @@ func TestSessionSCfgAsMapInterface(t *testing.T) {
|
||||
},
|
||||
}`
|
||||
eMap := map[string]interface{}{
|
||||
"enabled": false,
|
||||
"listen_bijson": "127.0.0.1:2014",
|
||||
"chargers_conns": []string{},
|
||||
"rals_conns": []string{},
|
||||
"cdrs_conns": []string{},
|
||||
"resources_conns": []string{},
|
||||
"thresholds_conns": []string{},
|
||||
"stats_conns": []string{},
|
||||
"suppliers_conns": []string{},
|
||||
"attributes_conns": []string{},
|
||||
"replication_conns": []string{},
|
||||
"debit_interval": "0",
|
||||
"store_session_costs": false,
|
||||
"min_call_duration": "0",
|
||||
"max_call_duration": "3h0m0s",
|
||||
"session_ttl": "0",
|
||||
"session_indexes": []string{},
|
||||
"client_protocol": 1.0,
|
||||
"channel_sync_interval": "0",
|
||||
"terminate_attempts": 5,
|
||||
"alterable_fields": []string{},
|
||||
"session_ttl_last_used": "0",
|
||||
"session_ttl_max_delay": "0",
|
||||
"session_ttl_usage": "0",
|
||||
"enabled": false,
|
||||
"listen_bijson": "127.0.0.1:2014",
|
||||
"chargers_conns": []string{},
|
||||
"rals_conns": []string{},
|
||||
"cdrs_conns": []string{},
|
||||
"resources_conns": []string{},
|
||||
"thresholds_conns": []string{},
|
||||
"stats_conns": []string{},
|
||||
"suppliers_conns": []string{},
|
||||
"attributes_conns": []string{},
|
||||
"replication_conns": []string{},
|
||||
"debit_interval": "0",
|
||||
"store_session_costs": false,
|
||||
"min_call_duration": "0",
|
||||
"max_call_duration": "3h0m0s",
|
||||
"session_ttl": "0",
|
||||
"session_indexes": []string{},
|
||||
"client_protocol": 1.0,
|
||||
"channel_sync_interval": "0",
|
||||
"terminate_attempts": 5,
|
||||
"alterable_fields": []string{},
|
||||
"session_ttl_last_used": "0",
|
||||
"session_ttl_max_delay": "0",
|
||||
"session_ttl_usage": "0",
|
||||
"session_ttl_last_usage": "0",
|
||||
}
|
||||
if jsnCfg, err := NewCgrJsonCfgFromBytes([]byte(cfgJSONStr)); err != nil {
|
||||
t.Error(err)
|
||||
@@ -227,30 +228,31 @@ func TestSessionSCfgAsMapInterface(t *testing.T) {
|
||||
},
|
||||
}`
|
||||
eMap = map[string]interface{}{
|
||||
"enabled": false,
|
||||
"listen_bijson": "127.0.0.1:2014",
|
||||
"chargers_conns": []string{"*internal"},
|
||||
"rals_conns": []string{"*internal"},
|
||||
"cdrs_conns": []string{"*internal"},
|
||||
"resources_conns": []string{"*internal"},
|
||||
"thresholds_conns": []string{"*internal"},
|
||||
"stats_conns": []string{"*internal"},
|
||||
"suppliers_conns": []string{"*internal"},
|
||||
"attributes_conns": []string{"*internal"},
|
||||
"replication_conns": []string{"*localhost"},
|
||||
"debit_interval": "0",
|
||||
"store_session_costs": false,
|
||||
"min_call_duration": "0",
|
||||
"max_call_duration": "3h0m0s",
|
||||
"session_ttl": "0",
|
||||
"session_indexes": []string{},
|
||||
"client_protocol": 1.0,
|
||||
"channel_sync_interval": "0",
|
||||
"terminate_attempts": 5,
|
||||
"alterable_fields": []string{},
|
||||
"session_ttl_last_used": "0",
|
||||
"session_ttl_max_delay": "0",
|
||||
"session_ttl_usage": "0",
|
||||
"enabled": false,
|
||||
"listen_bijson": "127.0.0.1:2014",
|
||||
"chargers_conns": []string{"*internal"},
|
||||
"rals_conns": []string{"*internal"},
|
||||
"cdrs_conns": []string{"*internal"},
|
||||
"resources_conns": []string{"*internal"},
|
||||
"thresholds_conns": []string{"*internal"},
|
||||
"stats_conns": []string{"*internal"},
|
||||
"suppliers_conns": []string{"*internal"},
|
||||
"attributes_conns": []string{"*internal"},
|
||||
"replication_conns": []string{"*localhost"},
|
||||
"debit_interval": "0",
|
||||
"store_session_costs": false,
|
||||
"min_call_duration": "0",
|
||||
"max_call_duration": "3h0m0s",
|
||||
"session_ttl": "0",
|
||||
"session_indexes": []string{},
|
||||
"client_protocol": 1.0,
|
||||
"channel_sync_interval": "0",
|
||||
"terminate_attempts": 5,
|
||||
"alterable_fields": []string{},
|
||||
"session_ttl_last_used": "0",
|
||||
"session_ttl_max_delay": "0",
|
||||
"session_ttl_usage": "0",
|
||||
"session_ttl_last_usage": "0",
|
||||
}
|
||||
if jsnCfg, err := NewCgrJsonCfgFromBytes([]byte(cfgJSONStr)); err != nil {
|
||||
t.Error(err)
|
||||
|
||||
@@ -39,6 +39,8 @@ cgrates (0.10.1) UNRELEASED; urgency=medium
|
||||
* [SupplierS] Allow multiple suppliers with the same ID
|
||||
* [Engine] Skip caching if limit is 0
|
||||
* [CacheS] Avoid long recaching
|
||||
* [SessionS] Use correctly SessionTTLUsage when calculate end usage in case of terminate session from ttl mechanism
|
||||
* [SessionS] Add SessionTLLLastUsage as option for an extra debit in case of ttl mechanism
|
||||
|
||||
-- DanB <danb@cgrates.org> Wed, 5 May 2020 15:22:59 +0200
|
||||
|
||||
|
||||
@@ -197,11 +197,12 @@ type riFieldNameVal struct {
|
||||
|
||||
// sTerminator holds the info needed to force-terminate sessions based on timer
|
||||
type sTerminator struct {
|
||||
timer *time.Timer
|
||||
endChan chan struct{}
|
||||
ttl time.Duration
|
||||
ttlLastUsed *time.Duration
|
||||
ttlUsage *time.Duration
|
||||
timer *time.Timer
|
||||
endChan chan struct{}
|
||||
ttl time.Duration
|
||||
ttlLastUsed *time.Duration
|
||||
ttlUsage *time.Duration
|
||||
ttlLastUsage *time.Duration
|
||||
}
|
||||
|
||||
// setSTerminator installs a new terminator for a session
|
||||
@@ -258,6 +259,15 @@ func (sS *SessionS) setSTerminator(s *Session) {
|
||||
utils.SessionS, utils.SessionTTLUsage, s.EventStart.String(), err.Error()))
|
||||
return
|
||||
}
|
||||
// TTLLastUsage
|
||||
ttlLastUsage, err := s.EventStart.GetDurationPtrOrDefault(
|
||||
utils.SessionTTLLastUsage, sS.cgrCfg.SessionSCfg().SessionTTLLastUsage)
|
||||
if err != nil {
|
||||
utils.Logger.Warning(
|
||||
fmt.Sprintf("<%s>, cannot extract <%s> from event: <%s>, err: <%s>",
|
||||
utils.SessionS, utils.SessionTTLLastUsage, s.EventStart.String(), err.Error()))
|
||||
return
|
||||
}
|
||||
// previously defined, reset
|
||||
if s.sTerminator != nil {
|
||||
s.sTerminator.ttl = ttl
|
||||
@@ -267,23 +277,31 @@ func (sS *SessionS) setSTerminator(s *Session) {
|
||||
if ttlUsage != nil {
|
||||
s.sTerminator.ttlUsage = ttlUsage
|
||||
}
|
||||
if ttlLastUsage != nil {
|
||||
s.sTerminator.ttlLastUsage = ttlLastUsage
|
||||
}
|
||||
s.sTerminator.timer.Reset(s.sTerminator.ttl)
|
||||
return
|
||||
}
|
||||
// new set
|
||||
s.sTerminator = &sTerminator{
|
||||
timer: time.NewTimer(ttl),
|
||||
endChan: make(chan struct{}),
|
||||
ttl: ttl,
|
||||
ttlLastUsed: ttlLastUsed,
|
||||
ttlUsage: ttlUsage,
|
||||
timer: time.NewTimer(ttl),
|
||||
endChan: make(chan struct{}),
|
||||
ttl: ttl,
|
||||
ttlLastUsed: ttlLastUsed,
|
||||
ttlUsage: ttlUsage,
|
||||
ttlLastUsage: ttlLastUsage,
|
||||
}
|
||||
|
||||
// schedule automatic termination
|
||||
go func() {
|
||||
select {
|
||||
case <-s.sTerminator.timer.C:
|
||||
sS.forceSTerminate(s, s.sTerminator.ttl,
|
||||
lastUsage := s.sTerminator.ttl
|
||||
if s.sTerminator.ttlLastUsage != nil {
|
||||
lastUsage = *s.sTerminator.ttlLastUsage
|
||||
}
|
||||
sS.forceSTerminate(s, lastUsage,
|
||||
s.sTerminator.ttlUsage, s.sTerminator.ttlLastUsed)
|
||||
case <-s.sTerminator.endChan:
|
||||
s.sTerminator.timer.Stop()
|
||||
|
||||
@@ -49,6 +49,7 @@ var (
|
||||
testSessionsDataTTLExpMultiUpdates,
|
||||
testSessionsDataMultipleDataNoUsage,
|
||||
testSessionsDataTTLUsageProtection,
|
||||
testSessionsDataTTLLastUsage,
|
||||
testSessionsDataTTKillEngine,
|
||||
}
|
||||
)
|
||||
@@ -1057,6 +1058,93 @@ func testSessionsDataTTLUsageProtection(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testSessionsDataTTLLastUsage(t *testing.T) {
|
||||
var acnt *engine.Account
|
||||
acntAttrs := &utils.AttrGetAccount{Tenant: "cgrates.org",
|
||||
Account: "testSessionsDataTTLLastUsage"}
|
||||
eAcntVal := 102400.0
|
||||
attrSetBalance := utils.AttrSetBalance{
|
||||
Tenant: acntAttrs.Tenant, Account: acntAttrs.Account,
|
||||
BalanceType: utils.DATA,
|
||||
Value: eAcntVal,
|
||||
Balance: map[string]interface{}{
|
||||
utils.ID: "testSessionsDataTTLLastUsage",
|
||||
},
|
||||
}
|
||||
var reply string
|
||||
if err := sDataRPC.Call(utils.APIerSv2SetBalance, attrSetBalance, &reply); err != nil {
|
||||
t.Error(err)
|
||||
} else if reply != utils.OK {
|
||||
t.Errorf("Received: %s", reply)
|
||||
}
|
||||
if err := sDataRPC.Call(utils.APIerSv2GetAccount, acntAttrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, totalVal)
|
||||
}
|
||||
|
||||
usage := int64(1024)
|
||||
initArgs := &V1InitSessionArgs{
|
||||
InitSession: true,
|
||||
CGREvent: &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "TestSessionsDataTTLLastUsage",
|
||||
Event: map[string]interface{}{
|
||||
utils.EVENT_NAME: "testSessionsDataTTLLastUsage",
|
||||
utils.ToR: utils.DATA,
|
||||
utils.OriginID: "testSessionsDataTTLLastUsage",
|
||||
utils.Account: acntAttrs.Account,
|
||||
utils.Subject: acntAttrs.Account,
|
||||
utils.Destination: utils.DATA,
|
||||
utils.Category: "data",
|
||||
utils.Tenant: "cgrates.org",
|
||||
utils.RequestType: utils.META_PREPAID,
|
||||
utils.SetupTime: time.Date(2016, time.January, 5, 18, 30, 59, 0, time.UTC),
|
||||
utils.AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC),
|
||||
utils.Usage: "1024",
|
||||
utils.SessionTTLLastUsage: "2048",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var initRpl *V1InitSessionReply
|
||||
if err := sDataRPC.Call(utils.SessionSv1InitiateSession,
|
||||
initArgs, &initRpl); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if initRpl.MaxUsage.Nanoseconds() != usage {
|
||||
t.Errorf("Expecting : %+v, received: %+v", usage, initRpl.MaxUsage.Nanoseconds())
|
||||
}
|
||||
|
||||
eAcntVal = 101376.000000
|
||||
if err := sDataRPC.Call(utils.APIerSv2GetAccount, acntAttrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal)
|
||||
}
|
||||
time.Sleep(70 * time.Millisecond)
|
||||
|
||||
eAcntVal = 99328.000000 // 101376.000000 ( units remains after init session ) - SessionTTLLastUsage ( 2048 )
|
||||
if err := sDataRPC.Call(utils.APIerSv2GetAccount, acntAttrs, &acnt); err != nil {
|
||||
t.Error(err)
|
||||
} else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal {
|
||||
t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal)
|
||||
}
|
||||
|
||||
// verify the cdr usage to be 3072 ( init usage ( 1024 ) + SessionTTLLastUsage ( 2048 ) )
|
||||
var cdrs []*engine.ExternalCDR
|
||||
req := utils.RPCCDRsFilter{Accounts: []string{acntAttrs.Account}}
|
||||
if err := sDataRPC.Call(utils.APIerSv2GetCDRs, &req, &cdrs); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if len(cdrs) != 1 {
|
||||
t.Error("Unexpected number of CDRs returned: ", len(cdrs))
|
||||
} else {
|
||||
if cdrs[0].Usage != "3072" {
|
||||
t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSessionsDataTTKillEngine(t *testing.T) {
|
||||
if err := engine.KillEngine(100); err != nil {
|
||||
t.Error(err)
|
||||
|
||||
@@ -333,6 +333,7 @@ const (
|
||||
SessionTTLMaxDelay = "SessionTTLMaxDelay"
|
||||
SessionTTLLastUsed = "SessionTTLLastUsed"
|
||||
SessionTTLUsage = "SessionTTLUsage"
|
||||
SessionTTLLastUsage = "SessionTTLLastUsage"
|
||||
HandlerSubstractUsage = "*substract_usage"
|
||||
XML = "xml"
|
||||
MetaGOB = "*gob"
|
||||
@@ -1705,6 +1706,7 @@ const (
|
||||
SessionTTLMaxDelayCfg = "session_ttl_max_delay"
|
||||
SessionTTLLastUsedCfg = "session_ttl_last_used"
|
||||
SessionTTLUsageCfg = "session_ttl_usage"
|
||||
SessionTTLLastUsageCfg = "session_ttl_last_usage"
|
||||
SessionIndexesCfg = "session_indexes"
|
||||
ClientProtocolCfg = "client_protocol"
|
||||
ChannelSyncIntervalCfg = "channel_sync_interval"
|
||||
|
||||
Reference in New Issue
Block a user