log_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package raft
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "reflect"
  6. "testing"
  7. )
  8. //------------------------------------------------------------------------------
  9. //
  10. // Tests
  11. //
  12. //------------------------------------------------------------------------------
  13. //--------------------------------------
  14. // Append
  15. //--------------------------------------
  16. // Ensure that we can append to a new log.
  17. func TestLogNewLog(t *testing.T) {
  18. path := getLogPath()
  19. log := newLog()
  20. log.ApplyFunc = func(e *LogEntry, c Command) (interface{}, error) {
  21. return nil, nil
  22. }
  23. if err := log.open(path); err != nil {
  24. t.Fatalf("Unable to open log: %v", err)
  25. }
  26. defer log.close()
  27. defer os.Remove(path)
  28. e, _ := newLogEntry(log, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
  29. if err := log.appendEntry(e); err != nil {
  30. t.Fatalf("Unable to append: %v", err)
  31. }
  32. e, _ = newLogEntry(log, nil, 2, 1, &testCommand2{X: 100})
  33. if err := log.appendEntry(e); err != nil {
  34. t.Fatalf("Unable to append: %v", err)
  35. }
  36. e, _ = newLogEntry(log, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
  37. if err := log.appendEntry(e); err != nil {
  38. t.Fatalf("Unable to append: %v", err)
  39. }
  40. // Partial commit.
  41. if err := log.setCommitIndex(2); err != nil {
  42. t.Fatalf("Unable to partially commit: %v", err)
  43. }
  44. if index, term := log.commitInfo(); index != 2 || term != 1 {
  45. t.Fatalf("Invalid commit info [IDX=%v, TERM=%v]", index, term)
  46. }
  47. // Full commit.
  48. if err := log.setCommitIndex(3); err != nil {
  49. t.Fatalf("Unable to commit: %v", err)
  50. }
  51. if index, term := log.commitInfo(); index != 3 || term != 2 {
  52. t.Fatalf("Invalid commit info [IDX=%v, TERM=%v]", index, term)
  53. }
  54. }
  55. // Ensure that we can decode and encode to an existing log.
  56. func TestLogExistingLog(t *testing.T) {
  57. tmpLog := newLog()
  58. e0, _ := newLogEntry(tmpLog, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
  59. e1, _ := newLogEntry(tmpLog, nil, 2, 1, &testCommand2{X: 100})
  60. e2, _ := newLogEntry(tmpLog, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
  61. log, path := setupLog([]*LogEntry{e0, e1, e2})
  62. defer log.close()
  63. defer os.Remove(path)
  64. // Validate existing log entries.
  65. if len(log.entries) != 3 {
  66. t.Fatalf("Expected 3 entries, got %d", len(log.entries))
  67. }
  68. if log.entries[0].Index() != 1 || log.entries[0].Term() != 1 {
  69. t.Fatalf("Unexpected entry[0]: %v", log.entries[0])
  70. }
  71. if log.entries[1].Index() != 2 || log.entries[1].Term() != 1 {
  72. t.Fatalf("Unexpected entry[1]: %v", log.entries[1])
  73. }
  74. if log.entries[2].Index() != 3 || log.entries[2].Term() != 2 {
  75. t.Fatalf("Unexpected entry[2]: %v", log.entries[2])
  76. }
  77. }
  78. // Ensure that we can check the contents of the log by index/term.
  79. func TestLogContainsEntries(t *testing.T) {
  80. tmpLog := newLog()
  81. e0, _ := newLogEntry(tmpLog, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
  82. e1, _ := newLogEntry(tmpLog, nil, 2, 1, &testCommand2{X: 100})
  83. e2, _ := newLogEntry(tmpLog, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
  84. log, path := setupLog([]*LogEntry{e0, e1, e2})
  85. defer log.close()
  86. defer os.Remove(path)
  87. if log.containsEntry(0, 0) {
  88. t.Fatalf("Zero-index entry should not exist in log.")
  89. }
  90. if log.containsEntry(1, 0) {
  91. t.Fatalf("Entry with mismatched term should not exist")
  92. }
  93. if log.containsEntry(4, 0) {
  94. t.Fatalf("Out-of-range entry should not exist")
  95. }
  96. if !log.containsEntry(2, 1) {
  97. t.Fatalf("Entry 2/1 should exist")
  98. }
  99. if !log.containsEntry(3, 2) {
  100. t.Fatalf("Entry 2/1 should exist")
  101. }
  102. }
  103. // Ensure that we can recover from an incomplete/corrupt log and continue logging.
  104. func TestLogRecovery(t *testing.T) {
  105. tmpLog := newLog()
  106. e0, _ := newLogEntry(tmpLog, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
  107. e1, _ := newLogEntry(tmpLog, nil, 2, 1, &testCommand2{X: 100})
  108. f, _ := ioutil.TempFile("", "raft-log-")
  109. e0.Encode(f)
  110. e1.Encode(f)
  111. f.WriteString("CORRUPT!")
  112. f.Close()
  113. log := newLog()
  114. log.ApplyFunc = func(e *LogEntry, c Command) (interface{}, error) {
  115. return nil, nil
  116. }
  117. if err := log.open(f.Name()); err != nil {
  118. t.Fatalf("Unable to open log: %v", err)
  119. }
  120. defer log.close()
  121. defer os.Remove(f.Name())
  122. e, _ := newLogEntry(log, nil, 3, 2, &testCommand1{Val: "bat", I: -5})
  123. if err := log.appendEntry(e); err != nil {
  124. t.Fatalf("Unable to append: %v", err)
  125. }
  126. // Validate existing log entries.
  127. if len(log.entries) != 3 {
  128. t.Fatalf("Expected 3 entries, got %d", len(log.entries))
  129. }
  130. if log.entries[0].Index() != 1 || log.entries[0].Term() != 1 {
  131. t.Fatalf("Unexpected entry[0]: %v", log.entries[0])
  132. }
  133. if log.entries[1].Index() != 2 || log.entries[1].Term() != 1 {
  134. t.Fatalf("Unexpected entry[1]: %v", log.entries[1])
  135. }
  136. if log.entries[2].Index() != 3 || log.entries[2].Term() != 2 {
  137. t.Fatalf("Unexpected entry[2]: %v", log.entries[2])
  138. }
  139. }
  140. //--------------------------------------
  141. // Append
  142. //--------------------------------------
  143. // Ensure that we can truncate uncommitted entries in the log.
  144. func TestLogTruncate(t *testing.T) {
  145. log, path := setupLog(nil)
  146. if err := log.open(path); err != nil {
  147. t.Fatalf("Unable to open log: %v", err)
  148. }
  149. defer os.Remove(path)
  150. entry1, _ := newLogEntry(log, nil, 1, 1, &testCommand1{Val: "foo", I: 20})
  151. if err := log.appendEntry(entry1); err != nil {
  152. t.Fatalf("Unable to append: %v", err)
  153. }
  154. entry2, _ := newLogEntry(log, nil, 2, 1, &testCommand2{X: 100})
  155. if err := log.appendEntry(entry2); err != nil {
  156. t.Fatalf("Unable to append: %v", err)
  157. }
  158. entry3, _ := newLogEntry(log, nil, 3, 2, &testCommand1{Val: "bar", I: 0})
  159. if err := log.appendEntry(entry3); err != nil {
  160. t.Fatalf("Unable to append: %v", err)
  161. }
  162. if err := log.setCommitIndex(2); err != nil {
  163. t.Fatalf("Unable to partially commit: %v", err)
  164. }
  165. // Truncate committed entry.
  166. if err := log.truncate(1, 1); err == nil || err.Error() != "raft.Log: Index is already committed (2): (IDX=1, TERM=1)" {
  167. t.Fatalf("Truncating committed entries shouldn't work: %v", err)
  168. }
  169. // Truncate past end of log.
  170. if err := log.truncate(4, 2); err == nil || err.Error() != "raft.Log: Entry index does not exist (MAX=3): (IDX=4, TERM=2)" {
  171. t.Fatalf("Truncating past end-of-log shouldn't work: %v", err)
  172. }
  173. // Truncate entry with mismatched term.
  174. if err := log.truncate(2, 2); err == nil || err.Error() != "raft.Log: Entry at index does not have matching term (1): (IDX=2, TERM=2)" {
  175. t.Fatalf("Truncating mismatched entries shouldn't work: %v", err)
  176. }
  177. // Truncate end of log.
  178. if err := log.truncate(3, 2); !(err == nil && reflect.DeepEqual(log.entries, []*LogEntry{entry1, entry2, entry3})) {
  179. t.Fatalf("Truncating end of log should work: %v\n\nEntries:\nActual: %v\nExpected: %v", err, log.entries, []*LogEntry{entry1, entry2, entry3})
  180. }
  181. // Truncate at last commit.
  182. if err := log.truncate(2, 1); !(err == nil && reflect.DeepEqual(log.entries, []*LogEntry{entry1, entry2})) {
  183. t.Fatalf("Truncating at last commit should work: %v\n\nEntries:\nActual: %v\nExpected: %v", err, log.entries, []*LogEntry{entry1, entry2})
  184. }
  185. // Append after truncate
  186. if err := log.appendEntry(entry3); err != nil {
  187. t.Fatalf("Unable to append after truncate: %v", err)
  188. }
  189. log.close()
  190. // Recovery the truncated log
  191. log = newLog()
  192. if err := log.open(path); err != nil {
  193. t.Fatalf("Unable to open log: %v", err)
  194. }
  195. // Validate existing log entries.
  196. if len(log.entries) != 3 {
  197. t.Fatalf("Expected 3 entries, got %d", len(log.entries))
  198. }
  199. if log.entries[0].Index() != 1 || log.entries[0].Term() != 1 {
  200. t.Fatalf("Unexpected entry[0]: %v", log.entries[0])
  201. }
  202. if log.entries[1].Index() != 2 || log.entries[1].Term() != 1 {
  203. t.Fatalf("Unexpected entry[1]: %v", log.entries[1])
  204. }
  205. if log.entries[2].Index() != 3 || log.entries[2].Term() != 2 {
  206. t.Fatalf("Unexpected entry[2]: %v", log.entries[2])
  207. }
  208. }