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.

564 lines
15 KiB

  1. package statesync
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. "github.com/tendermint/tendermint/types"
  9. )
  10. func setupChunkQueue(t *testing.T) (*chunkQueue, func()) {
  11. snapshot := &snapshot{
  12. Height: 3,
  13. Format: 1,
  14. Chunks: 5,
  15. Hash: []byte{7},
  16. Metadata: nil,
  17. }
  18. queue, err := newChunkQueue(snapshot, "")
  19. require.NoError(t, err)
  20. teardown := func() {
  21. err := queue.Close()
  22. require.NoError(t, err)
  23. }
  24. return queue, teardown
  25. }
  26. func TestNewChunkQueue_TempDir(t *testing.T) {
  27. snapshot := &snapshot{
  28. Height: 3,
  29. Format: 1,
  30. Chunks: 5,
  31. Hash: []byte{7},
  32. Metadata: nil,
  33. }
  34. dir, err := ioutil.TempDir("", "newchunkqueue")
  35. require.NoError(t, err)
  36. defer os.RemoveAll(dir)
  37. queue, err := newChunkQueue(snapshot, dir)
  38. require.NoError(t, err)
  39. files, err := ioutil.ReadDir(dir)
  40. require.NoError(t, err)
  41. assert.Len(t, files, 1)
  42. err = queue.Close()
  43. require.NoError(t, err)
  44. files, err = ioutil.ReadDir(dir)
  45. require.NoError(t, err)
  46. assert.Len(t, files, 0)
  47. }
  48. func TestChunkQueue(t *testing.T) {
  49. queue, teardown := setupChunkQueue(t)
  50. defer teardown()
  51. // Adding the first chunk should be fine
  52. added, err := queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}})
  53. require.NoError(t, err)
  54. assert.True(t, added)
  55. // Adding the last chunk should also be fine
  56. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 4, Chunk: []byte{3, 1, 4}})
  57. require.NoError(t, err)
  58. assert.True(t, added)
  59. // Adding the first or last chunks again should return false
  60. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}})
  61. require.NoError(t, err)
  62. assert.False(t, added)
  63. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 4, Chunk: []byte{3, 1, 4}})
  64. require.NoError(t, err)
  65. assert.False(t, added)
  66. // Adding the remaining chunks in reverse should be fine
  67. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 3, Chunk: []byte{3, 1, 3}})
  68. require.NoError(t, err)
  69. assert.True(t, added)
  70. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 2, Chunk: []byte{3, 1, 2}})
  71. require.NoError(t, err)
  72. assert.True(t, added)
  73. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{3, 1, 1}})
  74. require.NoError(t, err)
  75. assert.True(t, added)
  76. // At this point, we should be able to retrieve them all via Next
  77. for i := 0; i < 5; i++ {
  78. c, err := queue.Next()
  79. require.NoError(t, err)
  80. assert.Equal(t, &chunk{Height: 3, Format: 1, Index: uint32(i), Chunk: []byte{3, 1, byte(i)}}, c)
  81. }
  82. _, err = queue.Next()
  83. require.Error(t, err)
  84. assert.Equal(t, errDone, err)
  85. // It should still be possible to try to add chunks (which will be ignored)
  86. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}})
  87. require.NoError(t, err)
  88. assert.False(t, added)
  89. // After closing the queue it will also return false
  90. err = queue.Close()
  91. require.NoError(t, err)
  92. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}})
  93. require.NoError(t, err)
  94. assert.False(t, added)
  95. // Closing the queue again should also be fine
  96. err = queue.Close()
  97. require.NoError(t, err)
  98. }
  99. func TestChunkQueue_Add_ChunkErrors(t *testing.T) {
  100. testcases := map[string]struct {
  101. chunk *chunk
  102. }{
  103. "nil chunk": {nil},
  104. "nil body": {&chunk{Height: 3, Format: 1, Index: 0, Chunk: nil}},
  105. "wrong height": {&chunk{Height: 9, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}}},
  106. "wrong format": {&chunk{Height: 3, Format: 9, Index: 0, Chunk: []byte{3, 1, 0}}},
  107. "invalid index": {&chunk{Height: 3, Format: 1, Index: 5, Chunk: []byte{3, 1, 0}}},
  108. }
  109. for name, tc := range testcases {
  110. tc := tc
  111. t.Run(name, func(t *testing.T) {
  112. queue, teardown := setupChunkQueue(t)
  113. defer teardown()
  114. _, err := queue.Add(tc.chunk)
  115. require.Error(t, err)
  116. })
  117. }
  118. }
  119. func TestChunkQueue_Allocate(t *testing.T) {
  120. queue, teardown := setupChunkQueue(t)
  121. defer teardown()
  122. for i := uint32(0); i < queue.Size(); i++ {
  123. index, err := queue.Allocate()
  124. require.NoError(t, err)
  125. assert.EqualValues(t, i, index)
  126. }
  127. _, err := queue.Allocate()
  128. require.Error(t, err)
  129. assert.Equal(t, errDone, err)
  130. for i := uint32(0); i < queue.Size(); i++ {
  131. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: i, Chunk: []byte{byte(i)}})
  132. require.NoError(t, err)
  133. }
  134. // After all chunks have been allocated and retrieved, discarding a chunk will reallocate it.
  135. err = queue.Discard(2)
  136. require.NoError(t, err)
  137. index, err := queue.Allocate()
  138. require.NoError(t, err)
  139. assert.EqualValues(t, 2, index)
  140. _, err = queue.Allocate()
  141. require.Error(t, err)
  142. assert.Equal(t, errDone, err)
  143. // Discarding a chunk the closing the queue will return errDone.
  144. err = queue.Discard(2)
  145. require.NoError(t, err)
  146. err = queue.Close()
  147. require.NoError(t, err)
  148. _, err = queue.Allocate()
  149. require.Error(t, err)
  150. assert.Equal(t, errDone, err)
  151. }
  152. func TestChunkQueue_Discard(t *testing.T) {
  153. queue, teardown := setupChunkQueue(t)
  154. defer teardown()
  155. // Add a few chunks to the queue and fetch a couple
  156. _, err := queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{byte(0)}})
  157. require.NoError(t, err)
  158. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{byte(1)}})
  159. require.NoError(t, err)
  160. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 2, Chunk: []byte{byte(2)}})
  161. require.NoError(t, err)
  162. c, err := queue.Next()
  163. require.NoError(t, err)
  164. assert.EqualValues(t, 0, c.Index)
  165. c, err = queue.Next()
  166. require.NoError(t, err)
  167. assert.EqualValues(t, 1, c.Index)
  168. // Discarding the first chunk and re-adding it should cause it to be returned
  169. // immediately by Next(), before procceeding with chunk 2
  170. err = queue.Discard(0)
  171. require.NoError(t, err)
  172. added, err := queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{byte(0)}})
  173. require.NoError(t, err)
  174. assert.True(t, added)
  175. c, err = queue.Next()
  176. require.NoError(t, err)
  177. assert.EqualValues(t, 0, c.Index)
  178. c, err = queue.Next()
  179. require.NoError(t, err)
  180. assert.EqualValues(t, 2, c.Index)
  181. // Discard then allocate, add and fetch all chunks
  182. for i := uint32(0); i < queue.Size(); i++ {
  183. err := queue.Discard(i)
  184. require.NoError(t, err)
  185. }
  186. for i := uint32(0); i < queue.Size(); i++ {
  187. _, err := queue.Allocate()
  188. require.NoError(t, err)
  189. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: i, Chunk: []byte{byte(i)}})
  190. require.NoError(t, err)
  191. c, err = queue.Next()
  192. require.NoError(t, err)
  193. assert.EqualValues(t, i, c.Index)
  194. }
  195. // Discarding a non-existent chunk does nothing.
  196. err = queue.Discard(99)
  197. require.NoError(t, err)
  198. // When discard a couple of chunks, we should be able to allocate, add, and fetch them again.
  199. err = queue.Discard(3)
  200. require.NoError(t, err)
  201. err = queue.Discard(1)
  202. require.NoError(t, err)
  203. index, err := queue.Allocate()
  204. require.NoError(t, err)
  205. assert.EqualValues(t, 1, index)
  206. index, err = queue.Allocate()
  207. require.NoError(t, err)
  208. assert.EqualValues(t, 3, index)
  209. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 3, Chunk: []byte{3}})
  210. require.NoError(t, err)
  211. assert.True(t, added)
  212. added, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{1}})
  213. require.NoError(t, err)
  214. assert.True(t, added)
  215. chunk, err := queue.Next()
  216. require.NoError(t, err)
  217. assert.EqualValues(t, 1, chunk.Index)
  218. chunk, err = queue.Next()
  219. require.NoError(t, err)
  220. assert.EqualValues(t, 3, chunk.Index)
  221. _, err = queue.Next()
  222. require.Error(t, err)
  223. assert.Equal(t, errDone, err)
  224. // After closing the queue, discarding does nothing
  225. err = queue.Close()
  226. require.NoError(t, err)
  227. err = queue.Discard(2)
  228. require.NoError(t, err)
  229. }
  230. func TestChunkQueue_DiscardSender(t *testing.T) {
  231. queue, teardown := setupChunkQueue(t)
  232. defer teardown()
  233. // Allocate and add all chunks to the queue
  234. senders := []types.NodeID{types.NodeID("a"), types.NodeID("b"), types.NodeID("c")}
  235. for i := uint32(0); i < queue.Size(); i++ {
  236. _, err := queue.Allocate()
  237. require.NoError(t, err)
  238. _, err = queue.Add(&chunk{
  239. Height: 3,
  240. Format: 1,
  241. Index: i,
  242. Chunk: []byte{byte(i)},
  243. Sender: senders[int(i)%len(senders)],
  244. })
  245. require.NoError(t, err)
  246. }
  247. // Fetch the first three chunks
  248. for i := uint32(0); i < 3; i++ {
  249. _, err := queue.Next()
  250. require.NoError(t, err)
  251. }
  252. // Discarding an unknown sender should do nothing
  253. err := queue.DiscardSender(types.NodeID("x"))
  254. require.NoError(t, err)
  255. _, err = queue.Allocate()
  256. assert.Equal(t, errDone, err)
  257. // Discarding sender b should discard chunk 4, but not chunk 1 which has already been
  258. // returned.
  259. err = queue.DiscardSender(types.NodeID("b"))
  260. require.NoError(t, err)
  261. index, err := queue.Allocate()
  262. require.NoError(t, err)
  263. assert.EqualValues(t, 4, index)
  264. _, err = queue.Allocate()
  265. assert.Equal(t, errDone, err)
  266. }
  267. func TestChunkQueue_GetSender(t *testing.T) {
  268. queue, teardown := setupChunkQueue(t)
  269. defer teardown()
  270. peerAID := types.NodeID("aa")
  271. peerBID := types.NodeID("bb")
  272. _, err := queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{1}, Sender: peerAID})
  273. require.NoError(t, err)
  274. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{2}, Sender: peerBID})
  275. require.NoError(t, err)
  276. assert.EqualValues(t, "aa", queue.GetSender(0))
  277. assert.EqualValues(t, "bb", queue.GetSender(1))
  278. assert.EqualValues(t, "", queue.GetSender(2))
  279. // After the chunk has been processed, we should still know who the sender was
  280. chunk, err := queue.Next()
  281. require.NoError(t, err)
  282. require.NotNil(t, chunk)
  283. require.EqualValues(t, 0, chunk.Index)
  284. assert.EqualValues(t, "aa", queue.GetSender(0))
  285. }
  286. func TestChunkQueue_Next(t *testing.T) {
  287. queue, teardown := setupChunkQueue(t)
  288. defer teardown()
  289. // Next should block waiting for the next chunks, even when given out of order.
  290. chNext := make(chan *chunk, 10)
  291. go func() {
  292. for {
  293. c, err := queue.Next()
  294. if err == errDone {
  295. close(chNext)
  296. break
  297. }
  298. require.NoError(t, err)
  299. chNext <- c
  300. }
  301. }()
  302. assert.Empty(t, chNext)
  303. _, err := queue.Add(&chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{3, 1, 1}, Sender: types.NodeID("b")})
  304. require.NoError(t, err)
  305. select {
  306. case <-chNext:
  307. assert.Fail(t, "channel should be empty")
  308. default:
  309. }
  310. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}, Sender: types.NodeID("a")})
  311. require.NoError(t, err)
  312. assert.Equal(t,
  313. &chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}, Sender: types.NodeID("a")},
  314. <-chNext)
  315. assert.Equal(t,
  316. &chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{3, 1, 1}, Sender: types.NodeID("b")},
  317. <-chNext)
  318. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 4, Chunk: []byte{3, 1, 4}, Sender: types.NodeID("e")})
  319. require.NoError(t, err)
  320. select {
  321. case <-chNext:
  322. assert.Fail(t, "channel should be empty")
  323. default:
  324. }
  325. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 2, Chunk: []byte{3, 1, 2}, Sender: types.NodeID("c")})
  326. require.NoError(t, err)
  327. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 3, Chunk: []byte{3, 1, 3}, Sender: types.NodeID("d")})
  328. require.NoError(t, err)
  329. assert.Equal(t,
  330. &chunk{Height: 3, Format: 1, Index: 2, Chunk: []byte{3, 1, 2}, Sender: types.NodeID("c")},
  331. <-chNext)
  332. assert.Equal(t,
  333. &chunk{Height: 3, Format: 1, Index: 3, Chunk: []byte{3, 1, 3}, Sender: types.NodeID("d")},
  334. <-chNext)
  335. assert.Equal(t,
  336. &chunk{Height: 3, Format: 1, Index: 4, Chunk: []byte{3, 1, 4}, Sender: types.NodeID("e")},
  337. <-chNext)
  338. _, ok := <-chNext
  339. assert.False(t, ok, "channel should be closed")
  340. // Calling next on a finished queue should return done
  341. _, err = queue.Next()
  342. assert.Equal(t, errDone, err)
  343. }
  344. func TestChunkQueue_Next_Closed(t *testing.T) {
  345. queue, teardown := setupChunkQueue(t)
  346. defer teardown()
  347. // Calling Next on a closed queue should return done
  348. _, err := queue.Add(&chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{3, 1, 1}})
  349. require.NoError(t, err)
  350. err = queue.Close()
  351. require.NoError(t, err)
  352. _, err = queue.Next()
  353. assert.Equal(t, errDone, err)
  354. }
  355. func TestChunkQueue_Retry(t *testing.T) {
  356. queue, teardown := setupChunkQueue(t)
  357. defer teardown()
  358. allocateAddChunksToQueue(t, queue)
  359. // Retrying a couple of chunks makes Next() return them, but they are not allocatable
  360. queue.Retry(3)
  361. queue.Retry(1)
  362. _, err := queue.Allocate()
  363. assert.Equal(t, errDone, err)
  364. chunk, err := queue.Next()
  365. require.NoError(t, err)
  366. assert.EqualValues(t, 1, chunk.Index)
  367. chunk, err = queue.Next()
  368. require.NoError(t, err)
  369. assert.EqualValues(t, 3, chunk.Index)
  370. _, err = queue.Next()
  371. assert.Equal(t, errDone, err)
  372. }
  373. func TestChunkQueue_RetryAll(t *testing.T) {
  374. queue, teardown := setupChunkQueue(t)
  375. defer teardown()
  376. allocateAddChunksToQueue(t, queue)
  377. _, err := queue.Next()
  378. assert.Equal(t, errDone, err)
  379. queue.RetryAll()
  380. _, err = queue.Allocate()
  381. assert.Equal(t, errDone, err)
  382. for i := uint32(0); i < queue.Size(); i++ {
  383. chunk, err := queue.Next()
  384. require.NoError(t, err)
  385. assert.EqualValues(t, i, chunk.Index)
  386. }
  387. _, err = queue.Next()
  388. assert.Equal(t, errDone, err)
  389. }
  390. func TestChunkQueue_Size(t *testing.T) {
  391. queue, teardown := setupChunkQueue(t)
  392. defer teardown()
  393. assert.EqualValues(t, 5, queue.Size())
  394. err := queue.Close()
  395. require.NoError(t, err)
  396. assert.EqualValues(t, 0, queue.Size())
  397. }
  398. func TestChunkQueue_WaitFor(t *testing.T) {
  399. queue, teardown := setupChunkQueue(t)
  400. defer teardown()
  401. waitFor1 := queue.WaitFor(1)
  402. waitFor4 := queue.WaitFor(4)
  403. // Adding 0 and 2 should not trigger waiters
  404. _, err := queue.Add(&chunk{Height: 3, Format: 1, Index: 0, Chunk: []byte{3, 1, 0}})
  405. require.NoError(t, err)
  406. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 2, Chunk: []byte{3, 1, 2}})
  407. require.NoError(t, err)
  408. select {
  409. case <-waitFor1:
  410. require.Fail(t, "WaitFor(1) should not trigger on 0 or 2")
  411. case <-waitFor4:
  412. require.Fail(t, "WaitFor(4) should not trigger on 0 or 2")
  413. default:
  414. }
  415. // Adding 1 should trigger WaitFor(1), but not WaitFor(4). The channel should be closed.
  416. _, err = queue.Add(&chunk{Height: 3, Format: 1, Index: 1, Chunk: []byte{3, 1, 1}})
  417. require.NoError(t, err)
  418. assert.EqualValues(t, 1, <-waitFor1)
  419. _, ok := <-waitFor1
  420. assert.False(t, ok)
  421. select {
  422. case <-waitFor4:
  423. require.Fail(t, "WaitFor(4) should not trigger on 0 or 2")
  424. default:
  425. }
  426. // Fetch the first chunk. At this point, waiting for either 0 (retrieved from pool) or 1
  427. // (queued in pool) should immediately return true.
  428. c, err := queue.Next()
  429. require.NoError(t, err)
  430. assert.EqualValues(t, 0, c.Index)
  431. w := queue.WaitFor(0)
  432. assert.EqualValues(t, 0, <-w)
  433. _, ok = <-w
  434. assert.False(t, ok)
  435. w = queue.WaitFor(1)
  436. assert.EqualValues(t, 1, <-w)
  437. _, ok = <-w
  438. assert.False(t, ok)
  439. // Close the queue. This should cause the waiter for 4 to close, and also cause any future
  440. // waiters to get closed channels.
  441. err = queue.Close()
  442. require.NoError(t, err)
  443. _, ok = <-waitFor4
  444. assert.False(t, ok)
  445. w = queue.WaitFor(3)
  446. _, ok = <-w
  447. assert.False(t, ok)
  448. }
  449. func TestNumChunkReturned(t *testing.T) {
  450. queue, teardown := setupChunkQueue(t)
  451. defer teardown()
  452. assert.EqualValues(t, 5, queue.Size())
  453. allocateAddChunksToQueue(t, queue)
  454. assert.EqualValues(t, 5, queue.numChunksReturned())
  455. err := queue.Close()
  456. require.NoError(t, err)
  457. }
  458. // Allocate and add all chunks to the queue
  459. func allocateAddChunksToQueue(t *testing.T, q *chunkQueue) {
  460. t.Helper()
  461. for i := uint32(0); i < q.Size(); i++ {
  462. _, err := q.Allocate()
  463. require.NoError(t, err)
  464. _, err = q.Add(&chunk{Height: 3, Format: 1, Index: i, Chunk: []byte{byte(i)}})
  465. require.NoError(t, err)
  466. _, err = q.Next()
  467. require.NoError(t, err)
  468. }
  469. }