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.

365 lines
11 KiB

  1. package lite
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "sync"
  6. "testing"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. liteErr "github.com/tendermint/tendermint/lite/errors"
  10. )
  11. func TestMemStoreProvidergetByHeightBinaryAndLinearSameResult(t *testing.T) {
  12. p := NewMemStoreProvider().(*memStoreProvider)
  13. // Store a bunch of commits at specific heights
  14. // and then ensure that:
  15. // * getByHeightLinearSearch
  16. // * getByHeightBinarySearch
  17. // both return the exact same result
  18. // 1. Non-existent height commits
  19. nonExistent := []int64{-1000, -1, 0, 1, 10, 11, 17, 31, 67, 1000, 1e9}
  20. ensureNonExistentCommitsAtHeight(t, "getByHeightLinearSearch", p.getByHeightLinearSearch, nonExistent)
  21. ensureNonExistentCommitsAtHeight(t, "getByHeightBinarySearch", p.getByHeightBinarySearch, nonExistent)
  22. // 2. Save some known height commits
  23. knownHeights := []int64{0, 1, 7, 9, 12, 13, 18, 44, 23, 16, 1024, 100, 199, 1e9}
  24. createAndStoreCommits(t, p, knownHeights)
  25. // 3. Now check if those heights are retrieved
  26. ensureExistentCommitsAtHeight(t, "getByHeightLinearSearch", p.getByHeightLinearSearch, knownHeights)
  27. ensureExistentCommitsAtHeight(t, "getByHeightBinarySearch", p.getByHeightBinarySearch, knownHeights)
  28. // 4. And now for the height probing to ensure that any height
  29. // requested returns a fullCommit of height <= requestedHeight.
  30. comparegetByHeightAlgorithms(t, p, 0, 0)
  31. comparegetByHeightAlgorithms(t, p, 1, 1)
  32. comparegetByHeightAlgorithms(t, p, 2, 1)
  33. comparegetByHeightAlgorithms(t, p, 5, 1)
  34. comparegetByHeightAlgorithms(t, p, 7, 7)
  35. comparegetByHeightAlgorithms(t, p, 10, 9)
  36. comparegetByHeightAlgorithms(t, p, 12, 12)
  37. comparegetByHeightAlgorithms(t, p, 14, 13)
  38. comparegetByHeightAlgorithms(t, p, 19, 18)
  39. comparegetByHeightAlgorithms(t, p, 43, 23)
  40. comparegetByHeightAlgorithms(t, p, 45, 44)
  41. comparegetByHeightAlgorithms(t, p, 1025, 1024)
  42. comparegetByHeightAlgorithms(t, p, 101, 100)
  43. comparegetByHeightAlgorithms(t, p, 1e3, 199)
  44. comparegetByHeightAlgorithms(t, p, 1e4, 1024)
  45. comparegetByHeightAlgorithms(t, p, 1e9, 1e9)
  46. comparegetByHeightAlgorithms(t, p, 1e9+1, 1e9)
  47. }
  48. func createAndStoreCommits(t *testing.T, p Provider, heights []int64) {
  49. chainID := "cache-best-height-binary-and-linear"
  50. appHash := []byte("0xdeadbeef")
  51. keys := GenValKeys(len(heights) / 2)
  52. for _, h := range heights {
  53. vals := keys.ToValidators(10, int64(len(heights)/2))
  54. fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
  55. err := p.StoreCommit(fc)
  56. require.NoError(t, err, "StoreCommit height=%d", h)
  57. }
  58. }
  59. func comparegetByHeightAlgorithms(t *testing.T, p *memStoreProvider, ask, expect int64) {
  60. algos := map[string]func(int64) (FullCommit, error){
  61. "getHeightByLinearSearch": p.getByHeightLinearSearch,
  62. "getHeightByBinarySearch": p.getByHeightBinarySearch,
  63. }
  64. for algo, fn := range algos {
  65. fc, err := fn(ask)
  66. // t.Logf("%s got=%v want=%d", algo, expect, fc.Height())
  67. require.Nil(t, err, "%s: %+v", algo, err)
  68. if assert.Equal(t, expect, fc.Height()) {
  69. err = p.StoreCommit(fc)
  70. require.Nil(t, err, "%s: %+v", algo, err)
  71. }
  72. }
  73. }
  74. var blankFullCommit FullCommit
  75. func ensureNonExistentCommitsAtHeight(t *testing.T, prefix string, fn func(int64) (FullCommit, error), data []int64) {
  76. for i, qh := range data {
  77. fc, err := fn(qh)
  78. assert.NotNil(t, err, "#%d: %s: height=%d should return non-nil error", i, prefix, qh)
  79. assert.Equal(t, fc, blankFullCommit, "#%d: %s: height=%d\ngot =%+v\nwant=%+v", i, prefix, qh, fc, blankFullCommit)
  80. }
  81. }
  82. func ensureExistentCommitsAtHeight(t *testing.T, prefix string, fn func(int64) (FullCommit, error), data []int64) {
  83. for i, qh := range data {
  84. fc, err := fn(qh)
  85. assert.Nil(t, err, "#%d: %s: height=%d should not return an error: %v", i, prefix, qh, err)
  86. assert.NotEqual(t, fc, blankFullCommit, "#%d: %s: height=%d got a blankCommit", i, prefix, qh)
  87. }
  88. }
  89. func BenchmarkGenCommit20(b *testing.B) {
  90. keys := GenValKeys(20)
  91. benchmarkGenCommit(b, keys)
  92. }
  93. func BenchmarkGenCommit100(b *testing.B) {
  94. keys := GenValKeys(100)
  95. benchmarkGenCommit(b, keys)
  96. }
  97. func BenchmarkGenCommitSec20(b *testing.B) {
  98. keys := GenSecpValKeys(20)
  99. benchmarkGenCommit(b, keys)
  100. }
  101. func BenchmarkGenCommitSec100(b *testing.B) {
  102. keys := GenSecpValKeys(100)
  103. benchmarkGenCommit(b, keys)
  104. }
  105. func benchmarkGenCommit(b *testing.B, keys ValKeys) {
  106. chainID := fmt.Sprintf("bench-%d", len(keys))
  107. vals := keys.ToValidators(20, 10)
  108. for i := 0; i < b.N; i++ {
  109. h := int64(1 + i)
  110. appHash := []byte(fmt.Sprintf("h=%d", h))
  111. resHash := []byte(fmt.Sprintf("res=%d", h))
  112. keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), resHash, 0, len(keys))
  113. }
  114. }
  115. // this benchmarks generating one key
  116. func BenchmarkGenValKeys(b *testing.B) {
  117. keys := GenValKeys(20)
  118. for i := 0; i < b.N; i++ {
  119. keys = keys.Extend(1)
  120. }
  121. }
  122. // this benchmarks generating one key
  123. func BenchmarkGenSecpValKeys(b *testing.B) {
  124. keys := GenSecpValKeys(20)
  125. for i := 0; i < b.N; i++ {
  126. keys = keys.Extend(1)
  127. }
  128. }
  129. func BenchmarkToValidators20(b *testing.B) {
  130. benchmarkToValidators(b, 20)
  131. }
  132. func BenchmarkToValidators100(b *testing.B) {
  133. benchmarkToValidators(b, 100)
  134. }
  135. // this benchmarks constructing the validator set (.PubKey() * nodes)
  136. func benchmarkToValidators(b *testing.B, nodes int) {
  137. keys := GenValKeys(nodes)
  138. for i := 1; i <= b.N; i++ {
  139. keys.ToValidators(int64(2*i), int64(i))
  140. }
  141. }
  142. func BenchmarkToValidatorsSec100(b *testing.B) {
  143. benchmarkToValidatorsSec(b, 100)
  144. }
  145. // this benchmarks constructing the validator set (.PubKey() * nodes)
  146. func benchmarkToValidatorsSec(b *testing.B, nodes int) {
  147. keys := GenSecpValKeys(nodes)
  148. for i := 1; i <= b.N; i++ {
  149. keys.ToValidators(int64(2*i), int64(i))
  150. }
  151. }
  152. func BenchmarkCertifyCommit20(b *testing.B) {
  153. keys := GenValKeys(20)
  154. benchmarkCertifyCommit(b, keys)
  155. }
  156. func BenchmarkCertifyCommit100(b *testing.B) {
  157. keys := GenValKeys(100)
  158. benchmarkCertifyCommit(b, keys)
  159. }
  160. func BenchmarkCertifyCommitSec20(b *testing.B) {
  161. keys := GenSecpValKeys(20)
  162. benchmarkCertifyCommit(b, keys)
  163. }
  164. func BenchmarkCertifyCommitSec100(b *testing.B) {
  165. keys := GenSecpValKeys(100)
  166. benchmarkCertifyCommit(b, keys)
  167. }
  168. func benchmarkCertifyCommit(b *testing.B, keys ValKeys) {
  169. chainID := "bench-certify"
  170. vals := keys.ToValidators(20, 10)
  171. cert := NewStaticCertifier(chainID, vals)
  172. check := keys.GenCommit(chainID, 123, nil, vals, []byte("foo"), []byte("params"), []byte("res"), 0, len(keys))
  173. for i := 0; i < b.N; i++ {
  174. err := cert.Certify(check)
  175. if err != nil {
  176. panic(err)
  177. }
  178. }
  179. }
  180. type algo bool
  181. const (
  182. linearSearch = true
  183. binarySearch = false
  184. )
  185. // Lazy load the commits
  186. var fcs5, fcs50, fcs100, fcs500, fcs1000 []FullCommit
  187. var h5, h50, h100, h500, h1000 []int64
  188. var commitsOnce sync.Once
  189. func lazyGenerateFullCommits(b *testing.B) {
  190. b.Logf("Generating FullCommits")
  191. commitsOnce.Do(func() {
  192. fcs5, h5 = genFullCommits(nil, nil, 5)
  193. b.Logf("Generated 5 FullCommits")
  194. fcs50, h50 = genFullCommits(fcs5, h5, 50)
  195. b.Logf("Generated 50 FullCommits")
  196. fcs100, h100 = genFullCommits(fcs50, h50, 100)
  197. b.Logf("Generated 100 FullCommits")
  198. fcs500, h500 = genFullCommits(fcs100, h100, 500)
  199. b.Logf("Generated 500 FullCommits")
  200. fcs1000, h1000 = genFullCommits(fcs500, h500, 1000)
  201. b.Logf("Generated 1000 FullCommits")
  202. })
  203. }
  204. func BenchmarkMemStoreProviderGetByHeightLinearSearch5(b *testing.B) {
  205. benchmarkMemStoreProvidergetByHeight(b, fcs5, h5, linearSearch)
  206. }
  207. func BenchmarkMemStoreProviderGetByHeightLinearSearch50(b *testing.B) {
  208. benchmarkMemStoreProvidergetByHeight(b, fcs50, h50, linearSearch)
  209. }
  210. func BenchmarkMemStoreProviderGetByHeightLinearSearch100(b *testing.B) {
  211. benchmarkMemStoreProvidergetByHeight(b, fcs100, h100, linearSearch)
  212. }
  213. func BenchmarkMemStoreProviderGetByHeightLinearSearch500(b *testing.B) {
  214. benchmarkMemStoreProvidergetByHeight(b, fcs500, h500, linearSearch)
  215. }
  216. func BenchmarkMemStoreProviderGetByHeightLinearSearch1000(b *testing.B) {
  217. benchmarkMemStoreProvidergetByHeight(b, fcs1000, h1000, linearSearch)
  218. }
  219. func BenchmarkMemStoreProviderGetByHeightBinarySearch5(b *testing.B) {
  220. benchmarkMemStoreProvidergetByHeight(b, fcs5, h5, binarySearch)
  221. }
  222. func BenchmarkMemStoreProviderGetByHeightBinarySearch50(b *testing.B) {
  223. benchmarkMemStoreProvidergetByHeight(b, fcs50, h50, binarySearch)
  224. }
  225. func BenchmarkMemStoreProviderGetByHeightBinarySearch100(b *testing.B) {
  226. benchmarkMemStoreProvidergetByHeight(b, fcs100, h100, binarySearch)
  227. }
  228. func BenchmarkMemStoreProviderGetByHeightBinarySearch500(b *testing.B) {
  229. benchmarkMemStoreProvidergetByHeight(b, fcs500, h500, binarySearch)
  230. }
  231. func BenchmarkMemStoreProviderGetByHeightBinarySearch1000(b *testing.B) {
  232. benchmarkMemStoreProvidergetByHeight(b, fcs1000, h1000, binarySearch)
  233. }
  234. var rng = rand.New(rand.NewSource(10))
  235. func benchmarkMemStoreProvidergetByHeight(b *testing.B, fcs []FullCommit, fHeights []int64, algo algo) {
  236. lazyGenerateFullCommits(b)
  237. b.StopTimer()
  238. mp := NewMemStoreProvider()
  239. for i, fc := range fcs {
  240. if err := mp.StoreCommit(fc); err != nil {
  241. b.Fatalf("FullCommit #%d: err: %v", i, err)
  242. }
  243. }
  244. qHeights := make([]int64, len(fHeights))
  245. copy(qHeights, fHeights)
  246. // Append some non-existent heights to trigger the worst cases.
  247. qHeights = append(qHeights, 19, -100, -10000, 1e7, -17, 31, -1e9)
  248. memP := mp.(*memStoreProvider)
  249. searchFn := memP.getByHeightLinearSearch
  250. if algo == binarySearch { // nolint
  251. searchFn = memP.getByHeightBinarySearch
  252. }
  253. hPerm := rng.Perm(len(qHeights))
  254. b.StartTimer()
  255. b.ResetTimer()
  256. for i := 0; i < b.N; i++ {
  257. for _, j := range hPerm {
  258. h := qHeights[j]
  259. if _, err := searchFn(h); err != nil {
  260. }
  261. }
  262. }
  263. b.ReportAllocs()
  264. }
  265. func genFullCommits(prevFC []FullCommit, prevH []int64, want int) ([]FullCommit, []int64) {
  266. fcs := make([]FullCommit, len(prevFC))
  267. copy(fcs, prevFC)
  268. heights := make([]int64, len(prevH))
  269. copy(heights, prevH)
  270. appHash := []byte("benchmarks")
  271. chainID := "benchmarks-gen-full-commits"
  272. n := want
  273. keys := GenValKeys(2 + (n / 3))
  274. for i := 0; i < n; i++ {
  275. vals := keys.ToValidators(10, int64(n/2))
  276. h := int64(20 + 10*i)
  277. fcs = append(fcs, keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5))
  278. heights = append(heights, h)
  279. }
  280. return fcs, heights
  281. }
  282. func TestMemStoreProviderLatestCommitAlwaysUsesSorted(t *testing.T) {
  283. p := NewMemStoreProvider().(*memStoreProvider)
  284. // 1. With no commits yet stored, it should return ErrCommitNotFound
  285. got, err := p.LatestCommit()
  286. require.Equal(t, err.Error(), liteErr.ErrCommitNotFound().Error(), "should return ErrCommitNotFound()")
  287. require.Equal(t, got, blankFullCommit, "With no fullcommits, it should return a blank FullCommit")
  288. // 2. Generate some full commits now and we'll add them unsorted.
  289. genAndStoreCommitsOfHeight(t, p, 27, 100, 1, 12, 1000, 17, 91)
  290. fc, err := p.LatestCommit()
  291. require.Nil(t, err, "with commits saved no error expected")
  292. require.NotEqual(t, fc, blankFullCommit, "with commits saved no blank FullCommit")
  293. require.Equal(t, fc.Height(), int64(1000), "the latest commit i.e. the largest expected")
  294. }
  295. func genAndStoreCommitsOfHeight(t *testing.T, p Provider, heights ...int64) {
  296. n := len(heights)
  297. appHash := []byte("tests")
  298. chainID := "tests-gen-full-commits"
  299. keys := GenValKeys(2 + (n / 3))
  300. for i := 0; i < n; i++ {
  301. h := heights[i]
  302. vals := keys.ToValidators(10, int64(n/2))
  303. fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
  304. err := p.StoreCommit(fc)
  305. require.NoError(t, err, "StoreCommit height=%d", h)
  306. }
  307. }