Skip to content
Snippets Groups Projects
Commit b26a2aa1 authored by Hans Moog's avatar Hans Moog
Browse files

Feat: made balance thread safe + added history cleanup

parent 2a0b2609
No related branches found
No related tags found
No related merge requests found
package mana package mana
import ( import (
"sync"
"github.com/iotaledger/goshimmer/packages/datastructure" "github.com/iotaledger/goshimmer/packages/datastructure"
"github.com/iotaledger/goshimmer/packages/errors" "github.com/iotaledger/goshimmer/packages/errors"
) )
...@@ -8,6 +10,7 @@ import ( ...@@ -8,6 +10,7 @@ import (
type Balance struct { type Balance struct {
calculator *Calculator calculator *Calculator
transferHistory *datastructure.DoublyLinkedList transferHistory *datastructure.DoublyLinkedList
mutex sync.RWMutex
} }
func NewBalance(calculator *Calculator) *Balance { func NewBalance(calculator *Calculator) *Balance {
...@@ -19,6 +22,9 @@ func NewBalance(calculator *Calculator) *Balance { ...@@ -19,6 +22,9 @@ func NewBalance(calculator *Calculator) *Balance {
// Returns the current mana balance. // Returns the current mana balance.
func (balance *Balance) GetValue(now ...uint64) (result uint64, err errors.IdentifiableError) { func (balance *Balance) GetValue(now ...uint64) (result uint64, err errors.IdentifiableError) {
balance.mutex.RLock()
defer balance.mutex.RUnlock()
if lastBalanceHistoryEntry, historyErr := balance.transferHistory.GetLast(); historyErr != nil { if lastBalanceHistoryEntry, historyErr := balance.transferHistory.GetLast(); historyErr != nil {
if !datastructure.ErrNoSuchElement.Equals(historyErr) { if !datastructure.ErrNoSuchElement.Equals(historyErr) {
err = historyErr err = historyErr
...@@ -46,6 +52,9 @@ func (balance *Balance) GetValue(now ...uint64) (result uint64, err errors.Ident ...@@ -46,6 +52,9 @@ func (balance *Balance) GetValue(now ...uint64) (result uint64, err errors.Ident
// Returns the timestamp of the last mana erosion. // Returns the timestamp of the last mana erosion.
func (balance *Balance) GetLastErosion() uint64 { func (balance *Balance) GetLastErosion() uint64 {
balance.mutex.RLock()
defer balance.mutex.RUnlock()
if lastBalanceHistoryEntry, err := balance.transferHistory.GetLast(); datastructure.ErrNoSuchElement.Equals(err) { if lastBalanceHistoryEntry, err := balance.transferHistory.GetLast(); datastructure.ErrNoSuchElement.Equals(err) {
return 0 return 0
} else { } else {
...@@ -54,7 +63,10 @@ func (balance *Balance) GetLastErosion() uint64 { ...@@ -54,7 +63,10 @@ func (balance *Balance) GetLastErosion() uint64 {
} }
// Books a new transfer to the balance. // Books a new transfer to the balance.
func (balance *Balance) AddTransfer(transfer *Transfer) { func (balance *Balance) BookTransfer(transfer *Transfer) {
balance.mutex.Lock()
defer balance.mutex.Unlock()
// check if we need to rollback transfers (to prevent rounding errors) // check if we need to rollback transfers (to prevent rounding errors)
rolledBackTransactions := balance.rollbackTransfers(transfer.spentTime) rolledBackTransactions := balance.rollbackTransfers(transfer.spentTime)
...@@ -67,6 +79,32 @@ func (balance *Balance) AddTransfer(transfer *Transfer) { ...@@ -67,6 +79,32 @@ func (balance *Balance) AddTransfer(transfer *Transfer) {
} }
} }
// Cleans up old transfer history entries to reduce the size of the data.
func (balance *Balance) CleanupTransferHistory(referenceTime uint64) (err errors.IdentifiableError) {
balance.mutex.Lock()
defer balance.mutex.Unlock()
if currentTransferHistoryEntry, historyErr := balance.transferHistory.GetFirstEntry(); historyErr != nil {
if !datastructure.ErrNoSuchElement.Equals(historyErr) {
err = historyErr
}
} else {
for currentTransferHistoryEntry.GetNext() != nil && currentTransferHistoryEntry.GetValue().(*BalanceHistoryEntry).transfer.spentTime < referenceTime {
nextTransferHistoryEntry := currentTransferHistoryEntry.GetNext()
if historyErr := balance.transferHistory.RemoveEntry(currentTransferHistoryEntry); historyErr != nil {
err = historyErr
break
}
currentTransferHistoryEntry = nextTransferHistoryEntry
}
}
return
}
// Rolls back transfers that have their spentTime after the given referenceTime and returns a slice containing the // Rolls back transfers that have their spentTime after the given referenceTime and returns a slice containing the
// rolled back transfers. // rolled back transfers.
// //
......
...@@ -6,24 +6,68 @@ import ( ...@@ -6,24 +6,68 @@ import (
"github.com/magiconair/properties/assert" "github.com/magiconair/properties/assert"
) )
func TestBalance_CleanupTransferHistory(t *testing.T) {
// initialize calculator
calculator := NewCalculator(500, 0.1)
// fill transfer history
balance1 := NewBalance(calculator)
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 10,
receivedTime: 1000,
spentTime: 1700,
})
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 0,
receivedTime: 700,
spentTime: 1000,
})
balance1.BookTransfer(&Transfer{
movedCoins: 1000,
burnedMana: 0,
receivedTime: 0,
spentTime: 700,
})
// cleanup transfer history
if err := balance1.CleanupTransferHistory(1900); err != nil {
t.Error(err)
return
}
// check result (correct balance, correct history size)
if val1, err := balance1.GetValue(); err != nil {
t.Error(err)
return
} else {
assert.Equal(t, val1, uint64(290))
}
assert.Equal(t, balance1.transferHistory.GetSize(), 1)
}
func TestBalance_AddTransfer(t *testing.T) { func TestBalance_AddTransfer(t *testing.T) {
// initialize calculator
calculator := NewCalculator(500, 0.1) calculator := NewCalculator(500, 0.1)
// spend coins multiple times // spend coins multiple times
balance1 := NewBalance(calculator) balance1 := NewBalance(calculator)
balance1.AddTransfer(&Transfer{ balance1.BookTransfer(&Transfer{
movedCoins: 1000, movedCoins: 1000,
burnedMana: 10, burnedMana: 10,
receivedTime: 1000, receivedTime: 1000,
spentTime: 1700, spentTime: 1700,
}) })
balance1.AddTransfer(&Transfer{ balance1.BookTransfer(&Transfer{
movedCoins: 1000, movedCoins: 1000,
burnedMana: 0, burnedMana: 0,
receivedTime: 700, receivedTime: 700,
spentTime: 1000, spentTime: 1000,
}) })
balance1.AddTransfer(&Transfer{ balance1.BookTransfer(&Transfer{
movedCoins: 1000, movedCoins: 1000,
burnedMana: 0, burnedMana: 0,
receivedTime: 0, receivedTime: 0,
...@@ -32,7 +76,7 @@ func TestBalance_AddTransfer(t *testing.T) { ...@@ -32,7 +76,7 @@ func TestBalance_AddTransfer(t *testing.T) {
// hold coins for the full time // hold coins for the full time
balance2 := NewBalance(calculator) balance2 := NewBalance(calculator)
balance2.AddTransfer(&Transfer{ balance2.BookTransfer(&Transfer{
movedCoins: 1000, movedCoins: 1000,
burnedMana: 10, burnedMana: 10,
receivedTime: 0, receivedTime: 0,
...@@ -40,8 +84,18 @@ func TestBalance_AddTransfer(t *testing.T) { ...@@ -40,8 +84,18 @@ func TestBalance_AddTransfer(t *testing.T) {
}) })
// check result // check result
val1, _ := balance1.GetValue() if val1, err := balance1.GetValue(); err != nil {
assert.Equal(t, val1, uint64(291)) t.Error(err)
val2, _ := balance2.GetValue()
return
} else {
assert.Equal(t, val1, uint64(290))
}
if val2, err := balance2.GetValue(); err != nil {
t.Error(err)
return
} else {
assert.Equal(t, val2, uint64(291)) assert.Equal(t, val2, uint64(291))
} }
}
...@@ -6,12 +6,3 @@ type Transfer struct { ...@@ -6,12 +6,3 @@ type Transfer struct {
receivedTime uint64 receivedTime uint64
spentTime uint64 spentTime uint64
} }
func NewTransfer(movedCoins uint64, burnedMana uint64, receivedTime uint64, spentTime uint64) *Transfer {
return &Transfer{
movedCoins: movedCoins,
burnedMana: burnedMana,
receivedTime: receivedTime,
spentTime: spentTime,
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment