feat(wallet) process all the events and debounce updates
Process missing events Throttle down downloader's events to avoid overloading the CPU with change detection. Updates status-desktop #12120
This commit is contained in:
parent
dc726007b0
commit
a63d33e04a
|
@ -68,7 +68,8 @@ type Service struct {
|
||||||
subscriptions event.Subscription
|
subscriptions event.Subscription
|
||||||
ch chan walletevent.Event
|
ch chan walletevent.Event
|
||||||
// sessionsRWMutex is used to protect all sessions related members
|
// sessionsRWMutex is used to protect all sessions related members
|
||||||
sessionsRWMutex sync.RWMutex
|
sessionsRWMutex sync.RWMutex
|
||||||
|
debounceDuration time.Duration
|
||||||
|
|
||||||
pendingTracker *transactions.PendingTxTracker
|
pendingTracker *transactions.PendingTxTracker
|
||||||
}
|
}
|
||||||
|
@ -86,6 +87,8 @@ func NewService(db *sql.DB, tokenManager token.ManagerInterface, collectibles co
|
||||||
scheduler: async.NewMultiClientScheduler(),
|
scheduler: async.NewMultiClientScheduler(),
|
||||||
|
|
||||||
sessions: make(map[SessionID]*Session),
|
sessions: make(map[SessionID]*Session),
|
||||||
|
// here to be overwritten by tests
|
||||||
|
debounceDuration: 1 * time.Second,
|
||||||
|
|
||||||
pendingTracker: pendingTracker,
|
pendingTracker: pendingTracker,
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ func setupTestService(tb testing.TB) (state testState) {
|
||||||
state.pendingTracker = transactions.NewPendingTxTracker(db, state.chainClient, nil, state.eventFeed, pendingCheckInterval)
|
state.pendingTracker = transactions.NewPendingTxTracker(db, state.chainClient, nil, state.eventFeed, pendingCheckInterval)
|
||||||
|
|
||||||
state.service = NewService(db, state.tokenMock, state.collectiblesMock, state.eventFeed, state.pendingTracker)
|
state.service = NewService(db, state.tokenMock, state.collectiblesMock, state.eventFeed, state.pendingTracker)
|
||||||
|
state.service.debounceDuration = 0
|
||||||
state.close = func() {
|
state.close = func() {
|
||||||
require.NoError(tb, state.pendingTracker.Stop())
|
require.NoError(tb, state.pendingTracker.Stop())
|
||||||
require.NoError(tb, db.Close())
|
require.NoError(tb, db.Close())
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
eth "github.com/ethereum/go-ethereum/common"
|
eth "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
@ -331,66 +332,78 @@ func (s *Service) subscribeToEvents() {
|
||||||
|
|
||||||
// processEvents runs only if more than one session is active
|
// processEvents runs only if more than one session is active
|
||||||
func (s *Service) processEvents() {
|
func (s *Service) processEvents() {
|
||||||
|
eventCount := 0
|
||||||
|
lastUpdate := time.Now().UnixMilli()
|
||||||
for event := range s.ch {
|
for event := range s.ch {
|
||||||
// TODO #12120: process rest of the events transactions.EventPendingTransactionStatusChanged, transfer.EventNewTransfers
|
if event.Type == transactions.EventPendingTransactionUpdate ||
|
||||||
// TODO #12120: debounce for 1s and sum all events as extraCount to be sure we don't miss any change
|
event.Type == transactions.EventPendingTransactionStatusChanged ||
|
||||||
if event.Type == transactions.EventPendingTransactionUpdate {
|
event.Type == transfer.EventNewTransfers {
|
||||||
for sessionID := range s.sessions {
|
eventCount++
|
||||||
session := s.sessions[sessionID]
|
}
|
||||||
|
// debounce events updates
|
||||||
|
if eventCount > 0 &&
|
||||||
|
(time.Duration(time.Now().UnixMilli()-lastUpdate)*time.Millisecond) >= s.debounceDuration {
|
||||||
|
s.detectNew(eventCount)
|
||||||
|
eventCount = 0
|
||||||
|
lastUpdate = time.Now().UnixMilli()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extraCount := 1
|
func (s *Service) detectNew(changeCount int) {
|
||||||
fetchLen := len(session.model) + extraCount
|
for sessionID := range s.sessions {
|
||||||
activities, err := getActivityEntries(context.Background(), s.getDeps(), session.addresses, session.allAddresses, session.chainIDs, session.filter, 0, fetchLen)
|
session := s.sessions[sessionID]
|
||||||
if err != nil {
|
|
||||||
log.Error("Error getting activity entries", "error", err)
|
fetchLen := len(session.model) + changeCount
|
||||||
continue
|
activities, err := getActivityEntries(context.Background(), s.getDeps(), session.addresses, session.allAddresses, session.chainIDs, session.filter, 0, fetchLen)
|
||||||
}
|
if err != nil {
|
||||||
|
log.Error("Error getting activity entries", "error", err)
|
||||||
s.sessionsRWMutex.RLock()
|
continue
|
||||||
allData := append(session.new, session.model...)
|
}
|
||||||
new, _ /*removed*/ := findUpdates(allData, activities)
|
|
||||||
s.sessionsRWMutex.RUnlock()
|
s.sessionsRWMutex.RLock()
|
||||||
|
allData := append(session.new, session.model...)
|
||||||
s.sessionsRWMutex.Lock()
|
new, _ /*removed*/ := findUpdates(allData, activities)
|
||||||
lastProcessed := -1
|
s.sessionsRWMutex.RUnlock()
|
||||||
onTop := true
|
|
||||||
var mixed []*EntryUpdate
|
s.sessionsRWMutex.Lock()
|
||||||
for i, idRes := range new {
|
lastProcessed := -1
|
||||||
// Detect on top
|
onTop := true
|
||||||
if onTop {
|
var mixed []*EntryUpdate
|
||||||
// mixedIdentityResult.newPos includes session.new, therefore compensate for it
|
for i, idRes := range new {
|
||||||
if ((idRes.newPos - len(session.new)) - lastProcessed) > 1 {
|
// Detect on top
|
||||||
// From now on the events are not on top and continuous but mixed between existing entries
|
if onTop {
|
||||||
onTop = false
|
// mixedIdentityResult.newPos includes session.new, therefore compensate for it
|
||||||
mixed = make([]*EntryUpdate, 0, len(new)-i)
|
if ((idRes.newPos - len(session.new)) - lastProcessed) > 1 {
|
||||||
}
|
// From now on the events are not on top and continuous but mixed between existing entries
|
||||||
lastProcessed = idRes.newPos
|
onTop = false
|
||||||
}
|
mixed = make([]*EntryUpdate, 0, len(new)-i)
|
||||||
|
|
||||||
if onTop {
|
|
||||||
if session.new == nil {
|
|
||||||
session.new = make([]EntryIdentity, 0, len(new))
|
|
||||||
}
|
|
||||||
session.new = append(session.new, idRes.id)
|
|
||||||
} else {
|
|
||||||
modelPos := idRes.newPos - len(session.new)
|
|
||||||
entry := activities[idRes.newPos]
|
|
||||||
entry.isNew = true
|
|
||||||
mixed = append(mixed, &EntryUpdate{
|
|
||||||
Pos: modelPos,
|
|
||||||
Entry: &entry,
|
|
||||||
})
|
|
||||||
// Insert in session model at modelPos index
|
|
||||||
session.model = append(session.model[:modelPos], append([]EntryIdentity{{payloadType: entry.payloadType, transaction: entry.transaction, id: entry.id}}, session.model[modelPos:]...)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.sessionsRWMutex.Unlock()
|
|
||||||
|
|
||||||
if len(session.new) > 0 || len(mixed) > 0 {
|
|
||||||
go notify(s.eventFeed, sessionID, len(session.new) > 0, mixed)
|
|
||||||
}
|
}
|
||||||
|
lastProcessed = idRes.newPos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if onTop {
|
||||||
|
if session.new == nil {
|
||||||
|
session.new = make([]EntryIdentity, 0, len(new))
|
||||||
|
}
|
||||||
|
session.new = append(session.new, idRes.id)
|
||||||
|
} else {
|
||||||
|
modelPos := idRes.newPos - len(session.new)
|
||||||
|
entry := activities[idRes.newPos]
|
||||||
|
entry.isNew = true
|
||||||
|
mixed = append(mixed, &EntryUpdate{
|
||||||
|
Pos: modelPos,
|
||||||
|
Entry: &entry,
|
||||||
|
})
|
||||||
|
// Insert in session model at modelPos index
|
||||||
|
session.model = append(session.model[:modelPos], append([]EntryIdentity{{payloadType: entry.payloadType, transaction: entry.transaction, id: entry.id}}, session.model[modelPos:]...)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.sessionsRWMutex.Unlock()
|
||||||
|
|
||||||
|
if len(session.new) > 0 || len(mixed) > 0 {
|
||||||
|
go notify(s.eventFeed, sessionID, len(session.new) > 0, mixed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue