meta_srv/
state.rs

1// Copyright 2023 Greptime Team
2//
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//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::{Arc, RwLock};
16
17pub type StateRef = Arc<RwLock<State>>;
18
19/// State transition.
20/// ```text
21///                     +------------------------------+
22///                     |                              |
23///                     |                              |
24///                     |                              |
25/// +-------------------v--------------------+         |
26/// | LeaderState{enable_leader_cache:false} |         |
27/// +-------------------+--------------------+         |
28///                     |                              |
29///                     |                              |
30///           +---------v---------+                    |
31///           | Init Leader Cache |                    |
32///           +---------+---------+                    |
33///                     |                              |
34///                     |                              |
35/// +-------------------v-------------------+          |
36/// | LeaderState{enable_leader_cache:true} |          |
37/// +-------------------+-------------------+          |
38///                     |                              |
39///                     |                              |
40///             +-------v-------+                      |
41///             | FollowerState |                      |
42///             +-------+-------+                      |
43///                     |                              |
44///                     |                              |
45///                     +------------------------------+
46///```
47#[derive(Debug, Clone)]
48pub enum State {
49    Leader(LeaderState),
50    Follower(FollowerState),
51}
52
53#[derive(Debug, Clone)]
54pub struct LeaderState {
55    // Disables the leader cache during initiation
56    pub enable_leader_cache: bool,
57
58    pub server_addr: String,
59}
60
61#[derive(Debug, Clone)]
62pub struct FollowerState {
63    pub server_addr: String,
64}
65
66impl State {
67    pub fn follower(server_addr: String) -> State {
68        Self::Follower(FollowerState { server_addr })
69    }
70
71    pub fn leader(server_addr: String, enable_leader_cache: bool) -> State {
72        Self::Leader(LeaderState {
73            enable_leader_cache,
74            server_addr,
75        })
76    }
77
78    pub fn enable_leader_cache(&self) -> bool {
79        match &self {
80            State::Leader(leader) => leader.enable_leader_cache,
81            State::Follower(_) => false,
82        }
83    }
84
85    pub fn next_state<F>(&mut self, f: F)
86    where
87        F: FnOnce(&State) -> State,
88    {
89        *self = f(self);
90    }
91}
92
93pub fn become_leader(enable_leader_cache: bool) -> impl FnOnce(&State) -> State {
94    move |prev| match prev {
95        State::Leader(leader) => State::Leader(LeaderState { ..leader.clone() }),
96        State::Follower(follower) => State::Leader(LeaderState {
97            server_addr: follower.server_addr.to_string(),
98            enable_leader_cache,
99        }),
100    }
101}
102
103pub fn become_follower() -> impl FnOnce(&State) -> State {
104    move |prev| match prev {
105        State::Leader(leader) => State::Follower(FollowerState {
106            server_addr: leader.server_addr.to_string(),
107        }),
108        State::Follower(follower) => State::Follower(FollowerState { ..follower.clone() }),
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use std::assert_matches::assert_matches;
115
116    use crate::state::{become_follower, become_leader, FollowerState, LeaderState, State};
117
118    #[tokio::test]
119    async fn test_next_state() {
120        let mut state = State::follower("test".to_string());
121
122        state.next_state(become_leader(false));
123
124        assert_matches!(
125            state,
126            State::Leader(LeaderState {
127                enable_leader_cache: false,
128                ..
129            })
130        );
131
132        state.next_state(become_leader(false));
133
134        assert_matches!(
135            state,
136            State::Leader(LeaderState {
137                enable_leader_cache: false,
138                ..
139            })
140        );
141
142        state.next_state(become_follower());
143
144        assert_matches!(state, State::Follower(FollowerState { .. }));
145
146        state.next_state(become_follower());
147
148        assert_matches!(state, State::Follower(FollowerState { .. }));
149    }
150}