mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-23 08:08:45 +05:00
Add active sessions backup functionalitiy
This commit is contained in:
committed by
Dan Christian Bogos
parent
7e3b1b7052
commit
f356695f6f
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
49
engine/stored_session.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user