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