APIs - locking balance and action triggers when operating on them, adding reloadScheduler attribute in some methods

This commit is contained in:
DanB
2014-01-09 10:44:38 +01:00
parent 07dd19a9d5
commit 79497e8970
3 changed files with 59 additions and 38 deletions

View File

@@ -71,6 +71,7 @@ type AttrRemActionTiming struct {
Tenant string // Tenant he account belongs to
Account string // Account name
Direction string // Traffic direction
ReloadScheduler bool // If set it will reload the scheduler after adding
}
// Removes an ActionTimings or parts of it depending on filters being set
@@ -83,18 +84,26 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e
return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
}
}
// ToDo: lock here actionTimings
ats, err := self.AccountDb.GetActionTimings(attrs.ActionTimingsId)
_, err := engine.AccLock.Guard(engine.ACTION_TIMING_PREFIX+attrs.ActionTimingId, func() (float64, error) { // ToDo: Expand the scheduler to consider the locks also
ats, err := self.AccountDb.GetActionTimings(attrs.ActionTimingsId)
if err != nil {
return 0, err
} else if len(ats) == 0 {
return 0, errors.New(utils.ERR_NOT_FOUND)
}
ats = engine.RemActionTiming(ats, attrs.ActionTimingId, utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction))
if err := self.AccountDb.SetActionTimings(attrs.ActionTimingsId, ats); err != nil {
return 0, err
}
return 0, nil
})
if err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
} else if len(ats) == 0 {
return errors.New(utils.ERR_NOT_FOUND)
}
ats = engine.RemActionTiming(ats, attrs.ActionTimingId, utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction))
if err := self.AccountDb.SetActionTimings(attrs.ActionTimingsId, ats); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
if attrs.ReloadScheduler && self.Sched != nil {
self.Sched.LoadActionTimings(self.AccountDb)
self.Sched.Restart()
}
// ToDo: Unlock here actionTimings
*reply = OK
return nil
}
@@ -120,27 +129,34 @@ type AttrRemAcntActionTriggers struct {
}
// Returns a list of ActionTriggers on an account
func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTrigger, reply *string) error {
func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account", "Direction"}); len(missing) != 0 {
return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
}
ub, err := self.AccountDb.GetUserBalance(utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction))
balanceId := utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)
_, err := engine.AccLock.Guard(balanceId, func() (float64, error) {
ub, err := self.AccountDb.GetUserBalance(balanceId)
if err != nil {
return 0, err
}
for idx, actr := range ub.ActionTriggers {
if len(attrs.ActionTriggerId) != 0 && actr.Id != attrs.ActionTriggerId { // Empty actionTriggerId will match always
continue
}
if len(ub.ActionTriggers) != 1 { // Remove by index
ub.ActionTriggers[idx], ub.ActionTriggers = ub.ActionTriggers[len(ub.ActionTriggers)-1], ub.ActionTriggers[:len(ub.ActionTriggers)-1]
} else { // For last item, simply reinit the slice
ub.ActionTriggers = make(engine.ActionTriggerPriotityList, 0)
}
}
if err := self.AccountDb.SetUserBalance(ub); err != nil {
return 0, err
}
return 0, nil
})
if err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
}
for idx, actr := range ub.ActionTriggers {
if len(attrs.ActionTriggerId) != 0 && actr.Id != attrs.ActionTriggerId { // Empty actionTriggerId will match always
continue
}
if len(ub.ActionTriggers) != 1 { // Remove by index
ub.ActionTriggers[idx], ub.ActionTriggers = ub.ActionTriggers[len(ub.ActionTriggers)-1], ub.ActionTriggers[:len(ub.ActionTriggers)-1]
} else { // For last item, simply reinit the slice
ub.ActionTriggers = make(engine.ActionTriggerPriotityList, 0)
}
}
if err := self.AccountDb.SetUserBalance(ub); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
}
*reply = OK
return nil
}

View File

@@ -288,6 +288,7 @@ type AttrSetActionTimings struct {
ActionTimingsId string // Profile id
Overwrite bool // If previously defined, will be overwritten
ActionTimings []*ApiActionTiming // Set of actions this Actions profile will perform
ReloadScheduler bool // Enables automatic reload of the scheduler (eg: useful when adding a single action timing)
}
type ApiActionTiming struct {
@@ -342,6 +343,10 @@ func (self *ApierV1) SetActionTimings(attrs AttrSetActionTimings, reply *string)
if err := self.AccountDb.SetActionTimings(attrs.ActionTimingsId, storeAtms); err != nil {
return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error())
}
if attrs.ReloadScheduler && self.Sched != nil {
self.Sched.LoadActionTimings(self.AccountDb)
self.Sched.Restart()
}
*reply = OK
return nil
}
@@ -375,7 +380,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string
Executed: false,
}
tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
tag := utils.BalanceKey(attr.Tenant, attr.Account, attr.Direction)
_, err := engine.AccLock.Guard(tag, func() (float64, error) {
userBalance, err := self.AccountDb.GetUserBalance(tag)
if err != nil {
@@ -410,7 +415,7 @@ func (self *ApierV1) AddAccount(attr AttrAddAccount, reply *string) error {
if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Direction", "Account", "Type", "ActionTimingsId"}); len(missing) != 0 {
return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
}
tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account)
tag := utils.BalanceKey(attr.Tenant, attr.Account, attr.Direction)
ub := &engine.UserBalance{
Id: tag,
Type: attr.Type,
@@ -441,7 +446,7 @@ func (self *ApierV1) AddAccount(attr AttrAddAccount, reply *string) error {
}
// Process dependencies and load a specific AccountActions profile from storDb into dataDb.
func (self *ApierV1) SetAccountActions(attrs utils.TPAccountActions, reply *string) error {
func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *string) error {
if missing := utils.MissingStructFields(&attrs, []string{"TPid", "LoadId", "Tenant", "Account", "Direction"}); len(missing) != 0 {
return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing)
}
@@ -464,14 +469,14 @@ func (self *ApierV1) SetAccountActions(attrs utils.TPAccountActions, reply *stri
}
func (self *ApierV1) ReloadScheduler(input string, reply *string) error {
if self.Sched != nil {
self.Sched.LoadActionTimings(self.AccountDb)
self.Sched.Restart()
*reply = OK
return nil
if self.Sched == nil {
return errors.New(utils.ERR_NOT_FOUND)
}
*reply = utils.ERR_NOT_FOUND
return errors.New(utils.ERR_NOT_FOUND)
self.Sched.LoadActionTimings(self.AccountDb)
self.Sched.Restart()
*reply = OK
return nil
}
func (self *ApierV1) ReloadCache(attrs utils.ApiReloadCache, reply *string) error {

View File

@@ -754,17 +754,17 @@ func TestApierSetRatingProfile(t *testing.T) {
}
}
// Test here SetAccountActions
func TestApierSetAccountActions(t *testing.T) {
// Test here LoadAccountActions
func TestApierLoadAccountActions(t *testing.T) {
if !*testLocal {
return
}
reply := ""
aa1 := &utils.TPAccountActions{TPid: engine.TEST_SQL, LoadId: engine.TEST_SQL, Tenant: "cgrates.org", Account: "1001", Direction: "*out"}
if err := rater.Call("ApierV1.SetAccountActions", aa1, &reply); err != nil {
t.Error("Got error on ApierV1.SetAccountActions: ", err.Error())
if err := rater.Call("ApierV1.LoadAccountActions", aa1, &reply); err != nil {
t.Error("Got error on ApierV1.LoadAccountActions: ", err.Error())
} else if reply != "OK" {
t.Error("Calling ApierV1.SetAccountActions got reply: ", reply)
t.Error("Calling ApierV1.LoadAccountActions got reply: ", reply)
}
}