mirror of
https://github.com/btcsuite/btcd.git
synced 2026-08-13 12:32:51 +02:00
Merge pull request #2486 from alrs/indexer-err
blockchain/indexers: fix dropped error
This commit is contained in:
commit
1894fd415e
2 changed files with 177 additions and 1 deletions
|
|
@ -619,7 +619,7 @@ func dropIndex(db database.DB, idxKey []byte, idxName string, interrupt <-chan s
|
|||
return subBucketClosure(dbTx, idxKey, nil)
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate through each sub-bucket in reverse, deepest-first, deleting
|
||||
|
|
@ -668,6 +668,9 @@ func dropIndex(db database.DB, idxKey []byte, idxName string, interrupt <-chan s
|
|||
}
|
||||
return bucket.DeleteBucket(bucketName[len(bucketName)-1])
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Call extra index specific deinitialization for the transaction index.
|
||||
|
|
|
|||
173
blockchain/indexers/manager_test.go
Normal file
173
blockchain/indexers/manager_test.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// Copyright (c) 2026 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package indexers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/database"
|
||||
_ "github.com/btcsuite/btcd/database/ffldb"
|
||||
"github.com/btcsuite/btcd/wire/v2"
|
||||
)
|
||||
|
||||
type bucketBase struct {
|
||||
database.Bucket
|
||||
}
|
||||
|
||||
// errorBucket wraps a database bucket and injects errors into operations used
|
||||
// while cataloging and deleting index buckets.
|
||||
type errorBucket struct {
|
||||
bucketBase
|
||||
forEachBucketErr error
|
||||
deleteBucketErr error
|
||||
}
|
||||
|
||||
// Bucket returns a wrapped nested bucket that preserves error injection.
|
||||
func (b *errorBucket) Bucket(key []byte) database.Bucket {
|
||||
bucket := b.bucketBase.Bucket.Bucket(key)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &errorBucket{
|
||||
bucketBase: bucketBase{Bucket: bucket},
|
||||
forEachBucketErr: b.forEachBucketErr,
|
||||
deleteBucketErr: b.deleteBucketErr,
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachBucket returns the configured cataloging error when present.
|
||||
func (b *errorBucket) ForEachBucket(fn func(k []byte) error) error {
|
||||
if b.forEachBucketErr != nil {
|
||||
return b.forEachBucketErr
|
||||
}
|
||||
|
||||
return b.bucketBase.Bucket.ForEachBucket(fn)
|
||||
}
|
||||
|
||||
// DeleteBucket returns the configured bucket deletion error when present.
|
||||
func (b *errorBucket) DeleteBucket(key []byte) error {
|
||||
if b.deleteBucketErr != nil {
|
||||
return b.deleteBucketErr
|
||||
}
|
||||
|
||||
return b.bucketBase.Bucket.DeleteBucket(key)
|
||||
}
|
||||
|
||||
// errorTx wraps the metadata bucket of a database transaction so errors can
|
||||
// be injected into bucket operations.
|
||||
type errorTx struct {
|
||||
database.Tx
|
||||
forEachBucketErr error
|
||||
deleteBucketErr error
|
||||
}
|
||||
|
||||
// Metadata returns the wrapped top-level metadata bucket.
|
||||
func (tx *errorTx) Metadata() database.Bucket {
|
||||
return &errorBucket{
|
||||
bucketBase: bucketBase{Bucket: tx.Tx.Metadata()},
|
||||
forEachBucketErr: tx.forEachBucketErr,
|
||||
deleteBucketErr: tx.deleteBucketErr,
|
||||
}
|
||||
}
|
||||
|
||||
// errorDB wraps all managed transactions with the configured error injector.
|
||||
type errorDB struct {
|
||||
database.DB
|
||||
forEachBucketErr error
|
||||
deleteBucketErr error
|
||||
}
|
||||
|
||||
// View invokes the view function with an error-injecting transaction.
|
||||
func (db *errorDB) View(fn func(database.Tx) error) error {
|
||||
return db.DB.View(func(tx database.Tx) error {
|
||||
return fn(&errorTx{
|
||||
Tx: tx,
|
||||
forEachBucketErr: db.forEachBucketErr,
|
||||
deleteBucketErr: db.deleteBucketErr,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Update invokes the update function with an error-injecting transaction.
|
||||
func (db *errorDB) Update(fn func(database.Tx) error) error {
|
||||
return db.DB.Update(func(tx database.Tx) error {
|
||||
return fn(&errorTx{
|
||||
Tx: tx,
|
||||
forEachBucketErr: db.forEachBucketErr,
|
||||
deleteBucketErr: db.deleteBucketErr,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// createDropIndexTestDB creates a database with the metadata required to drop
|
||||
// a test index.
|
||||
func createDropIndexTestDB(t *testing.T, idxKey []byte) database.DB {
|
||||
t.Helper()
|
||||
|
||||
dbPath := filepath.Join(t.TempDir(), "blocks_ffldb")
|
||||
db, err := database.Create("ffldb", dbPath, wire.MainNet)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create test database: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := db.Close(); err != nil {
|
||||
t.Errorf("unable to close test database: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
err = db.Update(func(tx database.Tx) error {
|
||||
meta := tx.Metadata()
|
||||
indexesBucket, err := meta.CreateBucket(indexTipsBucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := indexesBucket.Put(idxKey, []byte{0x01}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = meta.CreateBucket(idxKey)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize test index: %v", err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
// TestDropIndexCatalogError ensures errors encountered while cataloging index
|
||||
// buckets are returned to the caller.
|
||||
func TestDropIndexCatalogError(t *testing.T) {
|
||||
idxKey := []byte("testidx")
|
||||
testErr := errors.New("test catalog error")
|
||||
db := &errorDB{
|
||||
DB: createDropIndexTestDB(t, idxKey),
|
||||
forEachBucketErr: testErr,
|
||||
}
|
||||
|
||||
err := dropIndex(db, idxKey, "test index", nil)
|
||||
if !errors.Is(err, testErr) {
|
||||
t.Fatalf("unexpected error: got %v, want %v", err, testErr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDropIndexDeleteBucketError ensures errors encountered while deleting an
|
||||
// index bucket are returned to the caller.
|
||||
func TestDropIndexDeleteBucketError(t *testing.T) {
|
||||
idxKey := []byte("testidx")
|
||||
testErr := errors.New("test bucket deletion error")
|
||||
db := &errorDB{
|
||||
DB: createDropIndexTestDB(t, idxKey),
|
||||
deleteBucketErr: testErr,
|
||||
}
|
||||
|
||||
err := dropIndex(db, idxKey, "test index", nil)
|
||||
if !errors.Is(err, testErr) {
|
||||
t.Fatalf("unexpected error: got %v, want %v", err, testErr)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue