vspd/webapi/cache.go
jholdstock 75026f5e91 Clean up use of shutdown context.
- Rename all instances to "shutdownCtx" to be really explicit. This context is special in that it can be closed at any time without warning, so it should be obvious to the reader.
- Don't use shutdownCtx in RPC clients. Clients should not stop working immediately when shutdown is signalled, they need to keep working while the process is shutting down.
2022-05-19 07:59:44 +01:00

122 lines
2.8 KiB
Go

// Copyright (c) 2020-2022 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package webapi
import (
"errors"
"sync"
"time"
"github.com/decred/vspd/database"
"github.com/decred/vspd/rpc"
"github.com/dustin/go-humanize"
)
// cache is used to store values which are commonly used by the API, so
// repeated web requests don't repeatedly trigger DB or RPC calls.
type cache struct {
// data is the cached data.
data cacheData
// mtx must be held to read/write cache data.
mtx sync.RWMutex
}
type cacheData struct {
UpdateTime string
PubKey string
DatabaseSize string
Voting int64
Voted int64
Revoked int64
VotingWalletsOnline int64
TotalVotingWallets int64
BlockHeight uint32
NetworkProportion float32
RevokedProportion float32
}
func (c *cache) getData() cacheData {
c.mtx.RLock()
defer c.mtx.RUnlock()
return c.data
}
// newCache creates a new cache and initializes it with static values.
func newCache(signPubKey string) *cache {
return &cache{
data: cacheData{
PubKey: signPubKey,
},
}
}
// update will use the provided database and RPC connections to update the
// dynamic values in the cache.
func (c *cache) update(db *database.VspDatabase, dcrd rpc.DcrdConnect,
wallets rpc.WalletConnect) error {
dbSize, err := db.Size()
if err != nil {
return err
}
// Get latest counts of voting, voted and revoked tickets.
voting, voted, revoked, err := db.CountTickets()
if err != nil {
return err
}
// Get latest best block height.
dcrdClient, _, err := dcrd.Client()
if err != nil {
return err
}
bestBlock, err := dcrdClient.GetBestBlockHeader()
if err != nil {
return err
}
if bestBlock.PoolSize == 0 {
return errors.New("dcr node reports a network ticket pool size of zero")
}
clients, failedConnections := wallets.Clients()
if len(clients) == 0 {
log.Error("Could not connect to any wallets")
} else if len(failedConnections) > 0 {
log.Errorf("Failed to connect to %d wallet(s), proceeding with only %d",
len(failedConnections), len(clients))
}
c.mtx.Lock()
defer c.mtx.Unlock()
c.data.UpdateTime = dateTime(time.Now().Unix())
c.data.DatabaseSize = humanize.Bytes(dbSize)
c.data.Voting = voting
c.data.Voted = voted
c.data.TotalVotingWallets = int64(len(clients) + len(failedConnections))
c.data.VotingWalletsOnline = int64(len(clients))
c.data.Revoked = revoked
c.data.BlockHeight = bestBlock.Height
c.data.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize)
// Prevent dividing by zero when pool has no voted tickets.
switch voted {
case 0:
if revoked == 0 {
c.data.RevokedProportion = 0
} else {
c.data.RevokedProportion = 1
}
default:
c.data.RevokedProportion = float32(revoked) / float32(voted)
}
return nil
}