Add active sessions backup functionalitiy

This commit is contained in:
arberkatellari
2024-06-11 17:01:50 +02:00
committed by Dan Christian Bogos
parent 7e3b1b7052
commit f356695f6f
37 changed files with 1748 additions and 28 deletions

View File

@@ -482,3 +482,15 @@ func (dbM *DataDBMock) SetVersions(vrs Versions, overwrite bool) (err error) {
func (dbM *DataDBMock) RemoveRatingProfileDrv(string) error {
return utils.ErrNotImplemented
}
func (dbM *DataDBMock) SetBackupSessionsDrv(storedSessions []*StoredSession, nodeID string, tnt string) error {
return utils.ErrNotImplemented
}
func (dbM *DataDBMock) GetSessionsBackupDrv(nodeID string, tnt string) ([]*StoredSession, error) {
return nil, utils.ErrNotImplemented
}
func (dbM *DataDBMock) RemoveSessionsBackupDrv(nodeID, tnt, cgrid string) error {
return utils.ErrNotImplemented
}

View File

@@ -3263,3 +3263,70 @@ func (dm *DataManager) checkFilters(tenant string, ids []string) (err error) {
}
return
}
// GetSessionsBackup gets sessions from dataDB backup
func (dm *DataManager) GetSessionsBackup(nodeID, tenant string) ([]*StoredSession, error) {
if dm == nil {
return nil, utils.ErrNoDatabaseConn
}
return dm.dataDB.GetSessionsBackupDrv(nodeID, tenant)
}
type SetBackupSessionsArgs struct {
StoredSessions []*StoredSession // all active sessions ready for backup
NodeID string // used as part of filter of DataDB query
Tenant string // used as part of filter of DataDB query
}
// SetBackupSessions stores the active sessions in dataDB
func (dm *DataManager) SetBackupSessions(nodeID, tenant string,
storedSessions []*StoredSession) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if err = dm.dataDB.SetBackupSessionsDrv(storedSessions, nodeID, tenant); err != nil {
return
}
if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaSessionsBackup]; itm.Replicate {
err = replicate(dm.connMgr, config.CgrConfig().DataDbCfg().RplConns,
config.CgrConfig().DataDbCfg().RplFiltered,
utils.SessionsBackupPrefix, utils.ConcatenatedKey(tenant, nodeID),
utils.ReplicatorSv1SetBackupSessions,
&SetBackupSessionsArgs{
StoredSessions: storedSessions,
NodeID: nodeID,
Tenant: tenant,
})
}
return
}
type RemoveSessionBackupArgs struct {
Tenant string // used as part of filter of DataDB query
NodeID string // used as part of filter of DataDB query
CGRID string // used as part of filter of DataDB query
}
// RemoveSessionsBackup remove one or all sessions from dataDB backup
func (dm *DataManager) RemoveSessionsBackup(nodeID, tenant, cgrid string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if err = dm.dataDB.RemoveSessionsBackupDrv(nodeID, tenant, cgrid); err != nil {
return
}
if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaSessionsBackup]; itm.Replicate {
err = replicate(dm.connMgr, config.CgrConfig().DataDbCfg().RplConns,
config.CgrConfig().DataDbCfg().RplFiltered,
utils.SessionsBackupPrefix, utils.ConcatenatedKey(tenant, nodeID),
utils.ReplicatorSv1RemoveSessionBackup,
&RemoveSessionBackupArgs{
CGRID: cgrid,
NodeID: nodeID,
Tenant: tenant,
})
}
return
}

View File

@@ -128,6 +128,9 @@ type DataDB interface {
GetDispatcherHostDrv(string, string) (*DispatcherHost, error)
SetDispatcherHostDrv(*DispatcherHost) error
RemoveDispatcherHostDrv(string, string) error
SetBackupSessionsDrv(sessions []*StoredSession, nodeID string, tenant string) error
GetSessionsBackupDrv(nodeID string, tenant string) ([]*StoredSession, error)
RemoveSessionsBackupDrv(nodeID, tenant, cgrid string) error
}
type StorDB interface {

View File

@@ -854,3 +854,38 @@ func (iDB *InternalDB) RemoveIndexesDrv(idxItmType, tntCtx, idxKey string) (err
iDB.db.Remove(idxItmType, utils.ConcatenatedKey(tntCtx, idxKey), true, utils.NonTransactional)
return
}
// Will backup active sessions in DataDB
func (iDB *InternalDB) SetBackupSessionsDrv(storedSessions []*StoredSession, nodeID string,
tnt string) error {
for _, sess := range storedSessions {
iDB.db.Set(utils.CacheSessionsBackup, sess.CGRID, sess,
[]string{utils.ConcatenatedKey(tnt, nodeID)}, true, utils.NonTransactional)
}
return nil
}
// Will restore sessions that were active from dataDB backup
func (iDB *InternalDB) GetSessionsBackupDrv(nodeID, tnt string) ([]*StoredSession, error) {
var storedSessions []*StoredSession
for _, sessIface := range iDB.db.GetGroupItems(utils.CacheSessionsBackup, utils.ConcatenatedKey(tnt,
nodeID)) {
sess := sessIface.(*StoredSession)
storedSessions = append(storedSessions, sess)
}
if len(storedSessions) == 0 {
return nil, utils.ErrNoBackupFound
}
return storedSessions, nil
}
// Will remove one or all sessions from dataDB backup
func (iDB *InternalDB) RemoveSessionsBackupDrv(nodeID, tnt, cgrid string) error {
if cgrid == utils.EmptyString {
iDB.db.RemoveGroup(utils.CacheSessionsBackup, utils.ConcatenatedKey(tnt,
nodeID), true, utils.NonTransactional)
return nil
}
iDB.db.Remove(utils.CacheSessionsBackup, cgrid, true, utils.NonTransactional)
return nil
}

View File

@@ -75,6 +75,7 @@ const (
ColDpp = "dispatcher_profiles"
ColDph = "dispatcher_hosts"
ColLID = "load_ids"
ColBkup = "sessions_backup"
)
var (
@@ -1956,3 +1957,18 @@ func (ms *MongoStorage) RemoveIndexesDrv(idxItmType, tntCtx, idxKey string) erro
return err
})
}
// Will backup active sessions in DataDB
func (ms *MongoStorage) SetBackupSessionsDrv(storedSessions []*StoredSession, nodeID, tnt string) error {
return utils.ErrNotImplemented
}
// Will restore sessions that were active from dataDB backup
func (ms *MongoStorage) GetSessionsBackupDrv(nodeID, tnt string) ([]*StoredSession, error) {
return nil, utils.ErrNotImplemented
}
// Will remove one or all sessions from dataDB Backup
func (ms *MongoStorage) RemoveSessionsBackupDrv(nodeID, tnt, cgrid string) error {
return utils.ErrNotImplemented
}

View File

@@ -66,6 +66,7 @@ const (
redis_HGET = "HGET"
redis_RENAME = "RENAME"
redis_HMSET = "HMSET"
redis_HSET = "HSET"
redisLoadError = "Redis is loading the dataset in memory"
RedisLimit = 524287 // https://github.com/StackExchange/StackExchange.Redis/issues/201#issuecomment-98639005
@@ -1246,3 +1247,60 @@ func (rs *RedisStorage) RemoveIndexesDrv(idxItmType, tntCtx, idxKey string) (err
}
return rs.Cmd(nil, redis_HDEL, utils.CacheInstanceToPrefix[idxItmType]+tntCtx, idxKey)
}
// Converts time.Time values inside EventStart and SRuns Events, to string type values. Used before marshaling StoredSessions with msgpack
func StoredSessionEvTimeAsStr(sess *StoredSession) {
utils.MapIfaceTimeAsString(sess.EventStart)
for i := range sess.SRuns {
utils.MapIfaceTimeAsString(sess.SRuns[i].Event)
}
}
// Will backup active sessions in DataDB
func (rs *RedisStorage) SetBackupSessionsDrv(storedSessions []*StoredSession, nodeID string,
tnt string) (err error) {
mp := make(map[string]string)
for _, sess := range storedSessions {
StoredSessionEvTimeAsStr(sess)
var sessByte []byte
if sessByte, err = rs.ms.Marshal(sess); err != nil {
return
}
mp[sess.CGRID] = string(sessByte)
if len(mp) == RedisLimit {
if err = rs.FlatCmd(nil, redis_HMSET, utils.SessionsBackupPrefix+utils.ConcatenatedKey(tnt,
nodeID), mp); err != nil {
return
}
mp = make(map[string]string)
}
}
return rs.FlatCmd(nil, redis_HMSET, utils.SessionsBackupPrefix+utils.ConcatenatedKey(tnt, nodeID), mp)
}
// Will restore sessions that were active from dataDB backup
func (rs *RedisStorage) GetSessionsBackupDrv(nodeID, tnt string) (r []*StoredSession, err error) {
mp := make(map[string]string)
if err = rs.Cmd(&mp, redis_HGETALL, utils.SessionsBackupPrefix+utils.ConcatenatedKey(tnt,
nodeID)); err != nil {
return
} else if len(mp) == 0 {
return nil, utils.ErrNoBackupFound
}
for _, v := range mp {
var ss *StoredSession
if err = rs.ms.Unmarshal([]byte(v), &ss); err != nil {
return
}
r = append(r, ss)
}
return
}
// Will remove one or all sessions from dataDB backup
func (rs *RedisStorage) RemoveSessionsBackupDrv(nodeID, tnt, cgrid string) error {
if cgrid == utils.EmptyString {
return rs.Cmd(nil, redis_DEL, utils.SessionsBackupPrefix+utils.ConcatenatedKey(tnt, nodeID))
}
return rs.Cmd(nil, redis_HDEL, utils.SessionsBackupPrefix+utils.ConcatenatedKey(tnt, nodeID), cgrid)
}

49
engine/stored_session.go Normal file
View File

@@ -0,0 +1,49 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package engine
import (
"time"
)
// used to evade import cycle of the real sessions.SRun struct
type StoredSRun struct {
Event MapEvent // Event received from ChargerS
CD *CallDescriptor // initial CD used for debits, updated on each debit
EventCost *EventCost
ExtraDuration time.Duration // keeps the current duration debited on top of what has been asked
LastUsage time.Duration // last requested Duration
TotalUsage time.Duration // sum of lastUsage
NextAutoDebit *time.Time
}
// Holds a Session for storing in DataDB
type StoredSession struct {
CGRID string
Tenant string
ResourceID string
ClientConnID string // connection ID towards the client so we can recover from passive
EventStart MapEvent // Event which started the session
DebitInterval time.Duration // execute debits for *prepaid runs
Chargeable bool // used in case of pausing debit
SRuns []*StoredSRun // forked based on ChargerS
OptsStart MapEvent
UpdatedAt time.Time // time when session was changed
}