Skip to main content

meta_srv/
bootstrap.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::net::SocketAddr;
16use std::sync::Arc;
17
18use api::v1::meta::cluster_server::ClusterServer;
19use api::v1::meta::config_server::ConfigServer;
20use api::v1::meta::heartbeat_server::HeartbeatServer;
21use api::v1::meta::procedure_service_server::ProcedureServiceServer;
22use api::v1::meta::store_server::StoreServer;
23use common_base::Plugins;
24use common_config::Configurable;
25#[cfg(any(feature = "pg_kvbackend", feature = "mysql_kvbackend"))]
26use common_meta::distributed_time_constants::META_LEASE_SECS;
27use common_meta::election::etcd::EtcdElection;
28use common_meta::kv_backend::chroot::ChrootKvBackend;
29use common_meta::kv_backend::etcd::EtcdStore;
30use common_meta::kv_backend::memory::MemoryKvBackend;
31use common_meta::kv_backend::{KvBackendRef, ResettableKvBackendRef};
32use common_telemetry::info;
33use either::Either;
34use servers::configurator::GrpcRouterConfiguratorRef;
35use servers::http::{HttpServer, HttpServerBuilder};
36use servers::metrics_handler::MetricsHandler;
37use servers::server::Server;
38use snafu::ResultExt;
39use tokio::net::TcpListener;
40use tokio::sync::mpsc::{self, Receiver, Sender};
41use tokio::sync::{Mutex, oneshot};
42use tonic::codec::CompressionEncoding;
43use tonic::transport::server::{Router, TcpIncoming};
44
45use crate::cluster::{MetaPeerClientBuilder, MetaPeerClientRef};
46use crate::error::OtherSnafu;
47use crate::metasrv::builder::MetasrvBuilder;
48use crate::metasrv::{
49    BackendImpl, ElectionRef, Metasrv, MetasrvOptions, SelectTarget, SelectorFactoryContext,
50    SelectorFactoryRef, SelectorRef,
51};
52use crate::selector::lease_based::LeaseBasedSelector;
53use crate::selector::load_based::LoadBasedSelector;
54use crate::selector::round_robin::RoundRobinSelector;
55use crate::selector::weight_compute::RegionNumsBasedWeightCompute;
56use crate::selector::{Selector, SelectorType};
57use crate::service::admin;
58use crate::service::admin::admin_axum_router;
59use crate::utils::etcd::create_etcd_client_with_tls;
60use crate::{Result, error};
61
62pub struct MetasrvInstance {
63    metasrv: Arc<Metasrv>,
64
65    http_server: Either<Option<HttpServerBuilder>, HttpServer>,
66
67    opts: MetasrvOptions,
68
69    signal_sender: Option<Sender<()>>,
70
71    plugins: Plugins,
72
73    /// gRPC serving state receiver. Only present if the gRPC server is started.
74    serve_state: Arc<Mutex<Option<oneshot::Receiver<Result<()>>>>>,
75
76    /// gRPC bind addr
77    bind_addr: Option<SocketAddr>,
78}
79
80impl MetasrvInstance {
81    pub async fn new(metasrv: Metasrv) -> Result<MetasrvInstance> {
82        let opts = metasrv.options().clone();
83        let plugins = metasrv.plugins().clone();
84        let metasrv = Arc::new(metasrv);
85
86        // Wire up the admin_axum_router as an extra router
87        let extra_routers = admin_axum_router(metasrv.clone());
88
89        let mut builder = HttpServerBuilder::new(opts.http.clone())
90            .with_metrics_handler(MetricsHandler)
91            .with_greptime_config_options(opts.to_toml().context(error::TomlFormatSnafu)?);
92        builder = builder.with_extra_router(extra_routers);
93
94        // put metasrv into plugins for later use
95        plugins.insert::<Arc<Metasrv>>(metasrv.clone());
96        Ok(MetasrvInstance {
97            metasrv,
98            http_server: Either::Left(Some(builder)),
99            opts,
100            signal_sender: None,
101            plugins,
102            serve_state: Default::default(),
103            bind_addr: None,
104        })
105    }
106
107    pub async fn start(&mut self) -> Result<()> {
108        if let Some(builder) = self.http_server.as_mut().left()
109            && let Some(builder) = builder.take()
110        {
111            let mut server = builder.build();
112
113            let addr = self.opts.http.addr.parse().context(error::ParseAddrSnafu {
114                addr: &self.opts.http.addr,
115            })?;
116            info!("starting http server at {}", addr);
117            server.start(addr).await.context(error::StartHttpSnafu)?;
118
119            self.http_server = Either::Right(server);
120        } else {
121            // If the http server builder is not present, the Metasrv has to be called "start"
122            // already, regardless of the startup was successful or not. Return an `Ok` here for
123            // simplicity.
124            return Ok(());
125        };
126
127        self.metasrv.try_start().await?;
128
129        let (tx, rx) = mpsc::channel::<()>(1);
130
131        self.signal_sender = Some(tx);
132
133        // Start gRPC server with admin services for backward compatibility
134        let mut router = router(self.metasrv.clone());
135        if let Some(configurator) = self
136            .metasrv
137            .plugins()
138            .get::<GrpcRouterConfiguratorRef<()>>()
139        {
140            router = configurator
141                .configure_grpc_router(router, ())
142                .await
143                .context(OtherSnafu)?;
144        }
145
146        let (serve_state_tx, serve_state_rx) = oneshot::channel();
147
148        let socket_addr =
149            bootstrap_metasrv_with_router(&self.opts.grpc.bind_addr, router, serve_state_tx, rx)
150                .await?;
151        self.bind_addr = Some(socket_addr);
152
153        *self.serve_state.lock().await = Some(serve_state_rx);
154        Ok(())
155    }
156
157    pub async fn shutdown(&self) -> Result<()> {
158        if let Some(mut rx) = self.serve_state.lock().await.take()
159            && let Ok(Err(err)) = rx.try_recv()
160        {
161            common_telemetry::error!(err; "Metasrv start failed")
162        }
163        if let Some(signal) = &self.signal_sender {
164            signal
165                .send(())
166                .await
167                .context(error::SendShutdownSignalSnafu)?;
168        }
169        self.metasrv.shutdown().await?;
170
171        if let Some(http_server) = self.http_server.as_ref().right() {
172            http_server
173                .shutdown()
174                .await
175                .context(error::ShutdownServerSnafu {
176                    server: http_server.name(),
177                })?;
178        }
179        Ok(())
180    }
181
182    pub fn plugins(&self) -> Plugins {
183        self.plugins.clone()
184    }
185
186    pub fn get_inner(&self) -> &Metasrv {
187        &self.metasrv
188    }
189    pub fn bind_addr(&self) -> &Option<SocketAddr> {
190        &self.bind_addr
191    }
192
193    pub fn mut_http_server(&mut self) -> &mut Either<Option<HttpServerBuilder>, HttpServer> {
194        &mut self.http_server
195    }
196
197    pub fn http_server(&self) -> Option<&HttpServer> {
198        self.http_server.as_ref().right()
199    }
200}
201
202pub async fn bootstrap_metasrv_with_router(
203    bind_addr: &str,
204    router: Router,
205    serve_state_tx: oneshot::Sender<Result<()>>,
206    mut shutdown_rx: Receiver<()>,
207) -> Result<SocketAddr> {
208    let listener = TcpListener::bind(bind_addr)
209        .await
210        .context(error::TcpBindSnafu { addr: bind_addr })?;
211
212    let real_bind_addr = listener
213        .local_addr()
214        .context(error::TcpBindSnafu { addr: bind_addr })?;
215
216    info!("gRPC server is bound to: {}", real_bind_addr);
217
218    let incoming = TcpIncoming::from(listener).with_nodelay(Some(true));
219
220    let _handle = common_runtime::spawn_global(async move {
221        let result = router
222            .serve_with_incoming_shutdown(incoming, async {
223                let _ = shutdown_rx.recv().await;
224            })
225            .await
226            .inspect_err(|err| common_telemetry::error!(err;"Failed to start metasrv"))
227            .context(error::StartGrpcSnafu);
228        let _ = serve_state_tx.send(result);
229    });
230
231    Ok(real_bind_addr)
232}
233
234#[macro_export]
235macro_rules! add_compressed_service {
236    ($builder:expr, $server:expr) => {
237        $builder.add_service(
238            $server
239                .accept_compressed(CompressionEncoding::Gzip)
240                .accept_compressed(CompressionEncoding::Zstd)
241                .send_compressed(CompressionEncoding::Gzip)
242                .send_compressed(CompressionEncoding::Zstd),
243        )
244    };
245}
246
247pub fn router(metasrv: Arc<Metasrv>) -> Router {
248    let mut router = tonic::transport::Server::builder()
249        // for admin services
250        .accept_http1(true)
251        // For quick network failures detection.
252        .http2_keepalive_interval(Some(metasrv.options().grpc.http2_keep_alive_interval))
253        .http2_keepalive_timeout(Some(metasrv.options().grpc.http2_keep_alive_timeout));
254    let router = add_compressed_service!(router, HeartbeatServer::from_arc(metasrv.clone()));
255    let router = add_compressed_service!(router, StoreServer::from_arc(metasrv.clone()));
256    let router = add_compressed_service!(router, ClusterServer::from_arc(metasrv.clone()));
257    let router = add_compressed_service!(router, ProcedureServiceServer::from_arc(metasrv.clone()));
258    let router = add_compressed_service!(router, ConfigServer::from_arc(metasrv.clone()));
259    router.add_service(admin::make_admin_service(metasrv))
260}
261
262pub async fn metasrv_builder(
263    opts: &MetasrvOptions,
264    plugins: Plugins,
265    kv_backend: Option<KvBackendRef>,
266) -> Result<MetasrvBuilder> {
267    let (mut kv_backend, election) = match (kv_backend, &opts.backend) {
268        (Some(kv_backend), _) => (kv_backend, None),
269        (None, BackendImpl::MemoryStore) => (Arc::new(MemoryKvBackend::new()) as _, None),
270        (None, BackendImpl::EtcdStore) => {
271            let etcd_client = create_etcd_client_with_tls(
272                &opts.store_addrs,
273                &opts.backend_client,
274                opts.backend_tls.as_ref(),
275            )
276            .await?;
277            let kv_backend = EtcdStore::with_etcd_client(etcd_client.clone(), opts.max_txn_ops);
278            let election = EtcdElection::with_etcd_client(
279                &opts.grpc.server_addr,
280                etcd_client,
281                opts.store_key_prefix.clone(),
282            )
283            .await
284            .context(error::KvBackendSnafu)?;
285
286            (kv_backend, Some(election))
287        }
288        #[cfg(feature = "pg_kvbackend")]
289        (None, BackendImpl::PostgresStore) => {
290            use std::time::Duration;
291
292            use common_meta::distributed_time_constants::POSTGRES_KEEP_ALIVE_SECS;
293            use common_meta::election::CANDIDATE_LEASE_SECS;
294            use deadpool_postgres::{Config, ManagerConfig, RecyclingMethod};
295
296            use crate::utils::postgres::{build_postgres_election, build_postgres_kv_backend};
297
298            let candidate_lease_ttl = Duration::from_secs(CANDIDATE_LEASE_SECS);
299            let meta_lease_ttl = Duration::from_secs(META_LEASE_SECS);
300
301            let mut cfg = Config::new();
302            cfg.keepalives = Some(true);
303            cfg.keepalives_idle = Some(Duration::from_secs(POSTGRES_KEEP_ALIVE_SECS));
304            cfg.manager = Some(ManagerConfig {
305                recycling_method: RecyclingMethod::Verified,
306            });
307
308            let election = build_postgres_election(
309                &opts.store_addrs,
310                Some(cfg.clone()),
311                opts.backend_tls.clone(),
312                opts.grpc.server_addr.clone(),
313                opts.store_key_prefix.clone(),
314                candidate_lease_ttl,
315                meta_lease_ttl,
316                opts.meta_schema_name.as_deref(),
317                &opts.meta_table_name,
318                opts.meta_election_lock_id,
319            )
320            .await?;
321
322            let kv_backend = build_postgres_kv_backend(
323                &opts.store_addrs,
324                Some(cfg),
325                opts.backend_tls.clone(),
326                opts.meta_schema_name.as_deref(),
327                &opts.meta_table_name,
328                opts.max_txn_ops,
329                opts.auto_create_schema,
330            )
331            .await?;
332
333            (kv_backend, Some(election))
334        }
335        #[cfg(feature = "mysql_kvbackend")]
336        (None, BackendImpl::MysqlStore) => {
337            use std::time::Duration;
338
339            use common_meta::election::CANDIDATE_LEASE_SECS;
340
341            use crate::utils::mysql::{build_mysql_election, build_mysql_kv_backend};
342
343            let kv_backend = build_mysql_kv_backend(
344                &opts.store_addrs,
345                opts.backend_tls.as_ref(),
346                &opts.meta_table_name,
347                opts.max_txn_ops,
348            )
349            .await?;
350            // Since election will acquire a lock of the table, we need a separate table for election.
351            let election_table_name = opts.meta_table_name.clone() + "_election";
352            let innode_lock_wait_timeout = Duration::from_secs(META_LEASE_SECS / 2);
353            let meta_lease_ttl = Duration::from_secs(META_LEASE_SECS);
354            let candidate_lease_ttl = Duration::from_secs(CANDIDATE_LEASE_SECS);
355
356            let election = build_mysql_election(
357                &opts.store_addrs,
358                opts.backend_tls.as_ref(),
359                opts.grpc.server_addr.clone(),
360                opts.store_key_prefix.clone(),
361                candidate_lease_ttl,
362                meta_lease_ttl,
363                &election_table_name,
364                innode_lock_wait_timeout,
365            )
366            .await?;
367            (kv_backend, Some(election))
368        }
369    };
370
371    if !opts.store_key_prefix.is_empty() {
372        info!(
373            "using chroot kv backend with prefix: {prefix}",
374            prefix = opts.store_key_prefix
375        );
376        kv_backend = Arc::new(ChrootKvBackend::new(
377            opts.store_key_prefix.clone().into_bytes(),
378            kv_backend,
379        ))
380    }
381
382    let in_memory = Arc::new(MemoryKvBackend::new()) as ResettableKvBackendRef;
383    let meta_peer_client = build_default_meta_peer_client(&election, &in_memory);
384
385    let base_selector: Arc<
386        dyn Selector<
387                Context = crate::metasrv::SelectorContext,
388                Output = Vec<common_meta::peer::Peer>,
389            >,
390    > = match opts.selector {
391        SelectorType::LoadBased => Arc::new(LoadBasedSelector::new(
392            RegionNumsBasedWeightCompute,
393            meta_peer_client.clone(),
394        )) as SelectorRef,
395        SelectorType::LeaseBased => Arc::new(LeaseBasedSelector) as SelectorRef,
396        SelectorType::RoundRobin => {
397            Arc::new(RoundRobinSelector::new(SelectTarget::Datanode)) as SelectorRef
398        }
399    };
400    info!(
401        "Using selector from options, selector type: {}",
402        opts.selector.as_ref()
403    );
404
405    let selector = if let Some(factory) = plugins.get::<SelectorFactoryRef>() {
406        info!("Building selector from plugin factory");
407        factory.build(SelectorFactoryContext {
408            metasrv_options: opts.clone(),
409            meta_peer_client: meta_peer_client.clone(),
410            in_memory: in_memory.clone(),
411            election: election.clone(),
412            base_selector,
413        })
414    } else {
415        base_selector
416    };
417
418    Ok(MetasrvBuilder::new()
419        .options(opts.clone())
420        .kv_backend(kv_backend)
421        .in_memory(in_memory)
422        .selector(selector)
423        .election(election)
424        .meta_peer_client(meta_peer_client)
425        .plugins(plugins))
426}
427
428pub(crate) fn build_default_meta_peer_client(
429    election: &Option<ElectionRef>,
430    in_memory: &ResettableKvBackendRef,
431) -> MetaPeerClientRef {
432    MetaPeerClientBuilder::default()
433        .election(election.clone())
434        .in_memory(in_memory.clone())
435        .build()
436        .map(Arc::new)
437        // Safety: all required fields set at initialization
438        .unwrap()
439}
440
441#[cfg(test)]
442mod tests {
443    use std::sync::atomic::{AtomicBool, Ordering};
444
445    use common_meta::kv_backend::memory::MemoryKvBackend;
446
447    use super::*;
448    use crate::metasrv::{SelectorFactory, SelectorFactoryContext};
449
450    struct RecordingSelectorFactory {
451        called: Arc<AtomicBool>,
452    }
453
454    impl SelectorFactory for RecordingSelectorFactory {
455        fn build(&self, ctx: SelectorFactoryContext) -> SelectorRef {
456            self.called.store(true, Ordering::Relaxed);
457            ctx.base_selector
458        }
459    }
460
461    #[tokio::test]
462    async fn metasrv_builder_builds_load_based_selector_from_plugin_factory() {
463        let called = Arc::new(AtomicBool::new(false));
464        let plugins = Plugins::new();
465        plugins.insert(Arc::new(RecordingSelectorFactory {
466            called: called.clone(),
467        }) as SelectorFactoryRef);
468        let opts = MetasrvOptions {
469            selector: SelectorType::LoadBased,
470            ..Default::default()
471        };
472
473        metasrv_builder(
474            &opts,
475            plugins,
476            Some(Arc::new(MemoryKvBackend::new()) as KvBackendRef),
477        )
478        .await
479        .unwrap();
480
481        assert!(called.load(Ordering::Relaxed));
482    }
483}