blockchain: Remove UtxoCacher DB Tx dependency.
This removes the dependency that the UtxoCacher interface and UtxoCache implementation currently have on the database.Tx interface. This is necessary since the UtxoCache will move away from the current database interface in favor of an interface that is better suited for the UTXO database in a subsequent commit.
This commit is contained in:
parent
be78f4d0a1
commit
b4806d9e0f
@ -7,7 +7,6 @@ package blockchain
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database/v2"
|
||||
"github.com/decred/dcrd/dcrutil/v4"
|
||||
"github.com/decred/dcrd/txscript/v4"
|
||||
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
||||
@ -111,27 +110,22 @@ func (b *BlockChain) TicketsWithAddress(address stdaddr.Address, isTreasuryEnabl
|
||||
|
||||
encodedAddr := address.String()
|
||||
var ticketsWithAddr []chainhash.Hash
|
||||
err := b.utxoDb.View(func(dbTx database.Tx) error {
|
||||
for _, hash := range tickets {
|
||||
outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake}
|
||||
utxo, err := b.utxoCache.FetchEntry(dbTx, outpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(utxo.ScriptVersion(),
|
||||
utxo.PkScript(), b.chainParams, isTreasuryEnabled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addrs[0].String() == encodedAddr {
|
||||
ticketsWithAddr = append(ticketsWithAddr, hash)
|
||||
}
|
||||
for _, hash := range tickets {
|
||||
outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake}
|
||||
utxo, err := b.utxoCache.FetchEntry(outpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(utxo.ScriptVersion(),
|
||||
utxo.PkScript(), b.chainParams, isTreasuryEnabled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addrs[0].String() == encodedAddr {
|
||||
ticketsWithAddr = append(ticketsWithAddr, hash)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ticketsWithAddr, nil
|
||||
@ -223,20 +217,14 @@ func (b *BlockChain) TicketPoolValue() (dcrutil.Amount, error) {
|
||||
b.chainLock.RUnlock()
|
||||
|
||||
var amt int64
|
||||
err := b.utxoDb.View(func(dbTx database.Tx) error {
|
||||
for _, hash := range sn.LiveTickets() {
|
||||
outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake}
|
||||
utxo, err := b.utxoCache.FetchEntry(dbTx, outpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
amt += utxo.Amount()
|
||||
for _, hash := range sn.LiveTickets() {
|
||||
outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake}
|
||||
utxo, err := b.utxoCache.FetchEntry(outpoint)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
||||
amt += utxo.Amount()
|
||||
}
|
||||
return dcrutil.Amount(amt), nil
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ const (
|
||||
periodicFlushInterval = time.Minute * 2
|
||||
)
|
||||
|
||||
// UtxoCacher represents a utxo cache that sits on top of the utxo set database.
|
||||
// UtxoCacher represents a utxo cache that sits on top of a utxo set backend.
|
||||
//
|
||||
// The interface contract requires that all of these methods are safe for
|
||||
// concurrent access.
|
||||
@ -73,7 +73,7 @@ type UtxoCacher interface {
|
||||
|
||||
// FetchEntries adds the requested transaction outputs to the provided view.
|
||||
// It first checks the cache for each output, and if an output does not exist
|
||||
// in the cache, it will fetch it from the database.
|
||||
// in the cache, it will fetch it from the backend.
|
||||
//
|
||||
// Upon completion of this function, the view will contain an entry for each
|
||||
// requested outpoint. Spent outputs, or those which otherwise don't exist,
|
||||
@ -82,20 +82,19 @@ type UtxoCacher interface {
|
||||
|
||||
// FetchEntry returns the specified transaction output from the utxo set. If
|
||||
// the output exists in the cache, it is returned immediately. Otherwise, it
|
||||
// uses an existing database transaction to fetch the output from the
|
||||
// database, cache it, and return it to the caller. The entry that is
|
||||
// returned can safely be mutated by the caller without invalidating the
|
||||
// cache.
|
||||
// fetches the output from the backend, caches it, and returns it to the
|
||||
// caller. The entry that is returned can safely be mutated by the caller
|
||||
// without invalidating the cache.
|
||||
//
|
||||
// When there is no entry for the provided output, nil will be returned for
|
||||
// both the entry and the error.
|
||||
FetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoEntry, error)
|
||||
FetchEntry(outpoint wire.OutPoint) (*UtxoEntry, error)
|
||||
|
||||
// Initialize initializes the utxo cache by ensuring that the utxo set is
|
||||
// caught up to the tip of the best chain.
|
||||
Initialize(b *BlockChain, tip *blockNode) error
|
||||
|
||||
// MaybeFlush conditionally flushes the cache to the database. A flush can be
|
||||
// MaybeFlush conditionally flushes the cache to the backend. A flush can be
|
||||
// forced by setting the force flush parameter.
|
||||
MaybeFlush(bestHash *chainhash.Hash, bestHeight uint32, forceFlush bool,
|
||||
logFlush bool) error
|
||||
@ -335,19 +334,19 @@ func (c *UtxoCache) SpendEntry(outpoint wire.OutPoint) {
|
||||
|
||||
// fetchEntry returns the specified transaction output from the utxo set. If
|
||||
// the output exists in the cache, it is returned immediately. Otherwise, it
|
||||
// uses an existing database transaction to fetch the output from the database,
|
||||
// cache it, and return it to the caller. A cloned copy of the entry is
|
||||
// returned so it can safely be mutated by the caller without invalidating the
|
||||
// cache.
|
||||
// fetches the output from the database, caches it, and returns it to the
|
||||
// caller. A cloned copy of the entry is returned so it can safely be mutated
|
||||
// by the caller without invalidating the cache.
|
||||
//
|
||||
// When there is no entry for the provided output, nil will be returned for both
|
||||
// the entry and the error.
|
||||
//
|
||||
// This function MUST be called with the cache lock held.
|
||||
func (c *UtxoCache) fetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoEntry, error) {
|
||||
func (c *UtxoCache) fetchEntry(outpoint wire.OutPoint) (*UtxoEntry, error) {
|
||||
// If the cache already has the entry, return it immediately. A cloned copy
|
||||
// of the entry is returned so it can safely be mutated by the caller without
|
||||
// invalidating the cache.
|
||||
var entry *UtxoEntry
|
||||
if entry, found := c.entries[outpoint]; found {
|
||||
c.hits++
|
||||
return entry.Clone(), nil
|
||||
@ -362,7 +361,11 @@ func (c *UtxoCache) fetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoE
|
||||
// will result in nil entries in the view. This is intentionally done
|
||||
// so other code can use the presence of an entry in the view as a way
|
||||
// to unnecessarily avoid attempting to reload it from the database.
|
||||
entry, err := dbFetchUtxoEntry(dbTx, outpoint)
|
||||
err := c.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
entry, err = dbFetchUtxoEntry(dbTx, outpoint)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -381,18 +384,17 @@ func (c *UtxoCache) fetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoE
|
||||
|
||||
// FetchEntry returns the specified transaction output from the utxo set. If
|
||||
// the output exists in the cache, it is returned immediately. Otherwise, it
|
||||
// uses an existing database transaction to fetch the output from the database,
|
||||
// cache it, and return it to the caller. A cloned copy of the entry is
|
||||
// returned so it can safely be mutated by the caller without invalidating the
|
||||
// cache.
|
||||
// fetches the output from the database, caches it, and returns it to the
|
||||
// caller. A cloned copy of the entry is returned so it can safely be mutated
|
||||
// by the caller without invalidating the cache.
|
||||
//
|
||||
// When there is no entry for the provided output, nil will be returned for both
|
||||
// the entry and the error.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *UtxoCache) FetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoEntry, error) {
|
||||
func (c *UtxoCache) FetchEntry(outpoint wire.OutPoint) (*UtxoEntry, error) {
|
||||
c.cacheLock.Lock()
|
||||
entry, err := c.fetchEntry(dbTx, outpoint)
|
||||
entry, err := c.fetchEntry(outpoint)
|
||||
c.cacheLock.Unlock()
|
||||
return entry, err
|
||||
}
|
||||
@ -408,25 +410,21 @@ func (c *UtxoCache) FetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoE
|
||||
// This function is safe for concurrent access.
|
||||
func (c *UtxoCache) FetchEntries(filteredSet viewFilteredSet, view *UtxoViewpoint) error {
|
||||
c.cacheLock.Lock()
|
||||
err := c.db.View(func(dbTx database.Tx) error {
|
||||
for outpoint := range filteredSet {
|
||||
entry, err := c.fetchEntry(dbTx, outpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE: Missing entries are not considered an error here and instead
|
||||
// will result in nil entries in the view. This is intentionally done
|
||||
// so other code can use the presence of an entry in the view as a way
|
||||
// to unnecessarily avoid attempting to reload it from the database.
|
||||
view.entries[outpoint] = entry
|
||||
for outpoint := range filteredSet {
|
||||
entry, err := c.fetchEntry(outpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
// NOTE: Missing entries are not considered an error here and instead
|
||||
// will result in nil entries in the view. This is intentionally done
|
||||
// so other code can use the presence of an entry in the view as a way
|
||||
// to unnecessarily avoid attempting to reload it from the database.
|
||||
view.entries[outpoint] = entry
|
||||
}
|
||||
c.cacheLock.Unlock()
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit updates the cache based on the state of each entry in the provided
|
||||
@ -952,19 +950,9 @@ func (b *BlockChain) ShutdownUtxoCache() {
|
||||
// any) is NOT.
|
||||
func (b *BlockChain) FetchUtxoEntry(outpoint wire.OutPoint) (*UtxoEntry, error) {
|
||||
b.chainLock.RLock()
|
||||
defer b.chainLock.RUnlock()
|
||||
|
||||
var entry *UtxoEntry
|
||||
err := b.utxoDb.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
entry, err = b.utxoCache.FetchEntry(dbTx, outpoint)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
entry, err := b.utxoCache.FetchEntry(outpoint)
|
||||
b.chainLock.RUnlock()
|
||||
return entry, err
|
||||
}
|
||||
|
||||
// UtxoStats represents unspent output statistics on the current utxo set.
|
||||
|
||||
@ -545,6 +545,7 @@ func TestFetchEntry(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
// Create a utxo cache with the cached entries specified by the test.
|
||||
utxoCache := createTestUtxoCache(t, test.cachedEntries)
|
||||
utxoCache.db = db
|
||||
wantTotalEntrySize := utxoCache.totalEntrySize
|
||||
|
||||
// Add entries specified by the test to the test database.
|
||||
@ -563,12 +564,7 @@ func TestFetchEntry(t *testing.T) {
|
||||
}
|
||||
|
||||
// Attempt to fetch the entry for the outpoint specified by the test.
|
||||
var entry *UtxoEntry
|
||||
err = db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
entry, err = utxoCache.FetchEntry(dbTx, test.outpoint)
|
||||
return err
|
||||
})
|
||||
entry, err = utxoCache.FetchEntry(test.outpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("%q: unexpected error fetching entry: %v", test.name, err)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user