Sort item IDs before locking to prevent deadlock

applies to stats,thresholds and resources
This commit is contained in:
ionutboangiu
2025-11-24 12:17:54 +02:00
committed by Dan Christian Bogos
parent 2a190c08c7
commit 5f66cd2220
3 changed files with 30 additions and 14 deletions

View File

@@ -20,7 +20,9 @@ package engine
import (
"fmt"
"maps"
"runtime"
"slices"
"sort"
"sync"
"time"
@@ -676,13 +678,17 @@ func (rS *ResourceService) matchingResourcesForEvent(tnt string, ev *utils.CGREv
return
}
}
rs = make(Resources, 0, len(rIDs))
for resName := range rIDs {
// Lock items in sorted order to prevent AB-BA deadlock.
itemIDs := slices.Sorted(maps.Keys(rIDs))
rs = make(Resources, 0, len(itemIDs))
for _, id := range itemIDs {
lkPrflID := guardian.Guardian.GuardIDs("",
config.CgrConfig().GeneralCfg().LockingTimeout,
resourceProfileLockKey(tnt, resName))
resourceProfileLockKey(tnt, id))
var rPrf *ResourceProfile
if rPrf, err = rS.dm.GetResourceProfile(tnt, resName,
if rPrf, err = rS.dm.GetResourceProfile(tnt, id,
true, true, utils.NonTransactional); err != nil {
guardian.Guardian.UnguardIDs(lkPrflID)
if err == utils.ErrNotFound {

View File

@@ -20,7 +20,9 @@ package engine
import (
"fmt"
"maps"
"runtime"
"slices"
"sync"
"time"
@@ -174,13 +176,17 @@ func (sS *StatService) matchingStatQueuesForEvent(tnt string, statsIDs []string,
return
}
}
sqs = make(StatQueues, 0, len(sqIDs))
for sqID := range sqIDs {
// Lock items in sorted order to prevent AB-BA deadlock.
itemIDs := slices.Sorted(maps.Keys(sqIDs))
sqs = make(StatQueues, 0, len(itemIDs))
for _, id := range itemIDs {
lkPrflID := guardian.Guardian.GuardIDs("",
config.CgrConfig().GeneralCfg().LockingTimeout,
statQueueProfileLockKey(tnt, sqID))
statQueueProfileLockKey(tnt, id))
var sqPrfl *StatQueueProfile
if sqPrfl, err = sS.dm.GetStatQueueProfile(tnt, sqID, true, true, utils.NonTransactional); err != nil {
if sqPrfl, err = sS.dm.GetStatQueueProfile(tnt, id, true, true, utils.NonTransactional); err != nil {
guardian.Guardian.UnguardIDs(lkPrflID)
if err == utils.ErrNotFound {
err = nil

View File

@@ -20,13 +20,13 @@ package engine
import (
"fmt"
"maps"
"runtime"
"slices"
"sort"
"sync"
"time"
"slices"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/guardian"
@@ -472,13 +472,17 @@ func (tS *ThresholdService) matchingThresholdsForEvent(tnt string, args *utils.C
return nil, err
}
}
ts = make(Thresholds, 0, len(tIDs))
for tID := range tIDs {
// Lock items in sorted order to prevent AB-BA deadlock.
itemIDs := slices.Sorted(maps.Keys(tIDs))
ts = make(Thresholds, 0, len(itemIDs))
for _, id := range itemIDs {
lkPrflID := guardian.Guardian.GuardIDs("",
config.CgrConfig().GeneralCfg().LockingTimeout,
thresholdProfileLockKey(tnt, tID))
thresholdProfileLockKey(tnt, id))
var tPrfl *ThresholdProfile
if tPrfl, err = tS.dm.GetThresholdProfile(tnt, tID, true, true, utils.NonTransactional); err != nil {
if tPrfl, err = tS.dm.GetThresholdProfile(tnt, id, true, true, utils.NonTransactional); err != nil {
guardian.Guardian.UnguardIDs(lkPrflID)
if err == utils.ErrNotFound {
err = nil