log_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package raft
  2. import (
  3. "reflect"
  4. "testing"
  5. )
  6. // TestAppend ensures:
  7. // 1. If an existing entry conflicts with a new one (same index
  8. // but different terms), delete the existing entry and all that
  9. // follow it
  10. // 2.Append any new entries not already in the log
  11. func TestAppend(t *testing.T) {
  12. previousEnts := []Entry{{Term: 1}, {Term: 2}}
  13. previousUnstable := int64(3)
  14. tests := []struct {
  15. after int64
  16. ents []Entry
  17. windex int64
  18. wents []Entry
  19. wunstable int64
  20. }{
  21. {
  22. 2,
  23. []Entry{},
  24. 2,
  25. []Entry{{Term: 1}, {Term: 2}},
  26. 3,
  27. },
  28. {
  29. 2,
  30. []Entry{{Term: 2}},
  31. 3,
  32. []Entry{{Term: 1}, {Term: 2}, {Term: 2}},
  33. 3,
  34. },
  35. // conflicts with index 1
  36. {
  37. 0,
  38. []Entry{{Term: 2}},
  39. 1,
  40. []Entry{{Term: 2}},
  41. 1,
  42. },
  43. // conflicts with index 2
  44. {
  45. 1,
  46. []Entry{{Term: 3}, {Term: 3}},
  47. 3,
  48. []Entry{{Term: 1}, {Term: 3}, {Term: 3}},
  49. 2,
  50. },
  51. }
  52. for i, tt := range tests {
  53. raftLog := newLog()
  54. raftLog.ents = append(raftLog.ents, previousEnts...)
  55. raftLog.unstable = previousUnstable
  56. index := raftLog.append(tt.after, tt.ents...)
  57. if index != tt.windex {
  58. t.Errorf("#%d: lastIndex = %d, want %d", i, index, tt.windex)
  59. }
  60. if g := raftLog.entries(1); !reflect.DeepEqual(g, tt.wents) {
  61. t.Errorf("#%d: logEnts = %+v, want %+v", i, g, tt.wents)
  62. }
  63. if g := raftLog.unstable; g != tt.wunstable {
  64. t.Errorf("#%d: unstable = %d, want %d", i, g, tt.wunstable)
  65. }
  66. }
  67. }
  68. // TestCompactionSideEffects ensures that all the log related funcationality works correctly after
  69. // a compaction.
  70. func TestCompactionSideEffects(t *testing.T) {
  71. var i int64
  72. lastIndex := int64(1000)
  73. raftLog := newLog()
  74. for i = 0; i < lastIndex; i++ {
  75. raftLog.append(int64(i), Entry{Term: int64(i + 1)})
  76. }
  77. raftLog.compact(500)
  78. if raftLog.lastIndex() != lastIndex {
  79. t.Errorf("lastIndex = %d, want %d", raftLog.lastIndex(), lastIndex)
  80. }
  81. for i := raftLog.offset; i <= raftLog.lastIndex(); i++ {
  82. if raftLog.term(i) != i {
  83. t.Errorf("term(%d) = %d, want %d", i, raftLog.term(i), i)
  84. }
  85. }
  86. for i := raftLog.offset; i <= raftLog.lastIndex(); i++ {
  87. if !raftLog.matchTerm(i, i) {
  88. t.Errorf("matchTerm(%d) = false, want true", i)
  89. }
  90. }
  91. offset, unstableEnts := raftLog.unstableEnts()
  92. if offset != 501 {
  93. t.Errorf("offset(unstableEntries) = %d, want = %d", offset, 500)
  94. }
  95. if g := len(unstableEnts); g != 500 {
  96. t.Errorf("len(unstableEntries) = %d, want = %d", g, 500)
  97. }
  98. prev := raftLog.lastIndex()
  99. raftLog.append(raftLog.lastIndex(), Entry{Term: raftLog.lastIndex() + 1})
  100. if raftLog.lastIndex() != prev+1 {
  101. t.Errorf("lastIndex = %d, want = %d", raftLog.lastIndex(), prev+1)
  102. }
  103. ents := raftLog.entries(raftLog.lastIndex())
  104. if len(ents) != 1 {
  105. t.Errorf("len(entries) = %d, want = %d", len(ents), 1)
  106. }
  107. }
  108. func TestUnstableEnts(t *testing.T) {
  109. previousEnts := []Entry{{Term: 1}, {Term: 2}}
  110. tests := []struct {
  111. unstable int64
  112. woffset int64
  113. wents []Entry
  114. wunstable int64
  115. }{
  116. {3, 3, nil, 3},
  117. {1, 1, []Entry{{Term: 1}, {Term: 2}}, 3},
  118. }
  119. for i, tt := range tests {
  120. raftLog := newLog()
  121. raftLog.ents = append(raftLog.ents, previousEnts...)
  122. raftLog.unstable = tt.unstable
  123. offset, ents := raftLog.unstableEnts()
  124. if offset != tt.woffset {
  125. t.Errorf("#%d: offset = %d, want = %d", i, offset, tt.woffset)
  126. }
  127. if !reflect.DeepEqual(ents, tt.wents) {
  128. t.Errorf("#%d: unstableEnts = %+v, want %+v", i, ents, tt.wents)
  129. }
  130. if g := raftLog.unstable; g != tt.wunstable {
  131. t.Errorf("#%d: unstable = %d, want %d", i, g, tt.wunstable)
  132. }
  133. }
  134. }
  135. //TestCompaction ensures that the number of log entreis is correct after compactions.
  136. func TestCompaction(t *testing.T) {
  137. tests := []struct {
  138. app int
  139. compact []int64
  140. wleft []int
  141. wallow bool
  142. }{
  143. // out of upper bound
  144. {1000, []int64{1001}, []int{-1}, false},
  145. {1000, []int64{300, 500, 800, 900}, []int{701, 501, 201, 101}, true},
  146. // out of lower bound
  147. {1000, []int64{300, 299}, []int{701, -1}, false},
  148. }
  149. for i, tt := range tests {
  150. func() {
  151. defer func() {
  152. if r := recover(); r != nil {
  153. if tt.wallow == true {
  154. t.Errorf("%d: allow = %v, want %v", i, false, true)
  155. }
  156. }
  157. }()
  158. raftLog := newLog()
  159. for i := 0; i < tt.app; i++ {
  160. raftLog.append(int64(i), Entry{})
  161. }
  162. for j := 0; j < len(tt.compact); j++ {
  163. raftLog.compact(tt.compact[j])
  164. if len(raftLog.ents) != tt.wleft[j] {
  165. t.Errorf("#%d.%d len = %d, want %d", i, j, len(raftLog.ents), tt.wleft[j])
  166. }
  167. }
  168. }()
  169. }
  170. }
  171. func TestLogRestore(t *testing.T) {
  172. var i int64
  173. raftLog := newLog()
  174. for i = 0; i < 100; i++ {
  175. raftLog.append(i, Entry{Term: i + 1})
  176. }
  177. index := int64(1000)
  178. term := int64(1000)
  179. raftLog.restore(index, term)
  180. // only has the guard entry
  181. if len(raftLog.ents) != 1 {
  182. t.Errorf("len = %d, want 0", len(raftLog.ents))
  183. }
  184. if raftLog.offset != index {
  185. t.Errorf("offset = %d, want %d", raftLog.offset, index)
  186. }
  187. if raftLog.applied != index {
  188. t.Errorf("applied = %d, want %d", raftLog.applied, index)
  189. }
  190. if raftLog.committed != index {
  191. t.Errorf("comitted = %d, want %d", raftLog.committed, index)
  192. }
  193. if raftLog.unstable != index+1 {
  194. t.Errorf("unstable = %d, want %d", raftLog.unstable, index+1)
  195. }
  196. if raftLog.term(index) != term {
  197. t.Errorf("term = %d, want %d", raftLog.term(index), term)
  198. }
  199. }
  200. func TestIsOutOfBounds(t *testing.T) {
  201. offset := int64(100)
  202. num := int64(100)
  203. l := &raftLog{offset: offset, ents: make([]Entry, num)}
  204. tests := []struct {
  205. index int64
  206. w bool
  207. }{
  208. {offset - 1, true},
  209. {offset, false},
  210. {offset + num/2, false},
  211. {offset + num - 1, false},
  212. {offset + num, true},
  213. }
  214. for i, tt := range tests {
  215. g := l.isOutOfBounds(tt.index)
  216. if g != tt.w {
  217. t.Errorf("#%d: isOutOfBounds = %v, want %v", i, g, tt.w)
  218. }
  219. }
  220. }
  221. func TestAt(t *testing.T) {
  222. var i int64
  223. offset := int64(100)
  224. num := int64(100)
  225. l := &raftLog{offset: offset}
  226. for i = 0; i < num; i++ {
  227. l.ents = append(l.ents, Entry{Term: i})
  228. }
  229. tests := []struct {
  230. index int64
  231. w *Entry
  232. }{
  233. {offset - 1, nil},
  234. {offset, &Entry{Term: 0}},
  235. {offset + num/2, &Entry{Term: num / 2}},
  236. {offset + num - 1, &Entry{Term: num - 1}},
  237. {offset + num, nil},
  238. }
  239. for i, tt := range tests {
  240. g := l.at(tt.index)
  241. if !reflect.DeepEqual(g, tt.w) {
  242. t.Errorf("#%d: at = %v, want %v", i, g, tt.w)
  243. }
  244. }
  245. }
  246. func TestSlice(t *testing.T) {
  247. var i int64
  248. offset := int64(100)
  249. num := int64(100)
  250. l := &raftLog{offset: offset}
  251. for i = 0; i < num; i++ {
  252. l.ents = append(l.ents, Entry{Term: i})
  253. }
  254. tests := []struct {
  255. from int64
  256. to int64
  257. w []Entry
  258. }{
  259. {offset - 1, offset + 1, nil},
  260. {offset, offset + 1, []Entry{{Term: 0}}},
  261. {offset + num/2, offset + num/2 + 1, []Entry{{Term: num / 2}}},
  262. {offset + num - 1, offset + num, []Entry{{Term: num - 1}}},
  263. {offset + num, offset + num + 1, nil},
  264. {offset + num/2, offset + num/2, nil},
  265. {offset + num/2, offset + num/2 - 1, nil},
  266. }
  267. for i, tt := range tests {
  268. g := l.slice(tt.from, tt.to)
  269. if !reflect.DeepEqual(g, tt.w) {
  270. t.Errorf("#%d: from %d to %d = %v, want %v", i, tt.from, tt.to, g, tt.w)
  271. }
  272. }
  273. }