Skip to main content

solana_runtime/
bank_forks_controller.rs

1use {
2    crate::{bank::Bank, installed_scheduler_pool::BankWithScheduler},
3    crossbeam_channel::{Receiver, RecvTimeoutError, Sender, bounded},
4    log::warn,
5    solana_clock::Slot,
6    solana_metrics::datapoint_info,
7    std::{
8        fmt,
9        sync::{Arc, Mutex},
10        time::{Duration, Instant},
11    },
12    thiserror::Error,
13};
14
15const CHANNEL_SIZE: usize = 16;
16
17#[derive(Debug, Error)]
18pub enum BankForksControllerError {
19    #[error("bank forks controller is disconnected")]
20    Disconnected,
21    #[error("bank to insert for slot {0} was stale, failed to insert")]
22    UnableToInsertStaleBank(Slot),
23}
24
25pub enum BankForksCommand {
26    InsertBank {
27        bank: Box<Bank>,
28        response_sender: Sender<Option<BankWithScheduler>>,
29    },
30    ClearBank {
31        slot: Slot,
32        response_sender: Sender<()>,
33    },
34}
35
36pub struct SetRootCommand {
37    pub parent_slot: Slot,
38    pub new_root: Slot,
39    pub highest_super_majority_root: Option<Slot>,
40}
41
42impl BankForksCommand {
43    fn metric_slot(&self) -> Slot {
44        match self {
45            Self::InsertBank { bank, .. } => bank.slot(),
46            Self::ClearBank { slot, .. } => *slot,
47        }
48    }
49}
50
51impl fmt::Display for BankForksCommand {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            Self::InsertBank { .. } => write!(f, "insert_bank"),
55            Self::ClearBank { .. } => write!(f, "clear_bank"),
56        }
57    }
58}
59
60pub trait BankForksController: Send + Sync {
61    fn insert_bank(&self, bank: Bank) -> Result<BankWithScheduler, BankForksControllerError>;
62
63    fn enqueue_set_root(
64        &self,
65        parent_slot: Slot,
66        new_root: Slot,
67        highest_super_majority_root: Option<Slot>,
68    );
69
70    fn clear_bank(&self, slot: Slot) -> Result<(), BankForksControllerError>;
71}
72
73/// Handle used by non-replay threads to serialize BankForks writes onto ReplayStage.
74#[derive(Clone)]
75pub struct BankForksControllerHandle {
76    sender: Sender<BankForksCommand>,
77    pending_set_root: Arc<Mutex<Option<SetRootCommand>>>,
78    set_root_signal_sender: Sender<()>,
79}
80
81impl BankForksControllerHandle {
82    pub fn new() -> (Self, BankForksCommandReceiver) {
83        let (sender, receiver) = bounded(CHANNEL_SIZE);
84        let (set_root_signal_sender, set_root_signal_receiver) = bounded(1);
85        let pending_set_root = Arc::new(Mutex::new(None));
86        (
87            Self {
88                sender,
89                pending_set_root: pending_set_root.clone(),
90                set_root_signal_sender,
91            },
92            BankForksCommandReceiver {
93                receiver,
94                pending_set_root,
95                set_root_signal_receiver,
96            },
97        )
98    }
99
100    fn send_command<T>(
101        &self,
102        command: BankForksCommand,
103        response_receiver: Receiver<T>,
104    ) -> Result<T, BankForksControllerError> {
105        let command_name = command.to_string();
106        let slot = command.metric_slot();
107        let queue_len_before_send = self.sender.len();
108        let total_start = Instant::now();
109        let send_start = Instant::now();
110        if self.sender.send(command).is_err() {
111            return Err(BankForksControllerError::Disconnected);
112        }
113        let send_us = send_start.elapsed().as_micros() as i64;
114
115        let response_wait_start = Instant::now();
116        let response = loop {
117            match response_receiver.recv_timeout(Duration::from_millis(100)) {
118                Ok(response) => break response,
119                Err(RecvTimeoutError::Disconnected) => {
120                    return Err(BankForksControllerError::Disconnected);
121                }
122                Err(RecvTimeoutError::Timeout) => (),
123            }
124            warn!(
125                "Replay is stuck, waiting for {}ms no response to {command_name} for {slot}",
126                response_wait_start.elapsed().as_millis()
127            );
128        };
129
130        let response_wait_us = response_wait_start.elapsed().as_micros() as i64;
131        let total_us = total_start.elapsed().as_micros() as i64;
132        datapoint_info!(
133            "bank_forks_controller-command",
134            ("command", command_name, String),
135            ("slot", slot as i64, i64),
136            ("queue_len_before_send", queue_len_before_send as i64, i64),
137            ("send_us", send_us, i64),
138            ("response_wait_us", response_wait_us, i64),
139            ("total_us", total_us, i64),
140        );
141
142        Ok(response)
143    }
144}
145
146impl BankForksController for BankForksControllerHandle {
147    fn insert_bank(&self, bank: Bank) -> Result<BankWithScheduler, BankForksControllerError> {
148        let slot = bank.slot();
149        let (response_sender, response_receiver) = bounded(1);
150        let bank = self.send_command(
151            BankForksCommand::InsertBank {
152                bank: Box::new(bank),
153                response_sender,
154            },
155            response_receiver,
156        )?;
157        bank.ok_or(BankForksControllerError::UnableToInsertStaleBank(slot))
158    }
159
160    fn enqueue_set_root(
161        &self,
162        parent_slot: Slot,
163        new_root: Slot,
164        highest_super_majority_root: Option<Slot>,
165    ) {
166        let total_start = Instant::now();
167        let command = SetRootCommand {
168            parent_slot,
169            new_root,
170            highest_super_majority_root,
171        };
172
173        {
174            let mut pending_set_root = self.pending_set_root.lock().unwrap();
175            // Replay only needs to process the highest pending root.
176            if pending_set_root
177                .as_ref()
178                .is_none_or(|pending| command.new_root > pending.new_root)
179            {
180                *pending_set_root = Some(command);
181            }
182        }
183        let _ = self.set_root_signal_sender.try_send(());
184
185        let total_us = total_start.elapsed().as_micros() as i64;
186
187        datapoint_info!(
188            "bank_forks_controller-command",
189            ("command", "set_root", String),
190            ("slot", new_root as i64, i64),
191            ("total_us", total_us, i64),
192        );
193    }
194
195    fn clear_bank(&self, slot: Slot) -> Result<(), BankForksControllerError> {
196        let (response_sender, response_receiver) = bounded(1);
197        self.send_command(
198            BankForksCommand::ClearBank {
199                slot,
200                response_sender,
201            },
202            response_receiver,
203        )
204    }
205}
206
207pub struct BankForksCommandReceiver {
208    receiver: Receiver<BankForksCommand>,
209    pending_set_root: Arc<Mutex<Option<SetRootCommand>>>,
210    set_root_signal_receiver: Receiver<()>,
211}
212
213impl BankForksCommandReceiver {
214    pub fn receiver(&self) -> &Receiver<BankForksCommand> {
215        &self.receiver
216    }
217
218    pub fn set_root_signal_receiver(&self) -> &Receiver<()> {
219        &self.set_root_signal_receiver
220    }
221
222    pub fn take_set_root_command(&self) -> Option<SetRootCommand> {
223        self.pending_set_root.lock().unwrap().take()
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use {
230        super::*,
231        crate::{bank::SlotLeader, bank_forks::BankForks, genesis_utils::create_genesis_config},
232        std::{thread, time::Duration},
233    };
234
235    #[test]
236    fn test_bank_forks_controller_keeps_highest_pending_set_root() {
237        let (controller, receiver) = BankForksControllerHandle::new();
238
239        controller.enqueue_set_root(5, 5, Some(5));
240        controller.enqueue_set_root(3, 3, Some(3));
241        assert_eq!(receiver.take_set_root_command().unwrap().new_root, 5);
242        assert!(receiver.take_set_root_command().is_none());
243
244        controller.enqueue_set_root(3, 3, Some(3));
245        controller.enqueue_set_root(5, 5, Some(5));
246        assert_eq!(receiver.take_set_root_command().unwrap().new_root, 5);
247    }
248
249    #[test]
250    fn test_bank_forks_controller_signals_pending_set_root() {
251        let (controller, receiver) = BankForksControllerHandle::new();
252
253        controller.enqueue_set_root(1, 1, Some(1));
254        receiver
255            .set_root_signal_receiver()
256            .recv_timeout(Duration::from_secs(1))
257            .unwrap();
258        assert_eq!(receiver.take_set_root_command().unwrap().new_root, 1);
259
260        controller.enqueue_set_root(2, 2, Some(2));
261        controller.enqueue_set_root(3, 3, Some(3));
262        receiver
263            .set_root_signal_receiver()
264            .recv_timeout(Duration::from_secs(1))
265            .unwrap();
266        assert!(receiver.set_root_signal_receiver().try_recv().is_err());
267        assert_eq!(receiver.take_set_root_command().unwrap().new_root, 3);
268    }
269
270    #[test]
271    fn test_bank_forks_controller_insert_and_set_root() {
272        let genesis = create_genesis_config(10_000);
273        let bank_forks = BankForks::new_rw_arc(Bank::new_for_tests(&genesis.genesis_config));
274        let (controller, receiver) = BankForksControllerHandle::new();
275        let replay_bank_forks = bank_forks.clone();
276        let (root_sender, root_receiver) = bounded(1);
277        let replay_thread = thread::spawn(move || {
278            loop {
279                if let Some(command) = receiver.take_set_root_command() {
280                    {
281                        let mut bank_forks = replay_bank_forks.write().unwrap();
282                        bank_forks.set_root(command.new_root, None, None);
283                    }
284                    root_sender.send(command.new_root).unwrap();
285                }
286                let command = match receiver.receiver().recv_timeout(Duration::from_millis(10)) {
287                    Ok(command) => command,
288                    Err(RecvTimeoutError::Timeout) => continue,
289                    Err(RecvTimeoutError::Disconnected) => break,
290                };
291                match command {
292                    BankForksCommand::InsertBank {
293                        bank,
294                        response_sender,
295                    } => {
296                        let bank = {
297                            let mut bank_forks = replay_bank_forks.write().unwrap();
298                            bank_forks.insert(*bank)
299                        };
300                        response_sender.send(Some(bank)).unwrap();
301                    }
302                    BankForksCommand::ClearBank {
303                        slot,
304                        response_sender,
305                    } => {
306                        let bank_to_clear =
307                            replay_bank_forks.read().unwrap().get_with_scheduler(slot);
308                        if let Some(bank) = bank_to_clear {
309                            let _ = bank.wait_for_completed_scheduler();
310                        }
311
312                        {
313                            let mut bank_forks = replay_bank_forks.write().unwrap();
314                            bank_forks.clear_bank(slot, false);
315                        }
316                        response_sender.send(()).unwrap();
317                    }
318                }
319            }
320        });
321
322        let parent_bank = bank_forks.read().unwrap().root_bank();
323        let bank = Bank::new_from_parent(parent_bank, SlotLeader::default(), 1);
324        bank.freeze();
325        let inserted_bank = controller.insert_bank(bank).unwrap();
326        assert_eq!(inserted_bank.slot(), 1);
327        assert!(bank_forks.read().unwrap().get(1).is_some());
328
329        controller.enqueue_set_root(1, 1, None);
330        assert_eq!(root_receiver.recv().unwrap(), 1);
331        assert_eq!(bank_forks.read().unwrap().root(), 1);
332
333        let parent_bank = bank_forks.read().unwrap().root_bank();
334        let bank = Bank::new_from_parent(parent_bank, SlotLeader::default(), 2);
335        bank.freeze();
336        let inserted_bank = controller.insert_bank(bank).unwrap();
337        assert_eq!(inserted_bank.slot(), 2);
338        assert!(bank_forks.read().unwrap().get(2).is_some());
339
340        controller.clear_bank(2).unwrap();
341        assert!(bank_forks.read().unwrap().get(2).is_none());
342
343        drop(controller);
344        replay_thread.join().unwrap();
345    }
346}