func (srvMngr *ServiceManager) StartScheduler(waitCache bool) error { srvMngr.RLock() schedRunning := srvMngr.sched != nil srvMngr.RUnlock() if schedRunning { return utils.NewCGRError(utils.ServiceManager, utils.CapitalizedMessage(utils.ServiceAlreadyRunning), utils.ServiceAlreadyRunning, "the scheduler is already running") } if waitCache { // Wait for cache to load data before starting cacheDone := <-srvMngr.cacheDoneChan srvMngr.cacheDoneChan <- cacheDone } utils.Logger.Info("<ServiceManager> Starting CGRateS Scheduler.") sched := scheduler.NewScheduler(srvMngr.ratingDB) srvMngr.Lock() srvMngr.sched = sched srvMngr.Unlock() go func() { sched.Loop() srvMngr.Lock() srvMngr.sched = nil // if we are after loop, the service is down srvMngr.Unlock() if srvMngr.cfg.SchedulerEnabled { srvMngr.engineShutdown <- true // shutdown engine since this service should be running } }() return nil }
func (m *Migrator) Migrate(taskID string) (err error) { switch taskID { default: // unsupported taskID err = utils.NewCGRError(utils.Migrator, utils.MandatoryIEMissingCaps, utils.UnsupportedMigrationTask, fmt.Sprintf("task <%s> is not a supported migration task", taskID)) case utils.MetaSetVersions: if err := m.storDB.SetVersions(engine.CurrentStorDBVersions()); err != nil { return utils.NewCGRError(utils.Migrator, utils.ServerErrorCaps, err.Error(), fmt.Sprintf("error: <%s> when updating CostDetails version into StorDB", err.Error())) } case utils.MetaCostDetails: err = m.migrateCostDetails() } return }
func GetCloned(key string) (cln interface{}, err error) { cacheMux.RLock() defer cacheMux.RUnlock() origVal, hasIt := cache.Get(key) if !hasIt { return nil, utils.NewCGRError(utils.Cache, utils.NotFoundCaps, utils.ItemNotFound, fmt.Sprintf("item with key <%s> was not found in <%s>", key)) } else if origVal == nil { return nil, nil } if _, canClone := origVal.(utils.Cloner); !canClone { return nil, utils.NewCGRError(utils.Cache, utils.NotCloneableCaps, utils.ItemNotCloneable, fmt.Sprintf("item with key <%s> is not following cloner interface", key)) } retVals := reflect.ValueOf(origVal).MethodByName("Clone").Call(nil) // Call Clone method on the object errIf := retVals[1].Interface() var notNil bool if err, notNil = errIf.(error); notNil { return } return retVals[0].Interface(), nil }
func (srvMngr *ServiceManager) StopScheduler() error { var sched *scheduler.Scheduler srvMngr.Lock() if srvMngr.sched != nil { sched = srvMngr.sched srvMngr.sched = nil // optimize the lock and release here } srvMngr.Unlock() if sched == nil { return utils.NewCGRError(utils.ServiceManager, utils.CapitalizedMessage(utils.ServiceAlreadyRunning), utils.ServiceAlreadyRunning, "the scheduler is not running") } srvMngr.cfg.SchedulerEnabled = false sched.Shutdown() return nil }
// RPC method, differs from storeSMCost through it's signature func (self *CdrServer) V1StoreSMCost(attr AttrCDRSStoreSMCost, reply *string) error { if attr.Cost.CGRID == "" { return utils.NewCGRError(utils.CDRSCtx, utils.MandatoryIEMissingCaps, fmt.Sprintf("%s: CGRID", utils.MandatoryInfoMissing), "SMCost: %+v with empty CGRID") } cacheKey := "V1StoreSMCost" + attr.Cost.CGRID + attr.Cost.RunID + attr.Cost.OriginID if item, err := self.getCache().Get(cacheKey); err == nil && item != nil { if item.Value != nil { *reply = item.Value.(string) } return item.Err } if err := self.storeSMCost(attr.Cost, attr.CheckDuplicate); err != nil { self.getCache().Cache(cacheKey, &cache.CacheItem{Err: err}) return utils.NewErrServerError(err) } self.getCache().Cache(cacheKey, &cache.CacheItem{Value: utils.OK}) *reply = utils.OK return nil }
func (m *Migrator) migrateCostDetails() (err error) { if m.storDB == nil { return utils.NewCGRError(utils.Migrator, utils.MandatoryIEMissingCaps, utils.NoStorDBConnection, "no connection to StorDB") } vrs, err := m.storDB.GetVersions(utils.COST_DETAILS) if err != nil { return utils.NewCGRError(utils.Migrator, utils.ServerErrorCaps, err.Error(), fmt.Sprintf("error: <%s> when querying storDB for versions", err.Error())) } else if len(vrs) == 0 { return utils.NewCGRError(utils.Migrator, utils.MandatoryIEMissingCaps, utils.UndefinedVersion, "version number is not defined for CostDetails model") } if vrs[utils.COST_DETAILS] != 1 { // Right now we only support migrating from version 1 return } var storSQL *sql.DB switch m.storDBType { case utils.MYSQL: storSQL = m.storDB.(*engine.MySQLStorage).Db case utils.POSTGRES: storSQL = m.storDB.(*engine.PostgresStorage).Db default: return utils.NewCGRError(utils.Migrator, utils.MandatoryIEMissingCaps, utils.UnsupportedDB, fmt.Sprintf("unsupported database type: <%s>", m.storDBType)) } rows, err := storSQL.Query("SELECT id, tor, direction, tenant, category, account, subject, destination, cost, cost_details FROM cdrs WHERE run_id!= '*raw' and cost_details IS NOT NULL AND deleted_at IS NULL") if err != nil { return utils.NewCGRError(utils.Migrator, utils.ServerErrorCaps, err.Error(), fmt.Sprintf("error: <%s> when querying storDB for cdrs", err.Error())) } defer rows.Close() for cnt := 0; rows.Next(); cnt++ { var id int64 var ccDirection, ccCategory, ccTenant, ccSubject, ccAccount, ccDestination, ccTor sql.NullString var ccCost sql.NullFloat64 var tts []byte if err := rows.Scan(&id, &ccTor, &ccDirection, &ccTenant, &ccCategory, &ccAccount, &ccSubject, &ccDestination, &ccCost, &tts); err != nil { return utils.NewCGRError(utils.Migrator, utils.ServerErrorCaps, err.Error(), fmt.Sprintf("error: <%s> when scanning at count: <%d>", err.Error(), cnt)) } var v1tmsps v1TimeSpans if err := json.Unmarshal(tts, &v1tmsps); err != nil { utils.Logger.Warning( fmt.Sprintf("<Migrator> Unmarshalling timespans at CDR with id: <%d>, error: <%s>", id, err.Error())) continue } v1CC := &v1CallCost{Direction: ccDirection.String, Category: ccCategory.String, Tenant: ccTenant.String, Subject: ccSubject.String, Account: ccAccount.String, Destination: ccDestination.String, TOR: ccTor.String, Cost: ccCost.Float64, Timespans: v1tmsps} cc, err := v1CC.AsCallCost() if err != nil { utils.Logger.Warning( fmt.Sprintf("<Migrator> Error: <%s> when converting into CallCost CDR with id: <%d>", err.Error(), id)) continue } if _, err := storSQL.Exec(fmt.Sprintf("UPDATE cdrs SET cost_details='%s' WHERE id=%d", cc.AsJSON(), id)); err != nil { utils.Logger.Warning( fmt.Sprintf("<Migrator> Error: <%s> updating CDR with id <%d> into StorDB", err.Error(), id)) continue } } // All done, update version wtih current one vrs = engine.Versions{utils.COST_DETAILS: engine.CurrentStorDBVersions()[utils.COST_DETAILS]} if err := m.storDB.SetVersions(vrs); err != nil { return utils.NewCGRError(utils.Migrator, utils.ServerErrorCaps, err.Error(), fmt.Sprintf("error: <%s> when updating CostDetails version into StorDB", err.Error())) } return }
// CacheDataFromDB loads data to cache // prfx represents the cache prefix, ids should be nil if all available data should be loaded // mustBeCached specifies that data needs to be cached in order to be retrieved from db func (rs *RedisStorage) CacheDataFromDB(prfx string, ids []string, mustBeCached bool) (err error) { if !utils.IsSliceMember([]string{utils.DESTINATION_PREFIX, utils.REVERSE_DESTINATION_PREFIX, utils.RATING_PLAN_PREFIX, utils.RATING_PROFILE_PREFIX, utils.ACTION_PREFIX, utils.ACTION_PLAN_PREFIX, utils.SHARED_GROUP_PREFIX, utils.DERIVEDCHARGERS_PREFIX, utils.LCR_PREFIX, utils.ALIASES_PREFIX, utils.REVERSE_ALIASES_PREFIX, utils.ResourceLimitsPrefix}, prfx) { return utils.NewCGRError(utils.REDIS, utils.MandatoryIEMissingCaps, utils.UnsupportedCachePrefix, fmt.Sprintf("prefix <%s> is not a supported cache prefix", prfx)) } if ids == nil { if ids, err = rs.GetKeysForPrefix(prfx); err != nil { return utils.NewCGRError(utils.REDIS, utils.ServerErrorCaps, err.Error(), fmt.Sprintf("redis error <%s> querying keys for prefix: <%s>", prfx)) } cache.RemPrefixKey(prfx, true, utils.NonTransactional) } for _, dataID := range ids { if mustBeCached { if _, hasIt := cache.Get(prfx + dataID); !hasIt { // only cache if previously there continue } } switch prfx { case utils.DESTINATION_PREFIX: _, err = rs.GetDestination(dataID, false, utils.NonTransactional) case utils.REVERSE_DESTINATION_PREFIX: _, err = rs.GetReverseDestination(dataID, false, utils.NonTransactional) case utils.RATING_PLAN_PREFIX: _, err = rs.GetRatingPlan(dataID, false, utils.NonTransactional) case utils.RATING_PROFILE_PREFIX: _, err = rs.GetRatingProfile(dataID, false, utils.NonTransactional) case utils.ACTION_PREFIX: _, err = rs.GetActions(dataID, false, utils.NonTransactional) case utils.ACTION_PLAN_PREFIX: _, err = rs.GetActionPlan(dataID, false, utils.NonTransactional) case utils.SHARED_GROUP_PREFIX: _, err = rs.GetSharedGroup(dataID, false, utils.NonTransactional) case utils.DERIVEDCHARGERS_PREFIX: _, err = rs.GetDerivedChargers(dataID, false, utils.NonTransactional) case utils.LCR_PREFIX: _, err = rs.GetLCR(dataID, false, utils.NonTransactional) case utils.ALIASES_PREFIX: _, err = rs.GetAlias(dataID, false, utils.NonTransactional) case utils.REVERSE_ALIASES_PREFIX: _, err = rs.GetReverseAlias(dataID, false, utils.NonTransactional) case utils.ResourceLimitsPrefix: _, err = rs.GetResourceLimit(dataID, false, utils.NonTransactional) } if err != nil { return utils.NewCGRError(utils.REDIS, utils.ServerErrorCaps, err.Error(), fmt.Sprintf("error <%s> querying redis for category: <%s>, dataID: <%s>", prfx, dataID)) } } return }