You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
7.5 KiB

6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
6 years ago
6 years ago
6 years ago
  1. package kv
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "testing"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. abci "github.com/tendermint/tendermint/abci/types"
  10. cmn "github.com/tendermint/tendermint/libs/common"
  11. db "github.com/tendermint/tendermint/libs/db"
  12. "github.com/tendermint/tendermint/libs/pubsub/query"
  13. "github.com/tendermint/tendermint/state/txindex"
  14. "github.com/tendermint/tendermint/types"
  15. )
  16. func TestTxIndex(t *testing.T) {
  17. indexer := NewTxIndex(db.NewMemDB())
  18. tx := types.Tx("HELLO WORLD")
  19. txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: nil}}
  20. hash := tx.Hash()
  21. batch := txindex.NewBatch(1)
  22. if err := batch.Add(txResult); err != nil {
  23. t.Error(err)
  24. }
  25. err := indexer.AddBatch(batch)
  26. require.NoError(t, err)
  27. loadedTxResult, err := indexer.Get(hash)
  28. require.NoError(t, err)
  29. assert.Equal(t, txResult, loadedTxResult)
  30. tx2 := types.Tx("BYE BYE WORLD")
  31. txResult2 := &types.TxResult{1, 0, tx2, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: nil}}
  32. hash2 := tx2.Hash()
  33. err = indexer.Index(txResult2)
  34. require.NoError(t, err)
  35. loadedTxResult2, err := indexer.Get(hash2)
  36. require.NoError(t, err)
  37. assert.Equal(t, txResult2, loadedTxResult2)
  38. }
  39. func TestTxSearch(t *testing.T) {
  40. allowedTags := []string{"account.number", "account.owner", "account.date"}
  41. indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
  42. txResult := txResultWithTags([]cmn.KVPair{
  43. {Key: []byte("account.number"), Value: []byte("1")},
  44. {Key: []byte("account.owner"), Value: []byte("Ivan")},
  45. {Key: []byte("not_allowed"), Value: []byte("Vlad")},
  46. })
  47. hash := txResult.Tx.Hash()
  48. err := indexer.Index(txResult)
  49. require.NoError(t, err)
  50. testCases := []struct {
  51. q string
  52. resultsLength int
  53. }{
  54. // search by hash
  55. {fmt.Sprintf("tx.hash = '%X'", hash), 1},
  56. // search by exact match (one tag)
  57. {"account.number = 1", 1},
  58. // search by exact match (two tags)
  59. {"account.number = 1 AND account.owner = 'Ivan'", 1},
  60. // search by exact match (two tags)
  61. {"account.number = 1 AND account.owner = 'Vlad'", 0},
  62. // search using a prefix of the stored value
  63. {"account.owner = 'Iv'", 0},
  64. // search by range
  65. {"account.number >= 1 AND account.number <= 5", 1},
  66. // search by range (lower bound)
  67. {"account.number >= 1", 1},
  68. // search by range (upper bound)
  69. {"account.number <= 5", 1},
  70. // search using not allowed tag
  71. {"not_allowed = 'boom'", 0},
  72. // search for not existing tx result
  73. {"account.number >= 2 AND account.number <= 5", 0},
  74. // search using not existing tag
  75. {"account.date >= TIME 2013-05-03T14:45:00Z", 0},
  76. // search using CONTAINS
  77. {"account.owner CONTAINS 'an'", 1},
  78. // search for non existing value using CONTAINS
  79. {"account.owner CONTAINS 'Vlad'", 0},
  80. // search using the wrong tag (of numeric type) using CONTAINS
  81. {"account.number CONTAINS 'Iv'", 0},
  82. }
  83. for _, tc := range testCases {
  84. t.Run(tc.q, func(t *testing.T) {
  85. results, err := indexer.Search(query.MustParse(tc.q))
  86. assert.NoError(t, err)
  87. assert.Len(t, results, tc.resultsLength)
  88. if tc.resultsLength > 0 {
  89. assert.Equal(t, []*types.TxResult{txResult}, results)
  90. }
  91. })
  92. }
  93. }
  94. func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) {
  95. allowedTags := []string{"account.number"}
  96. indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
  97. txResult := txResultWithTags([]cmn.KVPair{
  98. {Key: []byte("account.number"), Value: []byte("1")},
  99. {Key: []byte("account.number"), Value: []byte("2")},
  100. })
  101. err := indexer.Index(txResult)
  102. require.NoError(t, err)
  103. results, err := indexer.Search(query.MustParse("account.number >= 1"))
  104. assert.NoError(t, err)
  105. assert.Len(t, results, 1)
  106. assert.Equal(t, []*types.TxResult{txResult}, results)
  107. }
  108. func TestTxSearchMultipleTxs(t *testing.T) {
  109. allowedTags := []string{"account.number", "account.number.id"}
  110. indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
  111. // indexed first, but bigger height (to test the order of transactions)
  112. txResult := txResultWithTags([]cmn.KVPair{
  113. {Key: []byte("account.number"), Value: []byte("1")},
  114. })
  115. txResult.Tx = types.Tx("Bob's account")
  116. txResult.Height = 2
  117. txResult.Index = 1
  118. err := indexer.Index(txResult)
  119. require.NoError(t, err)
  120. // indexed second, but smaller height (to test the order of transactions)
  121. txResult2 := txResultWithTags([]cmn.KVPair{
  122. {Key: []byte("account.number"), Value: []byte("2")},
  123. })
  124. txResult2.Tx = types.Tx("Alice's account")
  125. txResult2.Height = 1
  126. txResult2.Index = 2
  127. err = indexer.Index(txResult2)
  128. require.NoError(t, err)
  129. // indexed third (to test the order of transactions)
  130. txResult3 := txResultWithTags([]cmn.KVPair{
  131. {Key: []byte("account.number"), Value: []byte("3")},
  132. })
  133. txResult3.Tx = types.Tx("Jack's account")
  134. txResult3.Height = 1
  135. txResult3.Index = 1
  136. err = indexer.Index(txResult3)
  137. require.NoError(t, err)
  138. // indexed fourth (to test we don't include txs with similar tags)
  139. // https://github.com/tendermint/tendermint/issues/2908
  140. txResult4 := txResultWithTags([]cmn.KVPair{
  141. {Key: []byte("account.number.id"), Value: []byte("1")},
  142. })
  143. txResult4.Tx = types.Tx("Mike's account")
  144. txResult4.Height = 2
  145. txResult4.Index = 2
  146. err = indexer.Index(txResult4)
  147. require.NoError(t, err)
  148. results, err := indexer.Search(query.MustParse("account.number >= 1"))
  149. assert.NoError(t, err)
  150. require.Len(t, results, 3)
  151. assert.Equal(t, []*types.TxResult{txResult3, txResult2, txResult}, results)
  152. }
  153. func TestIndexAllTags(t *testing.T) {
  154. indexer := NewTxIndex(db.NewMemDB(), IndexAllTags())
  155. txResult := txResultWithTags([]cmn.KVPair{
  156. {Key: []byte("account.owner"), Value: []byte("Ivan")},
  157. {Key: []byte("account.number"), Value: []byte("1")},
  158. })
  159. err := indexer.Index(txResult)
  160. require.NoError(t, err)
  161. results, err := indexer.Search(query.MustParse("account.number >= 1"))
  162. assert.NoError(t, err)
  163. assert.Len(t, results, 1)
  164. assert.Equal(t, []*types.TxResult{txResult}, results)
  165. results, err = indexer.Search(query.MustParse("account.owner = 'Ivan'"))
  166. assert.NoError(t, err)
  167. assert.Len(t, results, 1)
  168. assert.Equal(t, []*types.TxResult{txResult}, results)
  169. }
  170. func txResultWithTags(tags []cmn.KVPair) *types.TxResult {
  171. tx := types.Tx("HELLO WORLD")
  172. return &types.TxResult{
  173. Height: 1,
  174. Index: 0,
  175. Tx: tx,
  176. Result: abci.ResponseDeliverTx{
  177. Data: []byte{0},
  178. Code: abci.CodeTypeOK,
  179. Log: "",
  180. Tags: tags,
  181. },
  182. }
  183. }
  184. func benchmarkTxIndex(txsCount int64, b *testing.B) {
  185. dir, err := ioutil.TempDir("", "tx_index_db")
  186. if err != nil {
  187. b.Fatal(err)
  188. }
  189. defer os.RemoveAll(dir) // nolint: errcheck
  190. store := db.NewDB("tx_index", "leveldb", dir)
  191. indexer := NewTxIndex(store)
  192. batch := txindex.NewBatch(txsCount)
  193. txIndex := uint32(0)
  194. for i := int64(0); i < txsCount; i++ {
  195. tx := cmn.RandBytes(250)
  196. txResult := &types.TxResult{
  197. Height: 1,
  198. Index: txIndex,
  199. Tx: tx,
  200. Result: abci.ResponseDeliverTx{
  201. Data: []byte{0},
  202. Code: abci.CodeTypeOK,
  203. Log: "",
  204. Tags: []cmn.KVPair{},
  205. },
  206. }
  207. if err := batch.Add(txResult); err != nil {
  208. b.Fatal(err)
  209. }
  210. txIndex++
  211. }
  212. b.ResetTimer()
  213. for n := 0; n < b.N; n++ {
  214. err = indexer.AddBatch(batch)
  215. }
  216. if err != nil {
  217. b.Fatal(err)
  218. }
  219. }
  220. func BenchmarkTxIndex1(b *testing.B) { benchmarkTxIndex(1, b) }
  221. func BenchmarkTxIndex500(b *testing.B) { benchmarkTxIndex(500, b) }
  222. func BenchmarkTxIndex1000(b *testing.B) { benchmarkTxIndex(1000, b) }
  223. func BenchmarkTxIndex2000(b *testing.B) { benchmarkTxIndex(2000, b) }
  224. func BenchmarkTxIndex10000(b *testing.B) { benchmarkTxIndex(10000, b) }