mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-14 12:49:54 +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
a23b501c05
commit
a7ea24a3ef
@@ -413,6 +413,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)
|
||||
|
||||
@@ -224,33 +224,34 @@ type EventExporterJsonCfg struct {
|
||||
|
||||
// SessionSJsonCfg 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
|
||||
Routes_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
|
||||
Min_dur_low_balance *string
|
||||
Scheduler_conns *[]string
|
||||
Stir *STIRJsonCfg
|
||||
Enabled *bool
|
||||
Listen_bijson *string
|
||||
Chargers_conns *[]string
|
||||
Rals_conns *[]string
|
||||
Resources_conns *[]string
|
||||
Thresholds_conns *[]string
|
||||
Stats_conns *[]string
|
||||
Routes_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
|
||||
Min_dur_low_balance *string
|
||||
Scheduler_conns *[]string
|
||||
Stir *STIRJsonCfg
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -255,6 +256,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)
|
||||
}
|
||||
@@ -320,6 +328,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()
|
||||
@@ -431,6 +443,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,
|
||||
|
||||
@@ -160,31 +160,32 @@ 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{},
|
||||
"routes_conns": []string{},
|
||||
"attributes_conns": []string{},
|
||||
"replication_conns": []string{},
|
||||
"debit_interval": "0",
|
||||
"store_session_costs": false,
|
||||
"min_call_duration": "0",
|
||||
"max_call_duration": "3h0m0s",
|
||||
"min_dur_low_balance": "0",
|
||||
"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{},
|
||||
"routes_conns": []string{},
|
||||
"attributes_conns": []string{},
|
||||
"replication_conns": []string{},
|
||||
"debit_interval": "0",
|
||||
"store_session_costs": false,
|
||||
"min_call_duration": "0",
|
||||
"max_call_duration": "3h0m0s",
|
||||
"min_dur_low_balance": "0",
|
||||
"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",
|
||||
"stir": map[string]interface{}{
|
||||
"allowed_attest": []string{"*any"},
|
||||
"payload_maxduration": "-1",
|
||||
@@ -237,31 +238,32 @@ 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"},
|
||||
"routes_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",
|
||||
"min_dur_low_balance": "0",
|
||||
"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"},
|
||||
"routes_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",
|
||||
"min_dur_low_balance": "0",
|
||||
"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",
|
||||
"stir": map[string]interface{}{
|
||||
"allowed_attest": []string{"*any"},
|
||||
"payload_maxduration": "-1",
|
||||
|
||||
@@ -69,6 +69,7 @@ cgrates (0.11.0~dev) UNRELEASED; urgency=medium
|
||||
* [FilterS] Updated Filter indexes
|
||||
* [General] Added *mo+extraDuration time support (e.g. *mo+1h will be time.Now() + 1 month + 1 hour)
|
||||
* [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, 19 Feb 2020 13:25:52 +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
|
||||
@@ -261,6 +262,21 @@ func (sS *SessionS) setSTerminator(s *Session, opts engine.MapEvent) {
|
||||
utils.SessionS, utils.SessionTTLLastUsed, s.CGRID, opts.String(), err.Error()))
|
||||
return
|
||||
}
|
||||
// LastUsage
|
||||
var ttlLastUsage *time.Duration
|
||||
if opts.HasField(utils.SessionTTLLastUsage) {
|
||||
ttlLastUsage, err = opts.GetDurationPtr(utils.SessionTTLLastUsage)
|
||||
} else if s.OptsStart.HasField(utils.SessionTTLLastUsage) {
|
||||
ttlLastUsage, err = s.OptsStart.GetDurationPtr(utils.SessionTTLLastUsage)
|
||||
} else {
|
||||
ttlLastUsage = sS.cgrCfg.SessionSCfg().SessionTTLLastUsage
|
||||
}
|
||||
if err != nil {
|
||||
utils.Logger.Warning(
|
||||
fmt.Sprintf("<%s>, cannot extract <%s> for session:<%s>, from it's options: <%s>, err: <%s>",
|
||||
utils.SessionS, utils.SessionTTLLastUsage, s.CGRID, opts.String(), err.Error()))
|
||||
return
|
||||
}
|
||||
// TTLUsage
|
||||
var ttlUsage *time.Duration
|
||||
if opts.HasField(utils.SessionTTLUsage) {
|
||||
@@ -285,23 +301,31 @@ func (sS *SessionS) setSTerminator(s *Session, opts engine.MapEvent) {
|
||||
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, s.sTerminator.ttlUsage,
|
||||
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,
|
||||
}
|
||||
)
|
||||
@@ -1068,6 +1069,95 @@ 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,
|
||||
Opts: map[string]interface{}{
|
||||
utils.SessionTTLLastUsage: "2048",
|
||||
},
|
||||
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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -1843,6 +1843,7 @@ const (
|
||||
SessionTTLCfg = "session_ttl"
|
||||
SessionTTLMaxDelayCfg = "session_ttl_max_delay"
|
||||
SessionTTLLastUsedCfg = "session_ttl_last_used"
|
||||
SessionTTLLastUsageCfg = "session_ttl_last_usage"
|
||||
SessionTTLUsageCfg = "session_ttl_usage"
|
||||
SessionIndexesCfg = "session_indexes"
|
||||
ClientProtocolCfg = "client_protocol"
|
||||
@@ -2140,12 +2141,12 @@ var (
|
||||
MetaZeroLeft = "*zeroleft"
|
||||
)
|
||||
|
||||
// CGROptionsSet the posible cgr options
|
||||
// CGROptionsSet the possible cgr options
|
||||
var CGROptionsSet = NewStringSet([]string{STIRATest, STIRPayloadMaxDuration,
|
||||
STIRIdentity, STIROriginatorTn, STIROriginatorURI, STIRDestinationTn,
|
||||
STIRDestinationURI, STIRPublicKeyPath, STIRPrivateKeyPath,
|
||||
DebitInterval, Context, SessionTTL, SessionTTLMaxDelay,
|
||||
SessionTTLLastUsed, SessionTTLUsage, APIKey, RouteID})
|
||||
SessionTTLLastUsed, SessionTTLLastUsage, SessionTTLUsage, APIKey, RouteID})
|
||||
|
||||
// SessionS ProccessEvent posible options
|
||||
const (
|
||||
@@ -2164,10 +2165,11 @@ const (
|
||||
Context = "Context"
|
||||
|
||||
// SessionS terminator
|
||||
SessionTTL = "SessionTTL"
|
||||
SessionTTLMaxDelay = "SessionTTLMaxDelay"
|
||||
SessionTTLLastUsed = "SessionTTLLastUsed"
|
||||
SessionTTLUsage = "SessionTTLUsage"
|
||||
SessionTTL = "SessionTTL"
|
||||
SessionTTLMaxDelay = "SessionTTLMaxDelay"
|
||||
SessionTTLLastUsed = "SessionTTLLastUsed"
|
||||
SessionTTLLastUsage = "SessionTTLLastUsage"
|
||||
SessionTTLUsage = "SessionTTLUsage"
|
||||
|
||||
// DispatcherS
|
||||
APIKey = "APIKey"
|
||||
|
||||
Reference in New Issue
Block a user