log_array.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. use crate::index_term::IndexTerm;
  2. use crate::{Index, LogEntry, Term};
  3. /// A log array that stores a tail of the whole Raft log.
  4. ///
  5. /// The Raft log represented by the log array has length `end()`. Only log
  6. /// entries after `start()` are physically stored in the log array (with some
  7. /// caveats). The index and term of log entries in range `[start(), end())` are
  8. /// accessible. A snapshot is stored at the beginning of the log array, which
  9. /// covers all commands before and **including** `start()`. The command at
  10. /// `start()` is **not** accessible, but all commands after that are.
  11. ///
  12. /// New entries can be appended to the end of the Raft log via `add_command()`
  13. /// or `push()`.
  14. ///
  15. /// The log array can be truncated to at most one entry, which is at `start()`
  16. /// and contains the snapshot. The start of the log array can be shifted towards
  17. /// `end()`, if another snapshot at that position is provided.
  18. ///
  19. /// The log array can also be reset to a single entry, contains an index, a term
  20. /// and a snapshot, via `reset()`.
  21. ///
  22. /// The log array is guaranteed to have at least one element, containing an
  23. /// index, a term and a snapshot.
  24. ///
  25. /// All APIs **will** panic if the given index(es) are out of bound.
  26. ///
  27. /// NOT THREAD SAFE.
  28. #[derive(Clone, Serialize, Deserialize)]
  29. pub(crate) struct LogArray<C> {
  30. inner: Vec<LogEntry<C>>,
  31. snapshot: Vec<u8>,
  32. }
  33. impl<C: Default> LogArray<C> {
  34. /// Create the initial Raft log with no user-supplied commands.
  35. pub fn create() -> LogArray<C> {
  36. let ret = LogArray {
  37. inner: vec![Self::build_first_entry(0, Term(0))],
  38. snapshot: vec![],
  39. };
  40. ret.check_one_element();
  41. ret
  42. }
  43. }
  44. // Log accessors
  45. impl<C> LogArray<C> {
  46. /// The start of the stored log entries. The command at this index is no
  47. /// longer accessible, since it is included in the snapshot.
  48. pub fn start(&self) -> Index {
  49. self.first_entry().index
  50. }
  51. /// The end index of the Raft log.
  52. pub fn end(&self) -> usize {
  53. self.start() + self.inner.len()
  54. }
  55. /// The first index and term stored in this log array.
  56. pub fn first_index_term(&self) -> IndexTerm {
  57. self.first_entry().into()
  58. }
  59. /// The last index and term of the Raft log.
  60. pub fn last_index_term(&self) -> IndexTerm {
  61. self.last_entry().into()
  62. }
  63. /// The log entry at the given index.
  64. pub fn at(&self, index: Index) -> &LogEntry<C> {
  65. let index = self.check_at_index(index);
  66. &self.inner[index]
  67. }
  68. /// All log entries after the given index.
  69. pub fn after(&self, index: Index) -> &[LogEntry<C>] {
  70. let index = self.check_range_index(index);
  71. &self.inner[index..]
  72. }
  73. /// All log entries in range [start, end).
  74. pub fn between(&self, start: Index, end: Index) -> &[LogEntry<C>] {
  75. let start = self.check_range_index(start);
  76. let end = self.check_range_index(end);
  77. &self.inner[start..end]
  78. }
  79. /// All log entries stored in the array.
  80. #[allow(dead_code)]
  81. pub fn all(&self) -> &[LogEntry<C>] {
  82. &self.inner[..]
  83. }
  84. /// The snapshot before and including `start()`.
  85. pub fn snapshot(&self) -> (IndexTerm, &[u8]) {
  86. (self.first_index_term(), &self.snapshot)
  87. }
  88. }
  89. impl<C> std::ops::Index<usize> for LogArray<C> {
  90. type Output = LogEntry<C>;
  91. fn index(&self, index: usize) -> &Self::Output {
  92. self.at(index)
  93. }
  94. }
  95. // Mutations
  96. impl<C> LogArray<C> {
  97. /// Add a new entry to the Raft log, with term and command. The new index is
  98. /// returned.
  99. pub fn add_command(&mut self, term: Term, command: C) -> Index {
  100. let index = self.end();
  101. self.push(LogEntry {
  102. index,
  103. term,
  104. command,
  105. });
  106. index
  107. }
  108. /// Push a LogEntry into the Raft log. The index of the log entry must match
  109. /// the next index in the log.
  110. pub fn push(&mut self, log_entry: LogEntry<C>) {
  111. let index = log_entry.index;
  112. assert_eq!(index, self.end(), "Expecting new index to be exact at len");
  113. self.inner.push(log_entry);
  114. assert_eq!(
  115. index + 1,
  116. self.end(),
  117. "Expecting len increase by one after push",
  118. );
  119. assert_eq!(
  120. self.at(index).index,
  121. index,
  122. "Expecting pushed element to have the same index",
  123. );
  124. self.check_one_element();
  125. }
  126. /// Remove all log entries on and after `index`.
  127. pub fn truncate(&mut self, index: Index) {
  128. let index = self.check_middle_index(index);
  129. self.inner.truncate(index);
  130. self.check_one_element()
  131. }
  132. }
  133. impl<C: Default> LogArray<C> {
  134. /// Shift the start of the array to `index`, and store a new snapshot that
  135. /// covers all commands before and at `index`.
  136. pub fn shift(&mut self, index: Index, snapshot: Vec<u8>) {
  137. // Discard everything before index and store the snapshot.
  138. let offset = self.check_middle_index(index);
  139. // WARNING: Potentially all entries after offset would be copied.
  140. self.inner.drain(0..offset);
  141. self.snapshot = snapshot;
  142. // Override the first entry, we know there is at least one entry. This
  143. // is not strictly needed. One benefit is that the command can be
  144. // released after this point.
  145. let first = self.first_index_term();
  146. self.inner[0] = Self::build_first_entry(first.index, first.term);
  147. assert_eq!(
  148. first.index, index,
  149. "Expecting the first entry to have the same index."
  150. );
  151. self.check_one_element()
  152. }
  153. /// Reset the array to contain only one snapshot at the given `index` with
  154. /// the given `term`.
  155. pub fn reset(
  156. &mut self,
  157. index: Index,
  158. term: Term,
  159. snapshot: Vec<u8>,
  160. ) -> Vec<LogEntry<C>> {
  161. let mut inner = vec![Self::build_first_entry(index, term)];
  162. std::mem::swap(&mut inner, &mut self.inner);
  163. self.snapshot = snapshot;
  164. self.check_one_element();
  165. inner
  166. }
  167. }
  168. impl<C> LogArray<C> {
  169. fn first_entry(&self) -> &LogEntry<C> {
  170. self.inner
  171. .first()
  172. .expect("There must be at least one element in log")
  173. }
  174. fn last_entry(&self) -> &LogEntry<C> {
  175. &self
  176. .inner
  177. .last()
  178. .expect("There must be at least one entry in log")
  179. }
  180. fn offset(&self, index: Index) -> usize {
  181. index - self.start()
  182. }
  183. fn check_at_index(&self, index: Index) -> usize {
  184. assert!(
  185. index >= self.start() && index < self.end(),
  186. "Accessing start log index {} out of range [{}, {})",
  187. index,
  188. self.start(),
  189. self.end()
  190. );
  191. self.offset(index)
  192. }
  193. fn check_range_index(&self, index: Index) -> usize {
  194. assert!(
  195. index >= self.start() && index <= self.end(),
  196. "Accessing end log index {} out of range ({}, {}]",
  197. index,
  198. self.start(),
  199. self.end()
  200. );
  201. self.offset(index)
  202. }
  203. fn check_middle_index(&self, index: Index) -> usize {
  204. assert!(
  205. index > self.start() && index < self.end(),
  206. "Log index {} out of range ({}, {})",
  207. index,
  208. self.start(),
  209. self.end()
  210. );
  211. self.offset(index)
  212. }
  213. #[allow(clippy::len_zero)]
  214. fn check_one_element(&self) {
  215. assert!(
  216. self.inner.len() >= 1,
  217. "There must be at least one element in log"
  218. )
  219. }
  220. }
  221. impl<C: Default> LogArray<C> {
  222. fn build_first_entry(index: Index, term: Term) -> LogEntry<C> {
  223. LogEntry {
  224. index,
  225. term,
  226. command: C::default(),
  227. }
  228. }
  229. }
  230. #[cfg(test)]
  231. mod tests {
  232. use std::panic::catch_unwind;
  233. use super::*;
  234. fn make_log_array(len: usize) -> LogArray<i32> {
  235. make_log_array_range(0, len)
  236. }
  237. fn make_log_array_range(start: usize, end: usize) -> LogArray<i32> {
  238. let mut ret = vec![];
  239. for i in start..end {
  240. ret.push(LogEntry {
  241. term: Term(i / 3),
  242. index: i,
  243. command: (end - i) as i32,
  244. })
  245. }
  246. LogArray {
  247. inner: ret,
  248. snapshot: vec![1, 2, 3],
  249. }
  250. }
  251. fn default_log_array() -> (usize, usize, LogArray<i32>) {
  252. (8, 17, make_log_array_range(8, 17))
  253. }
  254. #[test]
  255. fn test_create() {
  256. let log = LogArray::<i32>::create();
  257. log.check_one_element();
  258. assert_eq!(1, log.end());
  259. assert_eq!((0, Term(0)), log.first_index_term().into());
  260. assert_eq!(0, log[0].command);
  261. }
  262. // TODO(ditsing): add invariant checks to restore and write a test.
  263. #[test]
  264. fn test_restore() {}
  265. #[test]
  266. fn test_start() {
  267. let log = make_log_array_range(9, 17);
  268. assert_eq!(9, log.start());
  269. let log = make_log_array(9);
  270. assert_eq!(0, log.start());
  271. }
  272. #[test]
  273. fn test_end() {
  274. let log = make_log_array(7);
  275. assert_eq!(7, log.end());
  276. let log = make_log_array_range(8, 17);
  277. assert_eq!(17, log.end());
  278. }
  279. #[test]
  280. fn test_accessors() {
  281. let (start, end, log) = default_log_array();
  282. assert_eq!((start, Term(2)), log.first_index_term().into());
  283. assert_eq!((end - 1, Term(5)), log.last_index_term().into());
  284. assert_eq!(
  285. ((start, Term(2)).into(), [1, 2, 3].as_ref()),
  286. log.snapshot()
  287. );
  288. let all = log.all();
  289. assert_eq!(end - start, all.len());
  290. for i in start..end {
  291. assert_eq!(all[i - start].index, i);
  292. }
  293. }
  294. #[test]
  295. fn test_at() {
  296. let (start, end, log) = default_log_array();
  297. let last = log.at(end - 1);
  298. assert_eq!(end - 1, last.index);
  299. assert_eq!(5, last.term.0);
  300. assert_eq!(1, last.command);
  301. let first = log.at(start);
  302. assert_eq!(start, first.index);
  303. assert_eq!(2, first.term.0);
  304. assert_eq!(9, first.command);
  305. assert!(start < 12);
  306. assert!(end > 12);
  307. let middle = log.at(12);
  308. assert_eq!(12, middle.index);
  309. assert_eq!(4, middle.term.0);
  310. assert_eq!(5, middle.command);
  311. let at_before_start = catch_unwind(|| {
  312. log.at(start - 1);
  313. });
  314. assert!(at_before_start.is_err());
  315. let at_after_end = catch_unwind(|| {
  316. log.at(end);
  317. });
  318. assert!(at_after_end.is_err());
  319. }
  320. #[test]
  321. fn test_index_operator() {
  322. let (start, end, log) = default_log_array();
  323. for i in start..end {
  324. assert_eq!(log[i].index, log.at(i).index);
  325. assert_eq!(log[i].term, log.at(i).term);
  326. assert_eq!(log[i].command, log.at(i).command);
  327. }
  328. assert!(catch_unwind(|| log[0].term).is_err());
  329. assert!(catch_unwind(|| log[20].term).is_err());
  330. }
  331. #[test]
  332. fn test_after() {
  333. let (start, end, log) = default_log_array();
  334. let offset = 12;
  335. assert!(offset > start);
  336. assert!(offset < end);
  337. let after = log.after(offset);
  338. assert_eq!(end - offset, after.len());
  339. for i in offset..end {
  340. assert_eq!(after[i - offset].index, i);
  341. assert_eq!(after[i - offset].term.0, i / 3);
  342. }
  343. assert!(catch_unwind(|| log.after(start - 1)).is_err());
  344. assert!(catch_unwind(|| log.after(start)).is_ok());
  345. assert!(catch_unwind(|| log.after(end)).is_ok());
  346. assert!(catch_unwind(|| log.after(end + 1)).is_err());
  347. }
  348. #[test]
  349. fn test_between() {
  350. let (start, end, log) = default_log_array();
  351. let between = log.between(start + 2, end);
  352. assert_eq!(end - start - 2, between.len());
  353. for i in start + 2..end {
  354. assert_eq!(between[i - start - 2].index, i);
  355. assert_eq!(between[i - start - 2].term.0, i / 3);
  356. }
  357. assert!(catch_unwind(|| log.between(start - 1, end)).is_err());
  358. assert!(catch_unwind(|| log.between(start + 2, end + 1)).is_err());
  359. assert!(catch_unwind(|| log.between(start, end)).is_ok());
  360. }
  361. #[test]
  362. fn test_add_command() {
  363. let (_, end, mut log) = default_log_array();
  364. let index = log.add_command(Term(8), 9);
  365. assert_eq!(8, log.at(index).term.0);
  366. assert_eq!(9, log.at(index).command);
  367. assert_eq!(index, end);
  368. assert_eq!(index + 1, log.end());
  369. }
  370. #[test]
  371. fn test_push() {
  372. let (_, end, mut log) = default_log_array();
  373. log.push(LogEntry {
  374. term: Term(8),
  375. index: end,
  376. command: 1,
  377. });
  378. assert_eq!(8, log.at(end).term.0);
  379. assert_eq!(1, log.at(end).command);
  380. assert_eq!(end + 1, log.end());
  381. }
  382. #[test]
  383. #[should_panic]
  384. fn test_push_small_index() {
  385. let (_, end, mut log) = default_log_array();
  386. log.push(LogEntry {
  387. term: Term(8),
  388. index: end - 1,
  389. command: 1,
  390. });
  391. }
  392. #[test]
  393. #[should_panic]
  394. fn test_push_big_index() {
  395. let (_, end, mut log) = default_log_array();
  396. log.push(LogEntry {
  397. term: Term(8),
  398. index: end + 1,
  399. command: 1,
  400. });
  401. }
  402. #[test]
  403. fn test_truncate() {
  404. let (start, _, mut log) = default_log_array();
  405. log.truncate(start + 5);
  406. assert_eq!(start + 5, log.end());
  407. for i in start..start + 5 {
  408. assert_eq!(log[i].index, i);
  409. assert_eq!(log[i].term.0, i / 3);
  410. }
  411. log.truncate(start + 1);
  412. assert_eq!(1, log.all().len());
  413. }
  414. #[test]
  415. #[should_panic]
  416. fn test_truncate_at_start() {
  417. let (start, _, mut log) = default_log_array();
  418. log.truncate(start);
  419. }
  420. #[test]
  421. #[should_panic]
  422. fn test_truncate_at_end() {
  423. let (_, end, mut log) = default_log_array();
  424. log.truncate(end);
  425. }
  426. #[test]
  427. #[should_panic]
  428. fn test_truncate_before_start() {
  429. let (start, _, mut log) = default_log_array();
  430. log.truncate(start - 1);
  431. }
  432. #[test]
  433. #[should_panic]
  434. fn test_truncate_after_end() {
  435. let (_, end, mut log) = default_log_array();
  436. log.truncate(end + 1);
  437. }
  438. #[test]
  439. fn test_shift() {
  440. let (start, end, mut log) = default_log_array();
  441. let offset = 10;
  442. assert!(offset > start);
  443. assert!(offset < end);
  444. log.shift(offset, vec![]);
  445. assert_eq!((offset, Term(3)), log.first_index_term().into());
  446. assert_eq!(0, log[offset].command);
  447. let all = log.all();
  448. assert_eq!(end - offset, all.len());
  449. for i in offset..end {
  450. assert_eq!(i, all[i - offset].index);
  451. assert_eq!(i / 3, all[i - offset].term.0);
  452. }
  453. assert_eq!(log.snapshot, vec![]);
  454. }
  455. #[test]
  456. #[should_panic]
  457. fn test_shift_before_start() {
  458. let (start, end, mut log) = default_log_array();
  459. assert!(start < end);
  460. log.shift(start - 1, vec![]);
  461. }
  462. #[test]
  463. #[should_panic]
  464. fn test_shift_at_start() {
  465. let (start, end, mut log) = default_log_array();
  466. assert!(start < end);
  467. log.shift(start, vec![]);
  468. }
  469. #[test]
  470. #[should_panic]
  471. fn test_shift_at_end() {
  472. let (start, end, mut log) = default_log_array();
  473. assert!(start < end);
  474. log.shift(end, vec![]);
  475. }
  476. #[test]
  477. #[should_panic]
  478. fn test_shift_after_end() {
  479. let (start, end, mut log) = default_log_array();
  480. assert!(start < end);
  481. log.shift(end + 1, vec![]);
  482. }
  483. #[test]
  484. fn test_reset() {
  485. let (start, end, mut log) = default_log_array();
  486. let dump = log.reset(88, Term(99), vec![1, 2]);
  487. assert_eq!(1, log.all().len());
  488. assert_eq!(vec![1, 2], log.snapshot);
  489. assert_eq!(88, log[88].index);
  490. assert_eq!(99, log[88].term.0);
  491. assert_eq!(0, log[88].command);
  492. assert_eq!(end - start, dump.len());
  493. }
  494. #[test]
  495. fn test_private_accessors() {
  496. let (start, end, log) = default_log_array();
  497. let first = log.first_entry();
  498. assert_eq!(start, first.index);
  499. assert_eq!(start / 3, first.term.0);
  500. let last = log.last_entry();
  501. assert_eq!(end - 1, last.index);
  502. assert_eq!((end - 1) / 3, last.term.0);
  503. assert_eq!(10 - start, log.offset(10));
  504. }
  505. #[test]
  506. fn test_check_start_index() {
  507. let (start, end, log) = default_log_array();
  508. assert!(start < end);
  509. assert!(catch_unwind(|| log.check_at_index(start - 8)).is_err());
  510. assert!(catch_unwind(|| log.check_at_index(start - 1)).is_err());
  511. assert!(catch_unwind(|| log.check_at_index(start)).is_ok());
  512. assert!(catch_unwind(|| log.check_at_index(start + 1)).is_ok());
  513. assert!(catch_unwind(|| log.check_at_index(end - 1)).is_ok());
  514. assert!(catch_unwind(|| log.check_at_index(end)).is_err());
  515. assert!(catch_unwind(|| log.check_at_index(end + 1)).is_err());
  516. assert!(catch_unwind(|| log.check_at_index(end + 5)).is_err());
  517. }
  518. #[test]
  519. fn test_check_range_index() {
  520. let (start, end, log) = default_log_array();
  521. assert!(start < end);
  522. assert!(catch_unwind(|| log.check_range_index(start - 8)).is_err());
  523. assert!(catch_unwind(|| log.check_range_index(start - 1)).is_err());
  524. assert!(catch_unwind(|| log.check_range_index(start)).is_ok());
  525. assert!(catch_unwind(|| log.check_range_index(start + 1)).is_ok());
  526. assert!(catch_unwind(|| log.check_range_index(end - 1)).is_ok());
  527. assert!(catch_unwind(|| log.check_range_index(end)).is_ok());
  528. assert!(catch_unwind(|| log.check_range_index(end + 1)).is_err());
  529. assert!(catch_unwind(|| log.check_range_index(end + 5)).is_err());
  530. }
  531. #[test]
  532. fn test_check_middle_index() {
  533. let (start, end, log) = default_log_array();
  534. assert!(start < end);
  535. assert!(catch_unwind(|| log.check_middle_index(start - 8)).is_err());
  536. assert!(catch_unwind(|| log.check_middle_index(start - 1)).is_err());
  537. assert!(catch_unwind(|| log.check_middle_index(start)).is_err());
  538. assert!(catch_unwind(|| log.check_middle_index(start + 1)).is_ok());
  539. assert!(catch_unwind(|| log.check_middle_index(end - 1)).is_ok());
  540. assert!(catch_unwind(|| log.check_middle_index(end)).is_err());
  541. assert!(catch_unwind(|| log.check_middle_index(end + 1)).is_err());
  542. assert!(catch_unwind(|| log.check_middle_index(end + 5)).is_err());
  543. }
  544. #[test]
  545. fn test_check_one_element() {
  546. let log = make_log_array(0);
  547. assert!(catch_unwind(|| log.check_one_element()).is_err());
  548. }
  549. #[test]
  550. fn test_integration_test() {
  551. let mut log = make_log_array(1);
  552. log.add_command(Term(3), 19);
  553. log.push(LogEntry {
  554. term: Term(3),
  555. index: 2,
  556. command: 3,
  557. });
  558. log.add_command(Term(4), 20);
  559. log.push(LogEntry {
  560. term: Term(4),
  561. index: 4,
  562. command: 7,
  563. });
  564. for i in 0..100 {
  565. log.add_command(Term(5), i);
  566. }
  567. assert_eq!(0, log.start());
  568. assert_eq!(105, log.end());
  569. assert_eq!((0, Term(0)), log.first_index_term().into());
  570. assert_eq!((104, Term(5)), log.last_index_term().into());
  571. assert_eq!(8, log.at(8).index);
  572. assert_eq!(5, log[8].term.0);
  573. assert_eq!(7, log[4].command);
  574. log.truncate(50);
  575. // End changed, start does not.
  576. assert_eq!(0, log.start());
  577. assert_eq!(50, log.end());
  578. assert_eq!((49, Term(5)), log.last_index_term().into());
  579. assert_eq!(49, log.at(49).index);
  580. assert_eq!(44, log[49].command);
  581. assert_eq!(5, log.at(5).term.0);
  582. // Cannot assert 50 is out of range. log is mut and cannot be used in
  583. // catch_unwind().
  584. // Snapshot is not changed.
  585. assert_eq!(((0, Term(0)).into(), [1, 2, 3].as_ref()), log.snapshot());
  586. log.shift(5, vec![]);
  587. // Start changed, end did not;
  588. assert_eq!(5, log.start());
  589. assert_eq!((5, Term(5)), log.first_index_term().into());
  590. assert_eq!(5, log.at(5).index);
  591. assert_eq!(5, log.at(5).term.0);
  592. // Cannot assert 4 is out of range. log is mut and cannot be used in
  593. // catch_unwind().
  594. // Snapshot is changed, too.
  595. assert_eq!(((5, Term(5)).into(), [].as_ref()), log.snapshot());
  596. // Ranged accessors.
  597. assert_eq!(45, log.all().len());
  598. assert_eq!(10, log.after(40).len());
  599. assert_eq!(20, log.between(20, 40).len());
  600. // Reset!
  601. log.reset(9, Term(7), vec![7, 8, 9]);
  602. assert_eq!(10, log.end());
  603. assert_eq!(1, log.all().len());
  604. assert_eq!(log.first_index_term(), log.last_index_term());
  605. assert_eq!(((9, Term(7)).into(), [7, 8, 9].as_ref()), log.snapshot());
  606. }
  607. }