1use std::sync::Arc;
16
17use clap::{Parser, ValueEnum};
18use common_error::ext::BoxedError;
19use common_meta::kv_backend::KvBackendRef;
20use common_meta::kv_backend::chroot::ChrootKvBackend;
21use common_meta::kv_backend::etcd::EtcdStore;
22use meta_srv::metasrv::BackendClientOptions;
23use meta_srv::utils::etcd::create_etcd_client_with_tls;
24use serde::{Deserialize, Serialize};
25use servers::tls::{TlsMode, TlsOption};
26use snafu::OptionExt;
27
28use crate::error::{EmptyStoreAddrsSnafu, InvalidArgumentsSnafu};
29
30#[derive(Clone, Debug, PartialEq, Serialize, Default, Deserialize, ValueEnum)]
32#[serde(rename_all = "snake_case")]
33#[allow(clippy::enum_variant_names)]
34pub enum BackendImpl {
35 #[default]
37 EtcdStore,
38 MemoryStore,
40 #[cfg(feature = "pg_kvbackend")]
41 PostgresStore,
43 #[cfg(feature = "mysql_kvbackend")]
44 MysqlStore,
46 RaftEngineStore,
48}
49
50#[derive(Debug, Default, Parser)]
51pub struct StoreConfig {
52 #[clap(long, alias = "store-addr", value_delimiter = ',', num_args = 1..)]
63 pub store_addrs: Vec<String>,
64
65 #[clap(long, default_value = "128")]
67 pub max_txn_ops: usize,
68
69 #[clap(long, value_enum, default_value = "etcd-store")]
71 pub backend: BackendImpl,
72
73 #[clap(long, default_value = "")]
75 pub store_key_prefix: String,
76
77 #[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
79 #[clap(long, default_value = common_meta::kv_backend::DEFAULT_META_TABLE_NAME)]
80 pub meta_table_name: String,
81
82 #[cfg(feature = "pg_kvbackend")]
84 #[clap(long)]
85 pub meta_schema_name: Option<String>,
86
87 #[cfg(feature = "pg_kvbackend")]
89 #[clap(long, default_value_t = true)]
90 pub auto_create_schema: bool,
91
92 #[clap(long = "backend-tls-mode", value_enum, default_value = "disable")]
94 pub backend_tls_mode: TlsMode,
95
96 #[clap(long = "backend-tls-cert-path", default_value = "")]
98 pub backend_tls_cert_path: String,
99
100 #[clap(long = "backend-tls-key-path", default_value = "")]
102 pub backend_tls_key_path: String,
103
104 #[clap(long = "backend-tls-ca-cert-path", default_value = "")]
106 pub backend_tls_ca_cert_path: String,
107
108 #[clap(long = "backend-tls-watch")]
110 pub backend_tls_watch: bool,
111}
112
113impl StoreConfig {
114 pub fn tls_config(&self) -> Option<TlsOption> {
115 if self.backend_tls_mode != TlsMode::Disable {
116 Some(TlsOption {
117 mode: self.backend_tls_mode,
118 cert_path: self.backend_tls_cert_path.clone(),
119 key_path: self.backend_tls_key_path.clone(),
120 ca_cert_path: self.backend_tls_ca_cert_path.clone(),
121 watch: self.backend_tls_watch,
122 })
123 } else {
124 None
125 }
126 }
127
128 fn sanitize_store_addrs(&self) -> Vec<String> {
130 self.store_addrs
131 .iter()
132 .map(|addr| common_meta::kv_backend::util::sanitize_connection_string(addr))
133 .collect()
134 }
135
136 pub async fn build(&self) -> Result<KvBackendRef, BoxedError> {
138 let max_txn_ops = self.max_txn_ops;
139 let store_addrs = &self.store_addrs;
140 if store_addrs.is_empty() {
141 EmptyStoreAddrsSnafu.fail().map_err(BoxedError::new)
142 } else {
143 common_telemetry::info!(
144 "Building kvbackend with store addrs: {:?}, backend: {:?}",
145 &self.sanitize_store_addrs(),
146 self.backend
147 );
148 let kvbackend = match self.backend {
149 BackendImpl::EtcdStore => {
150 let tls_config = self.tls_config();
151 let etcd_client = create_etcd_client_with_tls(
152 store_addrs,
153 &BackendClientOptions::default(),
154 tls_config.as_ref(),
155 )
156 .await
157 .map_err(BoxedError::new)?;
158 Ok(EtcdStore::with_etcd_client(etcd_client, max_txn_ops))
159 }
160 #[cfg(feature = "pg_kvbackend")]
161 BackendImpl::PostgresStore => {
162 let table_name = &self.meta_table_name;
163 let tls_config = self.tls_config();
164 Ok(meta_srv::utils::postgres::build_postgres_kv_backend(
165 store_addrs,
166 None,
167 tls_config,
168 self.meta_schema_name.as_deref(),
169 table_name,
170 max_txn_ops,
171 self.auto_create_schema,
172 )
173 .await
174 .map_err(BoxedError::new)?)
175 }
176 #[cfg(feature = "mysql_kvbackend")]
177 BackendImpl::MysqlStore => {
178 let table_name = &self.meta_table_name;
179 let tls_config = self.tls_config();
180 Ok(meta_srv::utils::mysql::build_mysql_kv_backend(
181 store_addrs,
182 tls_config.as_ref(),
183 table_name,
184 max_txn_ops,
185 )
186 .await
187 .map_err(BoxedError::new)?)
188 }
189 #[cfg(not(test))]
190 BackendImpl::MemoryStore => {
191 use crate::error::UnsupportedMemoryBackendSnafu;
192
193 UnsupportedMemoryBackendSnafu
194 .fail()
195 .map_err(BoxedError::new)
196 }
197 #[cfg(test)]
198 BackendImpl::MemoryStore => {
199 use common_meta::kv_backend::memory::MemoryKvBackend;
200
201 Ok(Arc::new(MemoryKvBackend::default()) as _)
202 }
203 BackendImpl::RaftEngineStore => {
204 let url = store_addrs
205 .first()
206 .context(InvalidArgumentsSnafu {
207 msg: "empty store addresses".to_string(),
208 })
209 .map_err(BoxedError::new)?;
210 let kvbackend =
211 standalone::build_metadata_kv_from_url(url).map_err(BoxedError::new)?;
212
213 Ok(kvbackend)
214 }
215 };
216 if self.store_key_prefix.is_empty() {
217 kvbackend
218 } else {
219 let chroot_kvbackend =
220 ChrootKvBackend::new(self.store_key_prefix.as_bytes().to_vec(), kvbackend?);
221 Ok(Arc::new(chroot_kvbackend))
222 }
223 }
224 }
225}