Line data Source code
1 : use desim::proto::SimEvent;
2 : use tracing::debug;
3 :
4 284029 : #[derive(Debug, Clone, PartialEq, Eq)]
5 : enum NodeKind {
6 : Unknown,
7 : Safekeeper,
8 : WalProposer,
9 : }
10 :
11 : impl Default for NodeKind {
12 84107 : fn default() -> Self {
13 84107 : Self::Unknown
14 84107 : }
15 : }
16 :
17 : /// Simulation state of walproposer/safekeeper, derived from the simulation logs.
18 84107 : #[derive(Clone, Debug, Default)]
19 : struct NodeInfo {
20 : kind: NodeKind,
21 :
22 : // walproposer
23 : is_sync: bool,
24 : term: u64,
25 : epoch_lsn: u64,
26 :
27 : // safekeeper
28 : commit_lsn: u64,
29 : flush_lsn: u64,
30 : }
31 :
32 : impl NodeInfo {
33 149342 : fn init_kind(&mut self, kind: NodeKind) {
34 149342 : if self.kind == NodeKind::Unknown {
35 84109 : self.kind = kind;
36 84109 : } else {
37 65233 : assert!(self.kind == kind);
38 : }
39 149342 : }
40 :
41 149342 : fn started(&mut self, data: &str) {
42 149342 : let mut parts = data.split(';');
43 149342 : assert!(parts.next().unwrap() == "started");
44 149342 : match parts.next().unwrap() {
45 149342 : "safekeeper" => {
46 77239 : self.init_kind(NodeKind::Safekeeper);
47 77239 : }
48 72103 : "walproposer" => {
49 72103 : self.init_kind(NodeKind::WalProposer);
50 72103 : let is_sync: u8 = parts.next().unwrap().parse().unwrap();
51 72103 : self.is_sync = is_sync != 0;
52 72103 : }
53 0 : _ => unreachable!(),
54 : }
55 149342 : }
56 : }
57 :
58 : /// Global state of the simulation, derived from the simulation logs.
59 4002 : #[derive(Debug, Default)]
60 : struct GlobalState {
61 : nodes: Vec<NodeInfo>,
62 : commit_lsn: u64,
63 : write_lsn: u64,
64 : max_write_lsn: u64,
65 :
66 : written_wal: u64,
67 : written_records: u64,
68 : }
69 :
70 : impl GlobalState {
71 4002 : fn new() -> Self {
72 4002 : Default::default()
73 4002 : }
74 :
75 288250 : fn get(&mut self, id: u32) -> &mut NodeInfo {
76 288250 : let id = id as usize;
77 288250 : if id >= self.nodes.len() {
78 84107 : self.nodes.resize(id + 1, NodeInfo::default());
79 204143 : }
80 288250 : &mut self.nodes[id]
81 288250 : }
82 : }
83 :
84 : /// Try to find inconsistencies in the simulation log.
85 4002 : pub fn validate_events(events: Vec<SimEvent>) {
86 4002 : const INITDB_LSN: u64 = 21623024;
87 4002 :
88 4002 : let hook = std::panic::take_hook();
89 4002 : scopeguard::defer_on_success! {
90 4002 : std::panic::set_hook(hook);
91 4002 : };
92 :
93 4002 : let mut state = GlobalState::new();
94 4002 : state.max_write_lsn = INITDB_LSN;
95 :
96 222798 : for event in events {
97 218796 : debug!("{:?}", event);
98 :
99 218796 : let node = state.get(event.node);
100 218796 : if event.data.starts_with("started;") {
101 149342 : node.started(&event.data);
102 149342 : continue;
103 69454 : }
104 69454 : assert!(node.kind != NodeKind::Unknown);
105 :
106 : // drop reference to unlock state
107 69454 : let mut node = node.clone();
108 69454 :
109 69454 : let mut parts = event.data.split(';');
110 69454 : match node.kind {
111 58278 : NodeKind::Safekeeper => match parts.next().unwrap() {
112 58278 : "tli_loaded" => {
113 58278 : let flush_lsn: u64 = parts.next().unwrap().parse().unwrap();
114 58278 : let commit_lsn: u64 = parts.next().unwrap().parse().unwrap();
115 58278 : node.flush_lsn = flush_lsn;
116 58278 : node.commit_lsn = commit_lsn;
117 58278 : }
118 0 : _ => unreachable!(),
119 : },
120 : NodeKind::WalProposer => {
121 11176 : match parts.next().unwrap() {
122 11176 : "prop_elected" => {
123 5869 : let prop_lsn: u64 = parts.next().unwrap().parse().unwrap();
124 5869 : let prop_term: u64 = parts.next().unwrap().parse().unwrap();
125 5869 : let prev_lsn: u64 = parts.next().unwrap().parse().unwrap();
126 5869 : let prev_term: u64 = parts.next().unwrap().parse().unwrap();
127 5869 :
128 5869 : assert!(prop_lsn >= prev_lsn);
129 5869 : assert!(prop_term >= prev_term);
130 :
131 5869 : assert!(prop_lsn >= state.commit_lsn);
132 :
133 5869 : if prop_lsn > state.write_lsn {
134 432 : assert!(prop_lsn <= state.max_write_lsn);
135 432 : debug!(
136 2 : "moving write_lsn up from {} to {}",
137 2 : state.write_lsn, prop_lsn
138 2 : );
139 432 : state.write_lsn = prop_lsn;
140 5437 : }
141 5869 : if prop_lsn < state.write_lsn {
142 1011 : debug!(
143 2 : "moving write_lsn down from {} to {}",
144 2 : state.write_lsn, prop_lsn
145 2 : );
146 1011 : state.write_lsn = prop_lsn;
147 4858 : }
148 :
149 5869 : node.epoch_lsn = prop_lsn;
150 5869 : node.term = prop_term;
151 : }
152 5307 : "write_wal" => {
153 2771 : assert!(!node.is_sync);
154 2771 : let start_lsn: u64 = parts.next().unwrap().parse().unwrap();
155 2771 : let end_lsn: u64 = parts.next().unwrap().parse().unwrap();
156 2771 : let cnt: u64 = parts.next().unwrap().parse().unwrap();
157 2771 :
158 2771 : let size = end_lsn - start_lsn;
159 2771 : state.written_wal += size;
160 2771 : state.written_records += cnt;
161 2771 :
162 2771 : // TODO: If we allow writing WAL before winning the election
163 2771 :
164 2771 : assert!(start_lsn >= state.commit_lsn);
165 2771 : assert!(end_lsn >= start_lsn);
166 : // assert!(start_lsn == state.write_lsn);
167 2771 : state.write_lsn = end_lsn;
168 2771 :
169 2771 : if end_lsn > state.max_write_lsn {
170 2326 : state.max_write_lsn = end_lsn;
171 2326 : }
172 : }
173 2536 : "commit_lsn" => {
174 2536 : let lsn: u64 = parts.next().unwrap().parse().unwrap();
175 2536 : assert!(lsn >= state.commit_lsn);
176 2536 : state.commit_lsn = lsn;
177 : }
178 0 : _ => unreachable!(),
179 : }
180 : }
181 0 : _ => unreachable!(),
182 : }
183 :
184 : // update the node in the state struct
185 69454 : *state.get(event.node) = node;
186 : }
187 4002 : }
|