mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-14 13:51:21 +00:00
state/txindex and pkg per indexer impl
This commit is contained in:
@ -9,7 +9,7 @@ import (
|
||||
. "github.com/tendermint/go-common"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
txindexer "github.com/tendermint/tendermint/state/tx/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@ -241,12 +241,12 @@ func (s *State) ApplyBlock(eventCache types.Fireable, proxyAppConn proxy.AppConn
|
||||
return fmt.Errorf("Commit failed for application: %v", err)
|
||||
}
|
||||
|
||||
batch := txindexer.NewBatch()
|
||||
batch := txindex.NewBatch()
|
||||
for i, r := range txResults {
|
||||
tx := block.Txs[i]
|
||||
batch.Index(tx.Hash(), *r)
|
||||
}
|
||||
s.TxIndexer.Batch(batch)
|
||||
s.TxIndexer.AddBatch(batch)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
cfg "github.com/tendermint/tendermint/config/tendermint_test"
|
||||
"github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
txindexer "github.com/tendermint/tendermint/state/tx/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@ -81,10 +81,10 @@ type dummyIndexer struct {
|
||||
Indexed int
|
||||
}
|
||||
|
||||
func (indexer *dummyIndexer) Tx(hash []byte) (*types.TxResult, error) {
|
||||
func (indexer *dummyIndexer) Get(hash []byte) (*types.TxResult, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (indexer *dummyIndexer) Batch(batch *txindexer.Batch) error {
|
||||
func (indexer *dummyIndexer) AddBatch(batch *txindex.Batch) error {
|
||||
indexer.Indexed += batch.Size()
|
||||
return nil
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
cfg "github.com/tendermint/go-config"
|
||||
dbm "github.com/tendermint/go-db"
|
||||
"github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/tendermint/state/tx"
|
||||
txindexer "github.com/tendermint/tendermint/state/tx/indexer"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/state/txindex/null"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@ -41,7 +41,7 @@ type State struct {
|
||||
// AppHash is updated after Commit
|
||||
AppHash []byte
|
||||
|
||||
TxIndexer tx.Indexer `json:"-"` // Transaction indexer.
|
||||
TxIndexer txindex.TxIndexer `json:"-"` // Transaction indexer.
|
||||
}
|
||||
|
||||
func LoadState(db dbm.DB) *State {
|
||||
@ -49,7 +49,7 @@ func LoadState(db dbm.DB) *State {
|
||||
}
|
||||
|
||||
func loadState(db dbm.DB, key []byte) *State {
|
||||
s := &State{db: db, TxIndexer: &txindexer.Null{}}
|
||||
s := &State{db: db, TxIndexer: &null.TxIndex{}}
|
||||
buf := db.Get(key)
|
||||
if len(buf) == 0 {
|
||||
return nil
|
||||
@ -188,6 +188,6 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) *State {
|
||||
Validators: types.NewValidatorSet(validators),
|
||||
LastValidators: types.NewValidatorSet(nil),
|
||||
AppHash: genDoc.AppHash,
|
||||
TxIndexer: &txindexer.Null{}, // we do not need indexer during replay and in tests
|
||||
TxIndexer: &null.TxIndex{}, // we do not need indexer during replay and in tests
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
txindexer "github.com/tendermint/tendermint/state/tx/indexer"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Indexer interface defines methods to index and search transactions.
|
||||
type Indexer interface {
|
||||
|
||||
// Batch analyzes, indexes or stores a batch of transactions.
|
||||
//
|
||||
// NOTE We do not specify Index method for analyzing a single transaction
|
||||
// here because it bears heavy perfomance loses. Almost all advanced indexers
|
||||
// support batching.
|
||||
Batch(b *txindexer.Batch) error
|
||||
|
||||
// Tx returns specified transaction or nil if the transaction is not indexed
|
||||
// or stored.
|
||||
Tx(hash []byte) (*types.TxResult, error)
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import "github.com/tendermint/tendermint/types"
|
||||
|
||||
// A Batch groups together multiple Index operations you would like performed
|
||||
// at the same time. The Batch structure is NOT thread-safe. You should only
|
||||
// perform operations on a batch from a single thread at a time. Once batch
|
||||
// execution has started, you may not modify it.
|
||||
type Batch struct {
|
||||
Ops map[string]types.TxResult
|
||||
}
|
||||
|
||||
// NewBatch creates a new Batch.
|
||||
func NewBatch() *Batch {
|
||||
return &Batch{
|
||||
Ops: make(map[string]types.TxResult),
|
||||
}
|
||||
}
|
||||
|
||||
// Index adds or updates entry for the given hash.
|
||||
func (b *Batch) Index(hash []byte, result types.TxResult) error {
|
||||
if len(hash) == 0 {
|
||||
return ErrorEmptyHash
|
||||
}
|
||||
b.Ops[string(hash)] = result
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size returns the total number of operations inside the batch.
|
||||
func (b *Batch) Size() int {
|
||||
return len(b.Ops)
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrorEmptyHash indicates empty hash
|
||||
var ErrorEmptyHash = errors.New("Transaction hash cannot be empty")
|
@ -1,19 +0,0 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Null acts as a /dev/null.
|
||||
type Null struct{}
|
||||
|
||||
// Tx panics.
|
||||
func (indexer *Null) Tx(hash []byte) (*types.TxResult, error) {
|
||||
return nil, errors.New(`Indexing is disabled (set 'tx_indexer = "kv"' in config)`)
|
||||
}
|
||||
|
||||
// Batch returns nil.
|
||||
func (indexer *Null) Batch(batch *Batch) error {
|
||||
return nil
|
||||
}
|
60
state/txindex/indexer.go
Normal file
60
state/txindex/indexer.go
Normal file
@ -0,0 +1,60 @@
|
||||
package txindex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Indexer interface defines methods to index and search transactions.
|
||||
type TxIndexer interface {
|
||||
|
||||
// Batch analyzes, indexes or stores a batch of transactions.
|
||||
//
|
||||
// NOTE We do not specify Index method for analyzing a single transaction
|
||||
// here because it bears heavy perfomance loses. Almost all advanced indexers
|
||||
// support batching.
|
||||
AddBatch(b *Batch) error
|
||||
|
||||
// Tx returns specified transaction or nil if the transaction is not indexed
|
||||
// or stored.
|
||||
Get(hash []byte) (*types.TxResult, error)
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
// Txs are written as a batch
|
||||
|
||||
// A Batch groups together multiple Index operations you would like performed
|
||||
// at the same time. The Batch structure is NOT thread-safe. You should only
|
||||
// perform operations on a batch from a single thread at a time. Once batch
|
||||
// execution has started, you may not modify it.
|
||||
type Batch struct {
|
||||
Ops map[string]types.TxResult
|
||||
}
|
||||
|
||||
// NewBatch creates a new Batch.
|
||||
func NewBatch() *Batch {
|
||||
return &Batch{
|
||||
Ops: make(map[string]types.TxResult),
|
||||
}
|
||||
}
|
||||
|
||||
// Index adds or updates entry for the given hash.
|
||||
func (b *Batch) Index(hash []byte, result types.TxResult) error {
|
||||
if len(hash) == 0 {
|
||||
return ErrorEmptyHash
|
||||
}
|
||||
b.Ops[string(hash)] = result
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size returns the total number of operations inside the batch.
|
||||
func (b *Batch) Size() int {
|
||||
return len(b.Ops)
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
// Errors
|
||||
|
||||
// ErrorEmptyHash indicates empty hash
|
||||
var ErrorEmptyHash = errors.New("Transaction hash cannot be empty")
|
@ -1,4 +1,4 @@
|
||||
package indexer
|
||||
package kv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -6,28 +6,29 @@ import (
|
||||
|
||||
db "github.com/tendermint/go-db"
|
||||
"github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// KV is a simplest possible indexer, backed by Key-Value storage (levelDB).
|
||||
// TxIndex is the simplest possible indexer, backed by Key-Value storage (levelDB).
|
||||
// It could only index transaction by its identifier.
|
||||
type KV struct {
|
||||
type TxIndex struct {
|
||||
store db.DB
|
||||
}
|
||||
|
||||
// NewKV returns new instance of KV indexer.
|
||||
func NewKV(store db.DB) *KV {
|
||||
return &KV{store: store}
|
||||
// NewTxIndex returns new instance of TxIndex.
|
||||
func NewTxIndex(store db.DB) *TxIndex {
|
||||
return &TxIndex{store: store}
|
||||
}
|
||||
|
||||
// Tx gets transaction from the KV storage and returns it or nil if the
|
||||
// Get gets transaction from the TxIndex storage and returns it or nil if the
|
||||
// transaction is not found.
|
||||
func (indexer *KV) Tx(hash []byte) (*types.TxResult, error) {
|
||||
func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) {
|
||||
if len(hash) == 0 {
|
||||
return nil, ErrorEmptyHash
|
||||
return nil, txindex.ErrorEmptyHash
|
||||
}
|
||||
|
||||
rawBytes := indexer.store.Get(hash)
|
||||
rawBytes := txi.store.Get(hash)
|
||||
if rawBytes == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@ -43,9 +44,9 @@ func (indexer *KV) Tx(hash []byte) (*types.TxResult, error) {
|
||||
return txResult, nil
|
||||
}
|
||||
|
||||
// Batch writes a batch of transactions into the KV storage.
|
||||
func (indexer *KV) Batch(b *Batch) error {
|
||||
storeBatch := indexer.store.NewBatch()
|
||||
// Batch writes a batch of transactions into the TxIndex storage.
|
||||
func (txi *TxIndex) AddBatch(b *txindex.Batch) error {
|
||||
storeBatch := txi.store.NewBatch()
|
||||
for hash, result := range b.Ops {
|
||||
rawBytes := wire.BinaryBytes(&result)
|
||||
storeBatch.Set([]byte(hash), rawBytes)
|
@ -1,4 +1,4 @@
|
||||
package indexer
|
||||
package kv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -10,27 +10,28 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
db "github.com/tendermint/go-db"
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func TestKVIndex(t *testing.T) {
|
||||
indexer := &KV{store: db.NewMemDB()}
|
||||
func TestTxIndex(t *testing.T) {
|
||||
indexer := &TxIndex{store: db.NewMemDB()}
|
||||
|
||||
tx := types.Tx("HELLO WORLD")
|
||||
txResult := &types.TxResult{1, 1, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}}
|
||||
hash := tx.Hash()
|
||||
|
||||
batch := NewBatch()
|
||||
batch := txindex.NewBatch()
|
||||
batch.Index(hash, *txResult)
|
||||
err := indexer.Batch(batch)
|
||||
err := indexer.AddBatch(batch)
|
||||
require.Nil(t, err)
|
||||
|
||||
loadedTxResult, err := indexer.Tx(hash)
|
||||
loadedTxResult, err := indexer.Get(hash)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, txResult, loadedTxResult)
|
||||
}
|
||||
|
||||
func benchmarkKVIndex(txsCount int, b *testing.B) {
|
||||
func benchmarkTxIndex(txsCount int, b *testing.B) {
|
||||
txResult := &types.TxResult{1, 1, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: ""}}
|
||||
|
||||
dir, err := ioutil.TempDir("", "tx_indexer_db")
|
||||
@ -40,9 +41,9 @@ func benchmarkKVIndex(txsCount int, b *testing.B) {
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
store := db.NewDB("tx_indexer", "leveldb", dir)
|
||||
indexer := &KV{store: store}
|
||||
indexer := &TxIndex{store: store}
|
||||
|
||||
batch := NewBatch()
|
||||
batch := txindex.NewBatch()
|
||||
for i := 0; i < txsCount; i++ {
|
||||
batch.Index([]byte(fmt.Sprintf("hash%v", i)), *txResult)
|
||||
}
|
||||
@ -50,12 +51,12 @@ func benchmarkKVIndex(txsCount int, b *testing.B) {
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
err = indexer.Batch(batch)
|
||||
err = indexer.AddBatch(batch)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkKVIndex1(b *testing.B) { benchmarkKVIndex(1, b) }
|
||||
func BenchmarkKVIndex500(b *testing.B) { benchmarkKVIndex(500, b) }
|
||||
func BenchmarkKVIndex1000(b *testing.B) { benchmarkKVIndex(1000, b) }
|
||||
func BenchmarkKVIndex2000(b *testing.B) { benchmarkKVIndex(2000, b) }
|
||||
func BenchmarkKVIndex10000(b *testing.B) { benchmarkKVIndex(10000, b) }
|
||||
func BenchmarkTxIndex1(b *testing.B) { benchmarkTxIndex(1, b) }
|
||||
func BenchmarkTxIndex500(b *testing.B) { benchmarkTxIndex(500, b) }
|
||||
func BenchmarkTxIndex1000(b *testing.B) { benchmarkTxIndex(1000, b) }
|
||||
func BenchmarkTxIndex2000(b *testing.B) { benchmarkTxIndex(2000, b) }
|
||||
func BenchmarkTxIndex10000(b *testing.B) { benchmarkTxIndex(10000, b) }
|
21
state/txindex/null/null.go
Normal file
21
state/txindex/null/null.go
Normal file
@ -0,0 +1,21 @@
|
||||
package null
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/tendermint/tendermint/state/txindex"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// TxIndex acts as a /dev/null.
|
||||
type TxIndex struct{}
|
||||
|
||||
// Tx panics.
|
||||
func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) {
|
||||
return nil, errors.New(`Indexing is disabled (set 'tx_indexer = "kv"' in config)`)
|
||||
}
|
||||
|
||||
// Batch returns nil.
|
||||
func (txi *TxIndex) AddBatch(batch *txindex.Batch) error {
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user