2017-04-18 16:29:02 -07:00
|
|
|
package kv
|
2017-01-19 01:33:58 -08:00
|
|
|
|
|
|
|
import (
|
2017-11-26 17:16:21 -08:00
|
|
|
"fmt"
|
2017-01-19 01:33:58 -08:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
abci "github.com/tendermint/abci/types"
|
2017-04-18 16:29:02 -07:00
|
|
|
"github.com/tendermint/tendermint/state/txindex"
|
2017-01-19 01:33:58 -08:00
|
|
|
"github.com/tendermint/tendermint/types"
|
2017-05-29 20:11:40 -07:00
|
|
|
db "github.com/tendermint/tmlibs/db"
|
2017-11-26 17:16:21 -08:00
|
|
|
"github.com/tendermint/tmlibs/pubsub/query"
|
2017-01-19 01:33:58 -08:00
|
|
|
)
|
|
|
|
|
2017-04-18 16:29:02 -07:00
|
|
|
func TestTxIndex(t *testing.T) {
|
2017-11-30 18:02:39 -08:00
|
|
|
indexer := NewTxIndex(db.NewMemDB())
|
2017-01-19 01:33:58 -08:00
|
|
|
|
|
|
|
tx := types.Tx("HELLO WORLD")
|
2017-11-09 14:35:46 -08:00
|
|
|
txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: "", Tags: []*abci.KVPair{}}}
|
2017-04-10 14:18:22 -07:00
|
|
|
hash := tx.Hash()
|
2017-01-19 01:33:58 -08:00
|
|
|
|
2017-04-18 17:23:58 -07:00
|
|
|
batch := txindex.NewBatch(1)
|
2017-11-26 17:16:21 -08:00
|
|
|
if err := batch.Add(txResult); err != nil {
|
2017-09-06 08:50:43 -07:00
|
|
|
t.Error(err)
|
|
|
|
}
|
2017-11-28 16:58:39 -08:00
|
|
|
err := indexer.AddBatch(batch)
|
2017-11-26 17:16:21 -08:00
|
|
|
require.NoError(t, err)
|
2017-01-19 01:33:58 -08:00
|
|
|
|
2017-04-18 16:29:02 -07:00
|
|
|
loadedTxResult, err := indexer.Get(hash)
|
2017-11-26 17:16:21 -08:00
|
|
|
require.NoError(t, err)
|
2017-01-19 01:33:58 -08:00
|
|
|
assert.Equal(t, txResult, loadedTxResult)
|
2017-11-15 13:07:08 -08:00
|
|
|
|
|
|
|
tx2 := types.Tx("BYE BYE WORLD")
|
|
|
|
txResult2 := &types.TxResult{1, 0, tx2, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: "", Tags: []*abci.KVPair{}}}
|
|
|
|
hash2 := tx2.Hash()
|
|
|
|
|
2017-11-28 16:58:39 -08:00
|
|
|
err = indexer.Index(txResult2)
|
2017-11-26 17:16:21 -08:00
|
|
|
require.NoError(t, err)
|
2017-11-15 13:07:08 -08:00
|
|
|
|
|
|
|
loadedTxResult2, err := indexer.Get(hash2)
|
2017-11-26 17:16:21 -08:00
|
|
|
require.NoError(t, err)
|
2017-11-15 13:07:08 -08:00
|
|
|
assert.Equal(t, txResult2, loadedTxResult2)
|
2017-01-19 01:33:58 -08:00
|
|
|
}
|
|
|
|
|
2017-11-26 17:16:21 -08:00
|
|
|
func TestTxSearch(t *testing.T) {
|
2017-11-30 18:15:03 -08:00
|
|
|
allowedTags := []string{"account.number", "account.owner", "account.date"}
|
|
|
|
indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
|
2017-11-26 17:16:21 -08:00
|
|
|
|
|
|
|
tx := types.Tx("HELLO WORLD")
|
|
|
|
tags := []*abci.KVPair{
|
2017-11-28 16:58:39 -08:00
|
|
|
{Key: "account.number", ValueType: abci.KVPair_INT, ValueInt: 1},
|
|
|
|
{Key: "account.owner", ValueType: abci.KVPair_STRING, ValueString: "Ivan"},
|
|
|
|
{Key: "not_allowed", ValueType: abci.KVPair_STRING, ValueString: "Vlad"},
|
2017-11-26 17:16:21 -08:00
|
|
|
}
|
|
|
|
txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: "", Tags: tags}}
|
|
|
|
hash := tx.Hash()
|
|
|
|
|
2017-11-28 16:58:39 -08:00
|
|
|
err := indexer.Index(txResult)
|
2017-11-26 17:16:21 -08:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
q string
|
|
|
|
resultsLength int
|
|
|
|
}{
|
|
|
|
// search by hash
|
2017-11-28 16:58:39 -08:00
|
|
|
{fmt.Sprintf("tx.hash = '%X'", hash), 1},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search by exact match (one tag)
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.number = 1", 1},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search by exact match (two tags)
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.number = 1 AND account.owner = 'Ivan'", 1},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search by exact match (two tags)
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.number = 1 AND account.owner = 'Vlad'", 0},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search by range
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.number >= 1 AND account.number <= 5", 1},
|
2017-11-29 18:04:00 -08:00
|
|
|
// search by range (lower bound)
|
|
|
|
{"account.number >= 1", 1},
|
|
|
|
// search by range (upper bound)
|
|
|
|
{"account.number <= 5", 1},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search using not allowed tag
|
2017-11-28 16:58:39 -08:00
|
|
|
{"not_allowed = 'boom'", 0},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search for not existing tx result
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.number >= 2 AND account.number <= 5", 0},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search using not existing tag
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.date >= TIME 2013-05-03T14:45:00Z", 0},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search using CONTAINS
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.owner CONTAINS 'an'", 1},
|
2017-11-26 17:16:21 -08:00
|
|
|
// search using CONTAINS
|
2017-11-28 16:58:39 -08:00
|
|
|
{"account.owner CONTAINS 'Vlad'", 0},
|
2017-11-26 17:16:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.q, func(t *testing.T) {
|
|
|
|
results, err := indexer.Search(query.MustParse(tc.q))
|
2017-11-28 16:58:39 -08:00
|
|
|
assert.NoError(t, err)
|
2017-11-26 17:16:21 -08:00
|
|
|
|
|
|
|
assert.Len(t, results, tc.resultsLength)
|
|
|
|
if tc.resultsLength > 0 {
|
2017-11-28 16:58:39 -08:00
|
|
|
assert.Equal(t, []*types.TxResult{txResult}, results)
|
2017-11-26 17:16:21 -08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-29 18:04:26 -08:00
|
|
|
func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) {
|
2017-11-30 18:15:03 -08:00
|
|
|
allowedTags := []string{"account.number"}
|
|
|
|
indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
|
2017-11-29 18:04:26 -08:00
|
|
|
|
|
|
|
tx := types.Tx("SAME MULTIPLE TAGS WITH DIFFERENT VALUES")
|
|
|
|
tags := []*abci.KVPair{
|
|
|
|
{Key: "account.number", ValueType: abci.KVPair_INT, ValueInt: 1},
|
|
|
|
{Key: "account.number", ValueType: abci.KVPair_INT, ValueInt: 2},
|
|
|
|
}
|
|
|
|
txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: "", Tags: tags}}
|
|
|
|
|
|
|
|
err := indexer.Index(txResult)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
results, err := indexer.Search(query.MustParse("account.number >= 1"))
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Len(t, results, 1)
|
|
|
|
assert.Equal(t, []*types.TxResult{txResult}, results)
|
|
|
|
}
|
|
|
|
|
2017-04-18 16:29:02 -07:00
|
|
|
func benchmarkTxIndex(txsCount int, b *testing.B) {
|
2017-04-18 16:56:41 -07:00
|
|
|
tx := types.Tx("HELLO WORLD")
|
2017-11-09 14:35:46 -08:00
|
|
|
txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeType_OK, Log: "", Tags: []*abci.KVPair{}}}
|
2017-01-19 01:33:58 -08:00
|
|
|
|
2017-04-18 17:55:40 -07:00
|
|
|
dir, err := ioutil.TempDir("", "tx_index_db")
|
2017-01-19 01:33:58 -08:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
2017-10-03 15:49:20 -07:00
|
|
|
defer os.RemoveAll(dir) // nolint: errcheck
|
2017-01-19 01:33:58 -08:00
|
|
|
|
2017-04-18 17:55:40 -07:00
|
|
|
store := db.NewDB("tx_index", "leveldb", dir)
|
2017-11-30 18:02:39 -08:00
|
|
|
indexer := NewTxIndex(store)
|
2017-01-19 01:33:58 -08:00
|
|
|
|
2017-04-18 17:23:58 -07:00
|
|
|
batch := txindex.NewBatch(txsCount)
|
2017-01-19 01:33:58 -08:00
|
|
|
for i := 0; i < txsCount; i++ {
|
2017-11-26 17:16:21 -08:00
|
|
|
if err := batch.Add(txResult); err != nil {
|
2017-09-06 08:50:43 -07:00
|
|
|
b.Fatal(err)
|
|
|
|
}
|
2017-06-27 13:05:21 -07:00
|
|
|
txResult.Index += 1
|
2017-01-19 01:33:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
|
|
|
for n := 0; n < b.N; n++ {
|
2017-11-28 16:58:39 -08:00
|
|
|
err = indexer.AddBatch(batch)
|
2017-06-27 13:05:21 -07:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
2017-01-19 01:33:58 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-18 16:29:02 -07:00
|
|
|
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) }
|