Skip to content

Commit

Permalink
missioncontrolstore: remove duplication of in-memory data
Browse files Browse the repository at this point in the history
This removes duplication of in-memory data during the periodic flushing
stage of the mission control store.

The existing code entirely duplicates the in-memory cache of the store,
which is very wasteful when only a few additional results are being
rotated into the store.

This has a significant performance penalty specially for wallets that
remain online for a long time with a low volument of payments. The worst
case scenario are wallets that see at most 1 new payment a second, where
the entire in-memory cache is recreated every second.

This commit improves the situation by determining what will be the
actual changes that need to be committed before initiating the db
transaction and only keeping track of these to update the in-memory
cache if the db tx is successful.
  • Loading branch information
matheusd committed Apr 22, 2024
1 parent 3486426 commit 242ac9f
Showing 1 changed file with 51 additions and 34 deletions.
85 changes: 51 additions & 34 deletions routing/missioncontrol_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,46 @@ func (b *missionControlStore) storeResults() error {
return nil
}

var (
keys *list.List
keysMap map[string]struct{}
)
// Create a deduped list of new entries.
newKeys := make(map[string]*paymentResult, l.Len())
for e := l.Front(); e != nil; {
pr := e.Value.(*paymentResult)
key := string(getResultKey(pr))
if _, ok := b.keysMap[key]; ok {
e, _ = e.Next(), l.Remove(e)
continue
}
if _, ok := newKeys[key]; ok {
e, _ = e.Next(), l.Remove(e)
continue
}
newKeys[key] = pr
e = e.Next()
}

// Create a list of entries to delete.
toDelete := b.keys.Len() + len(newKeys) - b.maxRecords
var delKeys []string
if b.maxRecords > 0 && toDelete > 0 {
delKeys = make([]string, 0, toDelete)

// Delete as many as needed from old keys.
for e := b.keys.Front(); len(delKeys) < toDelete && e != nil; {
delKeys = append(delKeys, e.Value.(string))
e = e.Next()
}

// If more deletions are needed, simply do not add from the
// list of new keys.
for e := l.Front(); len(delKeys) < toDelete && e != nil; {
toDelete--
pr := e.Value.(*paymentResult)
key := string(getResultKey(pr))
delete(newKeys, key)
l.Remove(e)
e = l.Front()
}
}

var storeCount, pruneCount int

Expand All @@ -355,51 +391,24 @@ func (b *missionControlStore) storeResults() error {
return err
}

// The store is assumed to be idempotent. It could be
// that the same result is added twice and in that case
// we don't need to put the value again.
if _, ok := keysMap[string(k)]; ok {
continue
}

// Put into results bucket.
if err := bucket.Put(k, v); err != nil {
return err
}

keys.PushBack(string(k))
keysMap[string(k)] = struct{}{}
storeCount++
}

// Prune oldest entries.
for {
if b.maxRecords == 0 || keys.Len() <= b.maxRecords {
break
}

front := keys.Front()
key := front.Value.(string)

for _, key := range delKeys {
if err := bucket.Delete([]byte(key)); err != nil {
return err
}

keys.Remove(front)
delete(keysMap, key)
pruneCount++
}

return nil
}, func() {
keys = list.New()
keys.PushBackList(b.keys)

keysMap = make(map[string]struct{})
for k := range b.keysMap {
keysMap[k] = struct{}{}
}

storeCount, pruneCount = 0, 0
})

Expand All @@ -410,8 +419,16 @@ func (b *missionControlStore) storeResults() error {
log.Debugf("Stored mission control results: %d added, %d deleted",
storeCount, pruneCount)

b.keys = keys
b.keysMap = keysMap
// DB Update was successful, update the in-memory cache.
for _, key := range delKeys {
delete(b.keysMap, key)
b.keys.Remove(b.keys.Front())
}
for e := l.Front(); e != nil; e = e.Next() {
pr := e.Value.(*paymentResult)
key := string(getResultKey(pr))
b.keys.PushBack(key)
}

return nil
}
Expand Down

0 comments on commit 242ac9f

Please sign in to comment.