log_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /*
  2. Copyright 2014 CoreOS, Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package raft
  14. import (
  15. "reflect"
  16. "testing"
  17. pb "github.com/coreos/etcd/raft/raftpb"
  18. )
  19. func TestFindConflict(t *testing.T) {
  20. previousEnts := []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}
  21. tests := []struct {
  22. from uint64
  23. ents []pb.Entry
  24. wconflict uint64
  25. }{
  26. // no conflict, empty ent
  27. {1, []pb.Entry{}, 0},
  28. {3, []pb.Entry{}, 0},
  29. // no conflict
  30. {1, []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}, 0},
  31. {2, []pb.Entry{{Term: 2}, {Term: 3}}, 0},
  32. {3, []pb.Entry{{Term: 3}}, 0},
  33. // no conflict, but has new entries
  34. {1, []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}, {Term: 4}, {Term: 4}}, 4},
  35. {2, []pb.Entry{{Term: 2}, {Term: 3}, {Term: 4}, {Term: 4}}, 4},
  36. {3, []pb.Entry{{Term: 3}, {Term: 4}, {Term: 4}}, 4},
  37. {4, []pb.Entry{{Term: 4}, {Term: 4}}, 4},
  38. // conflicts with existing entries
  39. {1, []pb.Entry{{Term: 4}, {Term: 4}}, 1},
  40. {2, []pb.Entry{{Term: 1}, {Term: 4}, {Term: 4}}, 2},
  41. {3, []pb.Entry{{Term: 1}, {Term: 2}, {Term: 4}, {Term: 4}}, 3},
  42. }
  43. for i, tt := range tests {
  44. raftLog := newLog()
  45. raftLog.append(raftLog.lastIndex(), previousEnts...)
  46. gconflict := raftLog.findConflict(tt.from, tt.ents)
  47. if gconflict != tt.wconflict {
  48. t.Errorf("#%d: conflict = %d, want %d", i, gconflict, tt.wconflict)
  49. }
  50. }
  51. }
  52. func TestIsUpToDate(t *testing.T) {
  53. previousEnts := []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}
  54. raftLog := newLog()
  55. raftLog.append(raftLog.lastIndex(), previousEnts...)
  56. tests := []struct {
  57. lastIndex uint64
  58. term uint64
  59. wUpToDate bool
  60. }{
  61. // greater term, ignore lastIndex
  62. {raftLog.lastIndex() - 1, 4, true},
  63. {raftLog.lastIndex(), 4, true},
  64. {raftLog.lastIndex() + 1, 4, true},
  65. // smaller term, ignore lastIndex
  66. {raftLog.lastIndex() - 1, 2, false},
  67. {raftLog.lastIndex(), 2, false},
  68. {raftLog.lastIndex() + 1, 2, false},
  69. // equal term, lager lastIndex wins
  70. {raftLog.lastIndex() - 1, 3, false},
  71. {raftLog.lastIndex(), 3, true},
  72. {raftLog.lastIndex() + 1, 3, true},
  73. }
  74. for i, tt := range tests {
  75. gUpToDate := raftLog.isUpToDate(tt.lastIndex, tt.term)
  76. if gUpToDate != tt.wUpToDate {
  77. t.Errorf("#%d: uptodate = %v, want %v", i, gUpToDate, tt.wUpToDate)
  78. }
  79. }
  80. }
  81. func TestAppend(t *testing.T) {
  82. previousEnts := []pb.Entry{{Term: 1}, {Term: 2}}
  83. previousUnstable := uint64(3)
  84. tests := []struct {
  85. after uint64
  86. ents []pb.Entry
  87. windex uint64
  88. wents []pb.Entry
  89. wunstable uint64
  90. }{
  91. {
  92. 2,
  93. []pb.Entry{},
  94. 2,
  95. []pb.Entry{{Term: 1}, {Term: 2}},
  96. 3,
  97. },
  98. {
  99. 2,
  100. []pb.Entry{{Term: 2}},
  101. 3,
  102. []pb.Entry{{Term: 1}, {Term: 2}, {Term: 2}},
  103. 3,
  104. },
  105. // conflicts with index 1
  106. {
  107. 0,
  108. []pb.Entry{{Term: 2}},
  109. 1,
  110. []pb.Entry{{Term: 2}},
  111. 1,
  112. },
  113. // conflicts with index 2
  114. {
  115. 1,
  116. []pb.Entry{{Term: 3}, {Term: 3}},
  117. 3,
  118. []pb.Entry{{Term: 1}, {Term: 3}, {Term: 3}},
  119. 2,
  120. },
  121. }
  122. for i, tt := range tests {
  123. raftLog := newLog()
  124. raftLog.append(raftLog.lastIndex(), previousEnts...)
  125. raftLog.unstable = previousUnstable
  126. index := raftLog.append(tt.after, tt.ents...)
  127. if index != tt.windex {
  128. t.Errorf("#%d: lastIndex = %d, want %d", i, index, tt.windex)
  129. }
  130. if g := raftLog.entries(1); !reflect.DeepEqual(g, tt.wents) {
  131. t.Errorf("#%d: logEnts = %+v, want %+v", i, g, tt.wents)
  132. }
  133. if g := raftLog.unstable; g != tt.wunstable {
  134. t.Errorf("#%d: unstable = %d, want %d", i, g, tt.wunstable)
  135. }
  136. }
  137. }
  138. // TestLogMaybeAppend ensures:
  139. // If the given (index, term) matches with the existing log:
  140. // 1. If an existing entry conflicts with a new one (same index
  141. // but different terms), delete the existing entry and all that
  142. // follow it
  143. // 2.Append any new entries not already in the log
  144. // If the given (index, term) does not match with the existing log:
  145. // return false
  146. func TestLogMaybeAppend(t *testing.T) {
  147. previousEnts := []pb.Entry{{Term: 1}, {Term: 2}, {Term: 3}}
  148. lastindex := uint64(3)
  149. lastterm := uint64(3)
  150. commit := uint64(1)
  151. tests := []struct {
  152. logTerm uint64
  153. index uint64
  154. committed uint64
  155. ents []pb.Entry
  156. wlasti uint64
  157. wappend bool
  158. wcommit uint64
  159. wpanic bool
  160. }{
  161. // not match: term is different
  162. {
  163. lastterm - 1, lastindex, lastindex, []pb.Entry{{Term: 4}},
  164. 0, false, commit, false,
  165. },
  166. // not match: index out of bound
  167. {
  168. lastterm, lastindex + 1, lastindex, []pb.Entry{{Term: 4}},
  169. 0, false, commit, false,
  170. },
  171. // match with the last existing entry
  172. {
  173. lastterm, lastindex, lastindex, nil,
  174. lastindex, true, lastindex, false,
  175. },
  176. {
  177. lastterm, lastindex, lastindex + 1, nil,
  178. lastindex, true, lastindex, false, // do not increase commit higher than lastnewi
  179. },
  180. {
  181. lastterm, lastindex, lastindex - 1, nil,
  182. lastindex, true, lastindex - 1, false, // commit up to the commit in the message
  183. },
  184. {
  185. lastterm, lastindex, 0, nil,
  186. lastindex, true, commit, false, // commit do not decrease
  187. },
  188. {
  189. 0, 0, lastindex, nil,
  190. 0, true, commit, false, // commit do not decrease
  191. },
  192. {
  193. lastterm, lastindex, lastindex, []pb.Entry{{Term: 4}},
  194. lastindex + 1, true, lastindex, false,
  195. },
  196. {
  197. lastterm, lastindex, lastindex + 1, []pb.Entry{{Term: 4}},
  198. lastindex + 1, true, lastindex + 1, false,
  199. },
  200. {
  201. lastterm, lastindex, lastindex + 2, []pb.Entry{{Term: 4}},
  202. lastindex + 1, true, lastindex + 1, false, // do not increase commit higher than lastnewi
  203. },
  204. {
  205. lastterm, lastindex, lastindex + 2, []pb.Entry{{Term: 4}, {Term: 4}},
  206. lastindex + 2, true, lastindex + 2, false,
  207. },
  208. // match with the the entry in the middle
  209. {
  210. lastterm - 1, lastindex - 1, lastindex, []pb.Entry{{Term: 4}},
  211. lastindex, true, lastindex, false,
  212. },
  213. {
  214. lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Term: 4}},
  215. lastindex - 1, true, lastindex - 1, false,
  216. },
  217. {
  218. lastterm - 3, lastindex - 3, lastindex, []pb.Entry{{Term: 4}},
  219. lastindex - 2, true, lastindex - 2, true, // conflict with existing committed entry
  220. },
  221. {
  222. lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Term: 4}, {Term: 4}},
  223. lastindex, true, lastindex, false,
  224. },
  225. }
  226. for i, tt := range tests {
  227. raftLog := newLog()
  228. raftLog.append(raftLog.lastIndex(), previousEnts...)
  229. raftLog.committed = commit
  230. func() {
  231. defer func() {
  232. if r := recover(); r != nil {
  233. if tt.wpanic != true {
  234. t.Errorf("%d: panic = %v, want %v", i, true, tt.wpanic)
  235. }
  236. }
  237. }()
  238. glasti, gappend := raftLog.maybeAppend(tt.index, tt.logTerm, tt.committed, tt.ents...)
  239. gcommit := raftLog.committed
  240. if glasti != tt.wlasti {
  241. t.Errorf("#%d: lastindex = %d, want %d", i, glasti, tt.wlasti)
  242. }
  243. if gappend != tt.wappend {
  244. t.Errorf("#%d: append = %v, want %v", i, gappend, tt.wappend)
  245. }
  246. if gcommit != tt.wcommit {
  247. t.Errorf("#%d: committed = %d, want %d", i, gcommit, tt.wcommit)
  248. }
  249. if gappend {
  250. gents := raftLog.slice(raftLog.lastIndex()-uint64(len(tt.ents))+1, raftLog.lastIndex()+1)
  251. if !reflect.DeepEqual(tt.ents, gents) {
  252. t.Errorf("%d: appended entries = %v, want %v", i, gents, tt.ents)
  253. }
  254. }
  255. }()
  256. }
  257. }
  258. // TestCompactionSideEffects ensures that all the log related funcationality works correctly after
  259. // a compaction.
  260. func TestCompactionSideEffects(t *testing.T) {
  261. var i uint64
  262. lastIndex := uint64(1000)
  263. lastTerm := lastIndex
  264. raftLog := newLog()
  265. for i = 0; i < lastIndex; i++ {
  266. raftLog.append(uint64(i), pb.Entry{Term: uint64(i + 1), Index: uint64(i + 1)})
  267. }
  268. raftLog.maybeCommit(lastIndex, lastTerm)
  269. raftLog.appliedTo(raftLog.committed)
  270. raftLog.compact(500)
  271. if raftLog.lastIndex() != lastIndex {
  272. t.Errorf("lastIndex = %d, want %d", raftLog.lastIndex(), lastIndex)
  273. }
  274. for i := raftLog.offset; i <= raftLog.lastIndex(); i++ {
  275. if raftLog.term(i) != i {
  276. t.Errorf("term(%d) = %d, want %d", i, raftLog.term(i), i)
  277. }
  278. }
  279. for i := raftLog.offset; i <= raftLog.lastIndex(); i++ {
  280. if !raftLog.matchTerm(i, i) {
  281. t.Errorf("matchTerm(%d) = false, want true", i)
  282. }
  283. }
  284. unstableEnts := raftLog.unstableEnts()
  285. if g := len(unstableEnts); g != 500 {
  286. t.Errorf("len(unstableEntries) = %d, want = %d", g, 500)
  287. }
  288. if unstableEnts[0].Index != 501 {
  289. t.Errorf("Index = %d, want = %d", unstableEnts[0].Index, 501)
  290. }
  291. prev := raftLog.lastIndex()
  292. raftLog.append(raftLog.lastIndex(), pb.Entry{Term: raftLog.lastIndex() + 1})
  293. if raftLog.lastIndex() != prev+1 {
  294. t.Errorf("lastIndex = %d, want = %d", raftLog.lastIndex(), prev+1)
  295. }
  296. ents := raftLog.entries(raftLog.lastIndex())
  297. if len(ents) != 1 {
  298. t.Errorf("len(entries) = %d, want = %d", len(ents), 1)
  299. }
  300. }
  301. func TestUnstableEnts(t *testing.T) {
  302. previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}}
  303. tests := []struct {
  304. unstable uint64
  305. wents []pb.Entry
  306. wunstable uint64
  307. }{
  308. {3, nil, 3},
  309. {1, previousEnts, 3},
  310. {0, append([]pb.Entry{{}}, previousEnts...), 3},
  311. }
  312. for i, tt := range tests {
  313. raftLog := newLog()
  314. raftLog.append(0, previousEnts...)
  315. raftLog.unstable = tt.unstable
  316. ents := raftLog.unstableEnts()
  317. if l := len(ents); l > 0 {
  318. raftLog.stableTo(ents[l-1].Index)
  319. }
  320. if !reflect.DeepEqual(ents, tt.wents) {
  321. t.Errorf("#%d: unstableEnts = %+v, want %+v", i, ents, tt.wents)
  322. }
  323. if g := raftLog.unstable; g != tt.wunstable {
  324. t.Errorf("#%d: unstable = %d, want %d", i, g, tt.wunstable)
  325. }
  326. }
  327. }
  328. func TestStableTo(t *testing.T) {
  329. tests := []struct {
  330. stable uint64
  331. wunstable uint64
  332. }{
  333. {0, 1},
  334. {1, 2},
  335. {2, 3},
  336. }
  337. for i, tt := range tests {
  338. raftLog := newLog()
  339. raftLog.stableTo(tt.stable)
  340. if raftLog.unstable != tt.wunstable {
  341. t.Errorf("#%d: unstable = %d, want %d", i, raftLog.unstable, tt.wunstable)
  342. }
  343. }
  344. }
  345. //TestCompaction ensures that the number of log entreis is correct after compactions.
  346. func TestCompaction(t *testing.T) {
  347. tests := []struct {
  348. applied uint64
  349. lastIndex uint64
  350. compact []uint64
  351. wleft []int
  352. wallow bool
  353. }{
  354. // out of upper bound
  355. {1000, 1000, []uint64{1001}, []int{-1}, false},
  356. {1000, 1000, []uint64{300, 500, 800, 900}, []int{701, 501, 201, 101}, true},
  357. // out of lower bound
  358. {1000, 1000, []uint64{300, 299}, []int{701, -1}, false},
  359. {0, 1000, []uint64{1}, []int{-1}, false},
  360. }
  361. for i, tt := range tests {
  362. func() {
  363. defer func() {
  364. if r := recover(); r != nil {
  365. if tt.wallow == true {
  366. t.Errorf("%d: allow = %v, want %v", i, false, true)
  367. }
  368. }
  369. }()
  370. raftLog := newLog()
  371. for i := uint64(0); i < tt.lastIndex; i++ {
  372. raftLog.append(uint64(i), pb.Entry{})
  373. }
  374. raftLog.maybeCommit(tt.applied, 0)
  375. raftLog.appliedTo(raftLog.committed)
  376. for j := 0; j < len(tt.compact); j++ {
  377. raftLog.compact(tt.compact[j])
  378. if len(raftLog.ents) != tt.wleft[j] {
  379. t.Errorf("#%d.%d len = %d, want %d", i, j, len(raftLog.ents), tt.wleft[j])
  380. }
  381. }
  382. }()
  383. }
  384. }
  385. func TestLogRestore(t *testing.T) {
  386. var i uint64
  387. raftLog := newLog()
  388. for i = 0; i < 100; i++ {
  389. raftLog.append(i, pb.Entry{Term: i + 1})
  390. }
  391. index := uint64(1000)
  392. term := uint64(1000)
  393. raftLog.restore(pb.Snapshot{Index: index, Term: term})
  394. // only has the guard entry
  395. if len(raftLog.ents) != 1 {
  396. t.Errorf("len = %d, want 0", len(raftLog.ents))
  397. }
  398. if raftLog.offset != index {
  399. t.Errorf("offset = %d, want %d", raftLog.offset, index)
  400. }
  401. if raftLog.applied != index {
  402. t.Errorf("applied = %d, want %d", raftLog.applied, index)
  403. }
  404. if raftLog.committed != index {
  405. t.Errorf("comitted = %d, want %d", raftLog.committed, index)
  406. }
  407. if raftLog.unstable != index+1 {
  408. t.Errorf("unstable = %d, want %d", raftLog.unstable, index+1)
  409. }
  410. if raftLog.term(index) != term {
  411. t.Errorf("term = %d, want %d", raftLog.term(index), term)
  412. }
  413. }
  414. func TestIsOutOfBounds(t *testing.T) {
  415. offset := uint64(100)
  416. num := uint64(100)
  417. l := &raftLog{offset: offset, ents: make([]pb.Entry, num)}
  418. tests := []struct {
  419. index uint64
  420. w bool
  421. }{
  422. {offset - 1, true},
  423. {offset, false},
  424. {offset + num/2, false},
  425. {offset + num - 1, false},
  426. {offset + num, true},
  427. }
  428. for i, tt := range tests {
  429. g := l.isOutOfBounds(tt.index)
  430. if g != tt.w {
  431. t.Errorf("#%d: isOutOfBounds = %v, want %v", i, g, tt.w)
  432. }
  433. }
  434. }
  435. func TestAt(t *testing.T) {
  436. var i uint64
  437. offset := uint64(100)
  438. num := uint64(100)
  439. l := &raftLog{offset: offset}
  440. for i = 0; i < num; i++ {
  441. l.ents = append(l.ents, pb.Entry{Term: i})
  442. }
  443. tests := []struct {
  444. index uint64
  445. w *pb.Entry
  446. }{
  447. {offset - 1, nil},
  448. {offset, &pb.Entry{Term: 0}},
  449. {offset + num/2, &pb.Entry{Term: num / 2}},
  450. {offset + num - 1, &pb.Entry{Term: num - 1}},
  451. {offset + num, nil},
  452. }
  453. for i, tt := range tests {
  454. g := l.at(tt.index)
  455. if !reflect.DeepEqual(g, tt.w) {
  456. t.Errorf("#%d: at = %v, want %v", i, g, tt.w)
  457. }
  458. }
  459. }
  460. func TestSlice(t *testing.T) {
  461. var i uint64
  462. offset := uint64(100)
  463. num := uint64(100)
  464. l := &raftLog{offset: offset}
  465. for i = 0; i < num; i++ {
  466. l.ents = append(l.ents, pb.Entry{Term: i})
  467. }
  468. tests := []struct {
  469. from uint64
  470. to uint64
  471. w []pb.Entry
  472. }{
  473. {offset - 1, offset + 1, nil},
  474. {offset, offset + 1, []pb.Entry{{Term: 0}}},
  475. {offset + num/2, offset + num/2 + 1, []pb.Entry{{Term: num / 2}}},
  476. {offset + num - 1, offset + num, []pb.Entry{{Term: num - 1}}},
  477. {offset + num, offset + num + 1, nil},
  478. {offset + num/2, offset + num/2, nil},
  479. {offset + num/2, offset + num/2 - 1, nil},
  480. }
  481. for i, tt := range tests {
  482. g := l.slice(tt.from, tt.to)
  483. if !reflect.DeepEqual(g, tt.w) {
  484. t.Errorf("#%d: from %d to %d = %v, want %v", i, tt.from, tt.to, g, tt.w)
  485. }
  486. }
  487. }