diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 8c07fd062..a489370e5 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -20,6 +20,7 @@ package engine import ( "log" "reflect" + "sync" "testing" "time" @@ -114,6 +115,100 @@ func populateDB() { } } +func debitTest(t *testing.T, wg *sync.WaitGroup) { + defer wg.Done() + t1 := time.Date(2017, time.February, 2, 17, 30, 0, 0, time.UTC) + t2 := time.Date(2017, time.February, 2, 17, 30, 59, 0, time.UTC) + cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Account: "moneyp", Subject: "nt", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} + _, err := cd.Debit() + if err != nil { + t.Errorf("Error debiting balance: %s", err) + } +} + +func TestParallelDebit(t *testing.T) { + var wg sync.WaitGroup + moneyConcurent := &Account{ + ID: "cgrates.org:moneyp", + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ + &Balance{Value: 10000, Weight: 10}, + }}, + } + if accountingStorage != nil && ratingStorage != nil { + accountingStorage.SetAccount(moneyConcurent) + } else { + t.Log("Could not connect to db!") + return + } + debitsToDo := 50 + for i := 0; i < debitsToDo; i++ { + wg.Add(1) + go debitTest(t, &wg) + } + wg.Wait() + t1 := time.Date(2017, time.February, 2, 17, 30, 0, 0, time.UTC) + t2 := time.Date(2017, time.February, 2, 17, 30, 59, 0, time.UTC) + cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Account: "moneyp", Subject: "nt", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} + + acc, err := cd.getAccount() + + if err != nil { + t.Errorf("Error debiting balance: %+v", err) + } + if acc.BalanceMap[utils.MONETARY][0].GetValue() != float64(10000-(debitsToDo*60)) { + t.Errorf("Balance does not match: %f, expected %f", acc.BalanceMap[utils.MONETARY][0].GetValue(), float64(10000-(debitsToDo*60))) + } + /* + out, err := json.Marshal(acc) + if err == nil { + t.Log("Account: %s", string(out)) + } + */ +} + +func TestSerialDebit(t *testing.T) { + var wg sync.WaitGroup + moneyConcurent := &Account{ + ID: "cgrates.org:moneyp", + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ + &Balance{Value: 10000, Weight: 10}, + }}, + } + if accountingStorage != nil && ratingStorage != nil { + accountingStorage.SetAccount(moneyConcurent) + } else { + t.Log("Could not connect to db!") + return + } + debitsToDo := 50 + for i := 0; i < debitsToDo; i++ { + wg.Add(1) + debitTest(t, &wg) + } + wg.Wait() + t1 := time.Date(2017, time.February, 2, 17, 30, 0, 0, time.UTC) + t2 := time.Date(2017, time.February, 2, 17, 30, 59, 0, time.UTC) + cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Account: "moneyp", Subject: "nt", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} + + acc, err := cd.getAccount() + + if err != nil { + t.Errorf("Error debiting balance: %+v", err) + } + if acc.BalanceMap[utils.MONETARY][0].GetValue() != float64(10000-(debitsToDo*60)) { + t.Errorf("Balance does not match: %f, expected %f", acc.BalanceMap[utils.MONETARY][0].GetValue(), float64(10000-(debitsToDo*60))) + } + /* + out, err := json.Marshal(acc) + if err == nil { + t.Log("Account: %s", string(out)) + } + */ + +} + func TestSplitSpans(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC) diff --git a/guardian/guardian.go b/guardian/guardian.go index 2858c173e..7001f29c7 100644 --- a/guardian/guardian.go +++ b/guardian/guardian.go @@ -40,7 +40,7 @@ type itemLock struct { // unlock() executes combined lock with autoremoving lock from Guardian func (il *itemLock) unlock() { atomic.AddInt64(&il.cnt, -1) - if il.count() == 0 { // last lock in the queue + if atomic.LoadInt64(&il.cnt) == 0 { // last lock in the queue Guardian.Lock() delete(Guardian.locksMap, il.keyID) Guardian.Unlock() @@ -48,10 +48,6 @@ func (il *itemLock) unlock() { il.Unlock() } -func (il *itemLock) count() int64 { - return atomic.LoadInt64(&il.cnt) -} - // GuardianLock is an optimized locking system per locking key type GuardianLock struct { locksMap map[string]*itemLock diff --git a/guardian/guardian_test.go b/guardian/guardian_test.go index 76495bd85..562cac817 100644 --- a/guardian/guardian_test.go +++ b/guardian/guardian_test.go @@ -19,6 +19,7 @@ package guardian import ( "sync" + "sync/atomic" "testing" "time" ) @@ -94,7 +95,7 @@ func TestGuardianGuardIDs(t *testing.T) { for _, lockID := range lockIDs { if itmLock, hasKey := Guardian.locksMap[lockID]; !hasKey { t.Errorf("Cannot find lock for lockID: %s", lockID) - } else if itmLock.count() != 1 { + } else if atomic.LoadInt64(&itmLock.cnt) != 1 { t.Errorf("Unexpected itmLock found: %+v", itmLock) } } @@ -102,42 +103,41 @@ func TestGuardianGuardIDs(t *testing.T) { time.Sleep(20 * time.Microsecond) // give time for goroutine to lock if itmLock, hasKey := Guardian.locksMap["test1"]; !hasKey { t.Errorf("Cannot find lock for lockID: %s", "test1") - } else if itmLock.count() != 1 { + } else if atomic.LoadInt64(&itmLock.cnt) != 1 { t.Errorf("Unexpected itmLock found: %+v", itmLock) } if itmLock, hasKey := Guardian.locksMap["test2"]; !hasKey { t.Errorf("Cannot find lock for lockID: %s", "test2") - } else if itmLock.count() != 2 { + } else if atomic.LoadInt64(&itmLock.cnt) != 2 { t.Errorf("Unexpected itmLock found: %+v", itmLock) } if itmLock, hasKey := Guardian.locksMap["test3"]; !hasKey { t.Errorf("Cannot find lock for lockID: %s", "test3") - } else if itmLock.count() != 2 { + } else if atomic.LoadInt64(&itmLock.cnt) != 2 { t.Errorf("Unexpected itmLock found: %+v", itmLock) } Guardian.GuardIDs(0, lockIDs...) if totalLockDur := time.Now().Sub(tStart); totalLockDur < lockDur { t.Errorf("Lock duration too small") } - //time.Sleep(1000 * time.Microsecond) if len(Guardian.locksMap) != 3 { t.Errorf("locksMap should be have 3 elements, have: %+v", Guardian.locksMap) } else if itmLock, hasKey := Guardian.locksMap["test1"]; !hasKey { t.Errorf("Cannot find lock for lockID: %s", "test1") - } else if itmLock.count() != 1 { - t.Errorf("Unexpected itmLock found: %+v", itmLock) - } else if itmLock, hasKey := Guardian.locksMap["test2"]; !hasKey { + } else if cnt := atomic.LoadInt64(&itmLock.cnt); cnt != 1 { + t.Errorf("Unexpected counter: %d for itmLock with id %s", cnt, "test1") + } else if _, hasKey := Guardian.locksMap["test2"]; !hasKey { t.Errorf("Cannot find lock for lockID: %s", "test2") - } else if itmLock.count() != 1 { - t.Errorf("Unexpected itmLock found: %+v", itmLock) + } else if cnt = atomic.LoadInt64(&itmLock.cnt); cnt != 1 { + t.Errorf("Unexpected counter: %d for itmLock with id %s", cnt, "test2") } else if itmLock, hasKey := Guardian.locksMap["test3"]; !hasKey { - t.Errorf("Cannot find lock for lockID: %s", "test2") - } else if itmLock.count() != 1 { - t.Errorf("Unexpected itmLock found: %+v", itmLock) + t.Errorf("Cannot find lock for lockID: %s", "test3") + } else if cnt = atomic.LoadInt64(&itmLock.cnt); cnt != 1 { + t.Errorf("Unexpected counter: %d for itmLock with id %s", cnt, "test3") } Guardian.UnguardIDs(lockIDs...) if len(Guardian.locksMap) != 0 { - t.Errorf("locksMap should be have 0 elements, have: %+v", Guardian.locksMap) + t.Errorf("locksMap should have 0 elements, has: %+v", Guardian.locksMap) } }