1use std::str::FromStr;
16
17use common_meta::datanode::DatanodeStatKey;
18use lazy_static::lazy_static;
19use regex::Regex;
20use serde::{Deserialize, Serialize};
21use snafu::{ensure, OptionExt, ResultExt};
22
23use crate::error;
24use crate::error::Result;
25
26pub(crate) const DATANODE_LEASE_PREFIX: &str = "__meta_datanode_lease";
27const INACTIVE_REGION_PREFIX: &str = "__meta_inactive_region";
28
29const DATANODE_STAT_PREFIX: &str = "__meta_datanode_stat";
30
31lazy_static! {
32 pub(crate) static ref DATANODE_LEASE_KEY_PATTERN: Regex =
33 Regex::new(&format!("^{DATANODE_LEASE_PREFIX}-([0-9]+)-([0-9]+)$")).unwrap();
34 static ref DATANODE_STAT_KEY_PATTERN: Regex =
35 Regex::new(&format!("^{DATANODE_STAT_PREFIX}-([0-9]+)-([0-9]+)$")).unwrap();
36 static ref INACTIVE_REGION_KEY_PATTERN: Regex = Regex::new(&format!(
37 "^{INACTIVE_REGION_PREFIX}-([0-9]+)-([0-9]+)-([0-9]+)$"
38 ))
39 .unwrap();
40}
41
42#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
43pub struct DatanodeLeaseKey {
44 pub node_id: u64,
45}
46
47impl DatanodeLeaseKey {
48 pub fn prefix_key() -> Vec<u8> {
49 format!("{DATANODE_LEASE_PREFIX}-0-").into_bytes()
50 }
51}
52
53impl From<&DatanodeLeaseKey> for DatanodeStatKey {
54 fn from(lease_key: &DatanodeLeaseKey) -> Self {
55 DatanodeStatKey {
56 node_id: lease_key.node_id,
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
62pub struct InactiveRegionKey {
63 pub node_id: u64,
64 pub region_id: u64,
65}
66
67impl InactiveRegionKey {
68 pub fn get_prefix_by_cluster() -> Vec<u8> {
69 format!("{}-0-", INACTIVE_REGION_PREFIX).into_bytes()
70 }
71}
72
73impl From<InactiveRegionKey> for Vec<u8> {
74 fn from(value: InactiveRegionKey) -> Self {
75 format!(
76 "{}-0-{}-{}",
77 INACTIVE_REGION_PREFIX, value.node_id, value.region_id
78 )
79 .into_bytes()
80 }
81}
82
83impl FromStr for InactiveRegionKey {
84 type Err = error::Error;
85
86 fn from_str(key: &str) -> Result<Self> {
87 let caps = INACTIVE_REGION_KEY_PATTERN
88 .captures(key)
89 .context(error::InvalidInactiveRegionKeySnafu { key })?;
90
91 ensure!(
92 caps.len() == 4,
93 error::InvalidInactiveRegionKeySnafu { key }
94 );
95
96 let node_id = caps[2].to_string();
97 let region_id = caps[3].to_string();
98 let node_id: u64 = node_id.parse().context(error::ParseNumSnafu {
99 err_msg: format!("invalid node_id: {node_id}"),
100 })?;
101 let region_id: u64 = region_id.parse().context(error::ParseNumSnafu {
102 err_msg: format!("invalid region_id: {region_id}"),
103 })?;
104
105 Ok(Self { node_id, region_id })
106 }
107}
108
109impl TryFrom<Vec<u8>> for InactiveRegionKey {
110 type Error = error::Error;
111
112 fn try_from(bytes: Vec<u8>) -> Result<Self> {
113 String::from_utf8(bytes)
114 .context(error::InvalidRegionKeyFromUtf8Snafu {})
115 .map(|x| x.parse())?
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_stat_key_round_trip() {
125 let key = DatanodeStatKey { node_id: 1 };
126
127 let key_bytes: Vec<u8> = key.into();
128 let new_key: DatanodeStatKey = key_bytes.try_into().unwrap();
129
130 assert_eq!(1, new_key.node_id);
131 }
132
133 #[test]
134 fn test_lease_key_round_trip() {
135 let key = DatanodeLeaseKey { node_id: 1 };
136
137 let key_bytes: Vec<u8> = key.clone().try_into().unwrap();
138 let new_key: DatanodeLeaseKey = key_bytes.try_into().unwrap();
139
140 assert_eq!(new_key, key);
141 }
142
143 #[test]
144 fn test_lease_key_to_stat_key() {
145 let lease_key = DatanodeLeaseKey { node_id: 101 };
146
147 let stat_key: DatanodeStatKey = (&lease_key).into();
148
149 assert_eq!(101, stat_key.node_id);
150 }
151
152 #[test]
153 fn test_inactive_region_key_round_trip() {
154 let key = InactiveRegionKey {
155 node_id: 1,
156 region_id: 2,
157 };
158
159 let key_bytes: Vec<u8> = key.into();
160 let new_key: InactiveRegionKey = key_bytes.try_into().unwrap();
161
162 assert_eq!(new_key, key);
163 }
164}