log_test.go 7.2 KB

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