Skip to main content

common_meta/election/rds/
mysql.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;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::time::Duration;
18
19use common_telemetry::{error, info, warn};
20use common_time::Timestamp;
21use snafu::{OptionExt, ResultExt, ensure};
22use sqlx::mysql::{MySqlArguments, MySqlRow};
23use sqlx::pool::PoolConnection;
24use sqlx::query::Query;
25use sqlx::{AssertSqlSafe, MySql, MySqlPool, MySqlTransaction, Row};
26use tokio::sync::{Mutex, MutexGuard, broadcast};
27use tokio::time::MissedTickBehavior;
28
29use crate::election::rds::{LEASE_SEP, Lease, RdsLeaderKey, parse_value_and_expire_time};
30use crate::election::{
31    Election, ElectionRef, LeaderChangeMessage, LeaderValue, MetasrvNodeInfo, listen_leader_change,
32    send_leader_change_and_set_flags,
33};
34use crate::error::{
35    AcquireMySqlClientSnafu, DecodeSqlValueSnafu, DeserializeFromJsonSnafu,
36    ElectionLeaderLeaseChangedSnafu, ElectionLeaderLeaseExpiredSnafu, ElectionNoLeaderSnafu,
37    MySqlExecutionSnafu, Result, SerializeToJsonSnafu, SqlExecutionTimeoutSnafu, UnexpectedSnafu,
38};
39use crate::key::{CANDIDATES_ROOT, ELECTION_KEY};
40
41/// Builds a [`Query`] from a dynamic SQL template.
42///
43/// sqlx 0.9 requires `&'static str` or [`AssertSqlSafe`] to reject unaudited
44/// dynamic SQL strings. The templates here are generated by
45/// [`ElectionSqlFactory`] from the configured table name (operator config,
46/// not user input), and all dynamic data is passed via bind parameters,
47/// so it is safe to mark them with [`AssertSqlSafe`].
48fn template_query(sql: &str) -> Query<'_, MySql, MySqlArguments> {
49    sqlx::query(AssertSqlSafe(sql))
50}
51
52struct ElectionSqlFactory<'a> {
53    table_name: &'a str,
54}
55
56struct ElectionSqlSet {
57    campaign: String,
58    // SQL to put a value with expire time.
59    //
60    // Parameters for the query:
61    // `$1`: key,
62    // `$2`: value,
63    // `$3`: lease time in seconds
64    //
65    // Returns:
66    // If the key already exists, return the previous value.
67    put_value_with_lease: String,
68    // SQL to update a value with expire time.
69    //
70    // Parameters for the query:
71    // `$1`: updated value,
72    // `$2`: lease time in seconds
73    // `$3`: key,
74    // `$4`: previous value,
75    update_value_with_lease: String,
76    // SQL to get a value with expire time.
77    //
78    // Parameters:
79    // `$1`: key
80    get_value_with_lease: String,
81    // SQL to get all values with expire time with the given key prefix.
82    //
83    // Parameters:
84    // `$1`: key prefix like 'prefix%'
85    //
86    // Returns:
87    // column 0: value,
88    // column 1: current timestamp
89    get_value_with_lease_by_prefix: String,
90    // SQL to delete a value.
91    //
92    // Parameters:
93    // `?`: key
94    //
95    // Returns:
96    // Rows affected
97    delete_value: String,
98}
99
100impl<'a> ElectionSqlFactory<'a> {
101    fn new(table_name: &'a str) -> Self {
102        Self { table_name }
103    }
104
105    fn build(self) -> ElectionSqlSet {
106        ElectionSqlSet {
107            campaign: self.campaign_sql(),
108            put_value_with_lease: self.put_value_with_lease_sql(),
109            update_value_with_lease: self.update_value_with_lease_sql(),
110            get_value_with_lease: self.get_value_with_lease_sql(),
111            get_value_with_lease_by_prefix: self.get_value_with_lease_by_prefix_sql(),
112            delete_value: self.delete_value_sql(),
113        }
114    }
115
116    /// Use `SELECT FOR UPDATE` to lock for compatibility with other MySQL-compatible databases
117    /// instead of directly using `GET_LOCK`.
118    fn campaign_sql(&self) -> String {
119        format!("SELECT * FROM `{}` FOR UPDATE;", self.table_name)
120    }
121
122    fn put_value_with_lease_sql(&self) -> String {
123        format!(
124            r#"
125            INSERT INTO `{}` (k, v) VALUES (
126                ?,
127                CONCAT(
128                    ?,
129                    '{}',
130                    DATE_FORMAT(DATE_ADD(NOW(4), INTERVAL ? SECOND), '%Y-%m-%d %T.%f')
131                )
132            )
133            ON DUPLICATE KEY UPDATE v = VALUES(v);
134            "#,
135            self.table_name, LEASE_SEP
136        )
137    }
138
139    fn update_value_with_lease_sql(&self) -> String {
140        format!(
141            r#"UPDATE `{}`
142               SET v = CONCAT(?, '{}', DATE_FORMAT(DATE_ADD(NOW(4), INTERVAL ? SECOND), '%Y-%m-%d %T.%f'))
143               WHERE k = ? AND v = ?"#,
144            self.table_name, LEASE_SEP
145        )
146    }
147
148    fn get_value_with_lease_sql(&self) -> String {
149        format!(
150            r#"SELECT v, DATE_FORMAT(NOW(4), '%Y-%m-%d %T.%f') FROM `{}` WHERE k = ?"#,
151            self.table_name
152        )
153    }
154
155    fn get_value_with_lease_by_prefix_sql(&self) -> String {
156        format!(
157            r#"SELECT v, DATE_FORMAT(NOW(4), '%Y-%m-%d %T.%f') FROM `{}` WHERE k LIKE ?"#,
158            self.table_name
159        )
160    }
161
162    fn delete_value_sql(&self) -> String {
163        format!("DELETE FROM `{}` WHERE k = ?;", self.table_name)
164    }
165}
166
167enum Executor<'a> {
168    Default(MutexGuard<'a, ElectionMysqlClient>),
169    Txn(TransactionWithExecutionTimeout<'a>),
170}
171
172impl Executor<'_> {
173    async fn query(
174        &mut self,
175        query: Query<'_, MySql, MySqlArguments>,
176        sql: &str,
177    ) -> Result<Vec<MySqlRow>> {
178        match self {
179            Executor::Default(client) => client.query(query, sql).await,
180            Executor::Txn(txn) => txn.query(query, sql).await,
181        }
182    }
183
184    async fn execute(&mut self, query: Query<'_, MySql, MySqlArguments>, sql: &str) -> Result<u64> {
185        match self {
186            Executor::Default(client) => client.execute(query, sql).await,
187            Executor::Txn(txn) => txn.execute(query, sql).await,
188        }
189    }
190
191    async fn commit(self) -> Result<()> {
192        match self {
193            Executor::Txn(txn) => txn.commit().await,
194            _ => Ok(()),
195        }
196    }
197}
198
199/// MySQL client for election.
200pub struct ElectionMysqlClient {
201    current: Option<PoolConnection<MySql>>,
202    pool: MySqlPool,
203
204    /// The client-side timeout for statement execution.
205    ///
206    /// This timeout is enforced by the client application and is independent of any server-side timeouts.
207    /// If a statement takes longer than this duration to execute, the client will abort the operation.
208    execution_timeout: Duration,
209
210    /// The maximum execution time for the statement.
211    ///
212    /// This timeout is enforced by the server and is independent of any client-side timeouts.
213    /// If a statement takes longer than this duration to execute, the server will abort the operation.
214    max_execution_time: Duration,
215
216    /// The lock wait timeout for the session.
217    ///
218    /// This timeout determines how long the server waits for a lock to be acquired before timing out.
219    /// If a lock cannot be acquired within this duration, the server will abort the operation.
220    innode_lock_wait_timeout: Duration,
221
222    /// The wait timeout for the session.
223    ///
224    /// This timeout determines how long the server waits for activity on a noninteractive connection
225    /// before closing it. If a connection is idle for longer than this duration, the server will
226    /// terminate it.
227    wait_timeout: Duration,
228
229    /// The table name for election.
230    table_for_election: String,
231}
232
233impl ElectionMysqlClient {
234    pub fn new(
235        pool: MySqlPool,
236        execution_timeout: Duration,
237        max_execution_time: Duration,
238        innode_lock_wait_timeout: Duration,
239        wait_timeout: Duration,
240        table_for_election: &str,
241    ) -> Self {
242        Self {
243            current: None,
244            pool,
245            execution_timeout,
246            max_execution_time,
247            innode_lock_wait_timeout,
248            wait_timeout,
249            table_for_election: table_for_election.to_string(),
250        }
251    }
252
253    fn create_table_sql(&self) -> String {
254        format!(
255            r#"
256            CREATE TABLE IF NOT EXISTS `{}` (
257                k VARBINARY(3072) PRIMARY KEY,
258                v BLOB
259            );"#,
260            self.table_for_election
261        )
262    }
263
264    fn insert_once_sql(&self) -> String {
265        format!(
266            "INSERT IGNORE INTO `{}` (k, v) VALUES ('__place_holder_for_lock', '');",
267            self.table_for_election
268        )
269    }
270
271    fn check_version_sql(&self) -> String {
272        "SELECT @@version;".to_string()
273    }
274
275    async fn reset_client(&mut self) -> Result<()> {
276        self.current = None;
277        self.maybe_init_client().await
278    }
279
280    async fn ensure_table_exists(&mut self) -> Result<()> {
281        let create_table_sql = self.create_table_sql();
282        let query = template_query(&create_table_sql);
283        self.execute(query, &create_table_sql).await?;
284        // Insert at least one row for `SELECT * FOR UPDATE` to work.
285        let insert_once_sql = self.insert_once_sql();
286        let query = template_query(&insert_once_sql);
287        self.execute(query, &insert_once_sql).await?;
288        Ok(())
289    }
290
291    async fn maybe_init_client(&mut self) -> Result<()> {
292        if self.current.is_none() {
293            let client = self.pool.acquire().await.context(AcquireMySqlClientSnafu)?;
294
295            self.current = Some(client);
296            let (query, sql) = if !self.wait_timeout.is_zero() {
297                let sql = "SET SESSION wait_timeout = ?, innodb_lock_wait_timeout = ?, max_execution_time = ?;";
298                (
299                    sqlx::query(sql)
300                        .bind(self.wait_timeout.as_secs())
301                        .bind(self.innode_lock_wait_timeout.as_secs())
302                        .bind(self.max_execution_time.as_millis() as u64),
303                    sql,
304                )
305            } else {
306                let sql = "SET SESSION innodb_lock_wait_timeout = ?, max_execution_time = ?;";
307                (
308                    sqlx::query(sql)
309                        .bind(self.innode_lock_wait_timeout.as_secs())
310                        .bind(self.max_execution_time.as_millis() as u64),
311                    sql,
312                )
313            };
314            self.set_session_isolation_level().await?;
315            self.execute(query, sql).await?;
316            self.check_version(&self.check_version_sql()).await?;
317        }
318        Ok(())
319    }
320
321    /// Set session isolation level to serializable.
322    ///
323    /// # Panics
324    /// if `current` is `None`.
325    async fn set_session_isolation_level(&mut self) -> Result<()> {
326        let sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE";
327        let query = sqlx::query(sql);
328        self.execute(query, sql).await?;
329        Ok(())
330    }
331
332    /// Check if the MySQL version is supported.
333    ///
334    /// # Panics
335    /// if `current` is `None`.
336    async fn check_version(&mut self, sql: &str) -> Result<()> {
337        // Check if the MySQL version is supported.
338        let query = template_query(sql);
339        // Safety: `maybe_init_client` ensures `current` is not `None`.
340        let client = self.current.as_mut().unwrap();
341        let row = tokio::time::timeout(self.execution_timeout, query.fetch_one(&mut **client))
342            .await
343            .map_err(|_| {
344                SqlExecutionTimeoutSnafu {
345                    sql,
346                    duration: self.execution_timeout,
347                }
348                .build()
349            })?;
350        match row {
351            Ok(row) => {
352                let version: String = row.try_get(0).context(DecodeSqlValueSnafu {})?;
353                if !version.starts_with("8.0") && !version.starts_with("5.7") {
354                    warn!(
355                        "Unsupported MySQL version: {}, expected: [5.7, 8.0]",
356                        version
357                    );
358                }
359            }
360            Err(e) => {
361                warn!(e; "Failed to check MySQL version through sql: {}", sql);
362            }
363        }
364        Ok(())
365    }
366
367    /// Returns the result of the query.
368    ///
369    /// # Panics
370    /// if `current` is `None`.
371    async fn execute(&mut self, query: Query<'_, MySql, MySqlArguments>, sql: &str) -> Result<u64> {
372        // Safety: `maybe_init_client` ensures `current` is not `None`.
373        let client = self.current.as_mut().unwrap();
374        let future = query.execute(&mut **client);
375        let rows_affected = tokio::time::timeout(self.execution_timeout, future)
376            .await
377            .map_err(|_| {
378                SqlExecutionTimeoutSnafu {
379                    sql,
380                    duration: self.execution_timeout,
381                }
382                .build()
383            })?
384            .context(MySqlExecutionSnafu { sql })?
385            .rows_affected();
386        Ok(rows_affected)
387    }
388
389    /// Returns the result of the query.
390    ///
391    /// # Panics
392    /// if `current` is `None`.
393    async fn query(
394        &mut self,
395        query: Query<'_, MySql, MySqlArguments>,
396        sql: &str,
397    ) -> Result<Vec<MySqlRow>> {
398        // Safety: `maybe_init_client` ensures `current` is not `None`.
399        let client = self.current.as_mut().unwrap();
400        let future = query.fetch_all(&mut **client);
401        tokio::time::timeout(self.execution_timeout, future)
402            .await
403            .map_err(|_| {
404                SqlExecutionTimeoutSnafu {
405                    sql,
406                    duration: self.execution_timeout,
407                }
408                .build()
409            })?
410            .context(MySqlExecutionSnafu { sql })
411    }
412
413    async fn transaction(&mut self) -> Result<TransactionWithExecutionTimeout<'_>> {
414        use sqlx::Acquire;
415        let client = self.current.as_mut().unwrap();
416        let transaction = client
417            .begin()
418            .await
419            .context(MySqlExecutionSnafu { sql: "BEGIN" })?;
420
421        Ok(TransactionWithExecutionTimeout {
422            transaction,
423            execution_timeout: self.execution_timeout,
424        })
425    }
426}
427
428struct TransactionWithExecutionTimeout<'a> {
429    transaction: MySqlTransaction<'a>,
430    execution_timeout: Duration,
431}
432
433impl TransactionWithExecutionTimeout<'_> {
434    async fn query(
435        &mut self,
436        query: Query<'_, MySql, MySqlArguments>,
437        sql: &str,
438    ) -> Result<Vec<MySqlRow>> {
439        let res = tokio::time::timeout(
440            self.execution_timeout,
441            query.fetch_all(&mut *self.transaction),
442        )
443        .await
444        .map_err(|_| {
445            SqlExecutionTimeoutSnafu {
446                sql,
447                duration: self.execution_timeout,
448            }
449            .build()
450        })?
451        .context(MySqlExecutionSnafu { sql })?;
452        Ok(res)
453    }
454
455    async fn execute(&mut self, query: Query<'_, MySql, MySqlArguments>, sql: &str) -> Result<u64> {
456        let res = tokio::time::timeout(
457            self.execution_timeout,
458            query.execute(&mut *self.transaction),
459        )
460        .await
461        .map_err(|_| {
462            SqlExecutionTimeoutSnafu {
463                sql,
464                duration: self.execution_timeout,
465            }
466            .build()
467        })?
468        .context(MySqlExecutionSnafu { sql })?;
469        Ok(res.rows_affected())
470    }
471
472    async fn commit(self) -> Result<()> {
473        tokio::time::timeout(self.execution_timeout, self.transaction.commit())
474            .await
475            .map_err(|_| {
476                SqlExecutionTimeoutSnafu {
477                    sql: "COMMIT",
478                    duration: self.execution_timeout,
479                }
480                .build()
481            })?
482            .context(MySqlExecutionSnafu { sql: "COMMIT" })?;
483        Ok(())
484    }
485}
486
487/// MySQL implementation of Election.
488pub struct MySqlElection {
489    leader_value: String,
490    client: Mutex<ElectionMysqlClient>,
491    is_leader: AtomicBool,
492    leader_infancy: AtomicBool,
493    leader_watcher: broadcast::Sender<LeaderChangeMessage>,
494    store_key_prefix: String,
495    candidate_lease_ttl: Duration,
496    meta_lease_ttl: Duration,
497    sql_set: ElectionSqlSet,
498}
499
500impl MySqlElection {
501    pub async fn with_mysql_client(
502        leader_value: String,
503        mut client: ElectionMysqlClient,
504        store_key_prefix: String,
505        candidate_lease_ttl: Duration,
506        meta_lease_ttl: Duration,
507        table_name: &str,
508    ) -> Result<ElectionRef> {
509        let sql_factory = ElectionSqlFactory::new(table_name);
510        client.maybe_init_client().await?;
511        client.ensure_table_exists().await?;
512        let tx = listen_leader_change(leader_value.clone());
513        Ok(Arc::new(Self {
514            leader_value,
515            client: Mutex::new(client),
516            is_leader: AtomicBool::new(false),
517            leader_infancy: AtomicBool::new(false),
518            leader_watcher: tx,
519            store_key_prefix,
520            candidate_lease_ttl,
521            meta_lease_ttl,
522            sql_set: sql_factory.build(),
523        }))
524    }
525
526    fn election_key(&self) -> String {
527        format!("{}{}", self.store_key_prefix, ELECTION_KEY)
528    }
529
530    fn candidate_root(&self) -> String {
531        format!("{}{}", self.store_key_prefix, CANDIDATES_ROOT)
532    }
533
534    fn candidate_key(&self) -> String {
535        format!("{}{}", self.candidate_root(), self.leader_value)
536    }
537
538    async fn maybe_init_client(&self) -> Result<()> {
539        let mut client = self.client.lock().await;
540        client.maybe_init_client().await?;
541        Ok(())
542    }
543}
544
545#[async_trait::async_trait]
546impl Election for MySqlElection {
547    type Leader = LeaderValue;
548
549    fn is_leader(&self) -> bool {
550        self.is_leader.load(Ordering::Relaxed)
551    }
552
553    fn in_leader_infancy(&self) -> bool {
554        self.leader_infancy
555            .compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire)
556            .is_ok()
557    }
558
559    async fn register_candidate(&self, node_info: &MetasrvNodeInfo) -> Result<()> {
560        self.maybe_init_client().await?;
561        let key = self.candidate_key();
562        let node_info =
563            serde_json::to_string(node_info).with_context(|_| SerializeToJsonSnafu {
564                input: format!("{node_info:?}"),
565            })?;
566
567        {
568            let client = self.client.lock().await;
569            let mut executor = Executor::Default(client);
570            let res = self
571                .put_value_with_lease(
572                    &key,
573                    &node_info,
574                    self.candidate_lease_ttl.as_secs(),
575                    &mut executor,
576                )
577                .await?;
578            // May registered before, just update the lease.
579            if !res {
580                warn!("Candidate already registered, update the lease");
581                self.delete_value(&key, &mut executor).await?;
582                self.put_value_with_lease(
583                    &key,
584                    &node_info,
585                    self.candidate_lease_ttl.as_secs(),
586                    &mut executor,
587                )
588                .await?;
589            }
590        }
591
592        // Check if the current lease has expired and renew the lease.
593        let mut keep_alive_interval = tokio::time::interval(self.candidate_lease_ttl / 2);
594        loop {
595            let _ = keep_alive_interval.tick().await;
596            let client = self.client.lock().await;
597            let mut executor = Executor::Default(client);
598            let lease = self
599                .get_value_with_lease(&key, &mut executor)
600                .await?
601                .unwrap_or_default();
602
603            ensure!(
604                lease.expire_time > lease.current,
605                UnexpectedSnafu {
606                    err_msg: format!(
607                        "Candidate lease expired at {:?} (current time: {:?}), key: {:?}",
608                        lease.expire_time,
609                        lease.current,
610                        String::from_utf8_lossy(&key.into_bytes())
611                    ),
612                }
613            );
614
615            self.update_value_with_lease(
616                &key,
617                &lease.origin,
618                &node_info,
619                self.candidate_lease_ttl.as_secs(),
620                &mut executor,
621            )
622            .await?;
623            std::mem::drop(executor);
624        }
625    }
626
627    async fn all_candidates(&self) -> Result<Vec<MetasrvNodeInfo>> {
628        self.maybe_init_client().await?;
629        let key_prefix = self.candidate_root();
630        let client = self.client.lock().await;
631        let mut executor = Executor::Default(client);
632        let (mut candidates, current) = self
633            .get_value_with_lease_by_prefix(&key_prefix, &mut executor)
634            .await?;
635        // Remove expired candidates
636        candidates.retain(|c| c.1 > current);
637        let mut valid_candidates = Vec::with_capacity(candidates.len());
638        for (c, _) in candidates {
639            let node_info: MetasrvNodeInfo =
640                serde_json::from_str(&c).with_context(|_| DeserializeFromJsonSnafu {
641                    input: format!("{:?}", c),
642                })?;
643            valid_candidates.push(node_info);
644        }
645        Ok(valid_candidates)
646    }
647
648    async fn campaign(&self) -> Result<()> {
649        self.maybe_init_client().await?;
650        let mut keep_alive_interval = tokio::time::interval(self.meta_lease_ttl / 2);
651        keep_alive_interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
652        loop {
653            self.do_campaign().await?;
654            keep_alive_interval.tick().await;
655        }
656    }
657
658    async fn reset_campaign(&self) {
659        info!("Resetting campaign");
660        if self.is_leader.load(Ordering::Relaxed) {
661            if let Err(err) = self.step_down_without_lock().await {
662                error!(err; "Failed to step down without lock");
663            }
664            info!("Step down without lock successfully, due to reset campaign");
665        }
666        if let Err(err) = self.client.lock().await.reset_client().await {
667            error!(err; "Failed to reset client");
668        }
669    }
670
671    async fn leader(&self) -> Result<Self::Leader> {
672        self.maybe_init_client().await?;
673        if self.is_leader.load(Ordering::Relaxed) {
674            Ok(self.leader_value.as_bytes().into())
675        } else {
676            let key = self.election_key();
677
678            let client = self.client.lock().await;
679            let mut executor = Executor::Default(client);
680            if let Some(lease) = self.get_value_with_lease(&key, &mut executor).await? {
681                ensure!(lease.expire_time > lease.current, ElectionNoLeaderSnafu);
682                Ok(lease.leader_value.as_bytes().into())
683            } else {
684                ElectionNoLeaderSnafu.fail()
685            }
686        }
687    }
688
689    async fn resign(&self) -> Result<()> {
690        todo!()
691    }
692
693    fn subscribe_leader_change(&self) -> broadcast::Receiver<LeaderChangeMessage> {
694        self.leader_watcher.subscribe()
695    }
696}
697
698impl MySqlElection {
699    /// Returns value, expire time and current time.
700    async fn get_value_with_lease(
701        &self,
702        key: &str,
703        executor: &mut Executor<'_>,
704    ) -> Result<Option<Lease>> {
705        let key = key.as_bytes();
706        let query = template_query(&self.sql_set.get_value_with_lease).bind(key);
707        let res = executor
708            .query(query, &self.sql_set.get_value_with_lease)
709            .await?;
710
711        if res.is_empty() {
712            return Ok(None);
713        }
714        // Safety: Checked if res is empty above.
715        let current_time_str = String::from_utf8_lossy(res[0].try_get(1).unwrap());
716        let current_time = match Timestamp::from_str(&current_time_str, None) {
717            Ok(ts) => ts,
718            Err(_) => UnexpectedSnafu {
719                err_msg: format!("Invalid timestamp: {}", current_time_str),
720            }
721            .fail()?,
722        };
723        // Safety: Checked if res is empty above.
724        let value_and_expire_time = String::from_utf8_lossy(res[0].try_get(0).unwrap_or_default());
725        let (value, expire_time) = parse_value_and_expire_time(&value_and_expire_time)?;
726
727        Ok(Some(Lease {
728            leader_value: value,
729            expire_time,
730            current: current_time,
731            origin: value_and_expire_time.to_string(),
732        }))
733    }
734
735    /// Returns all values and expire time with the given key prefix. Also returns the current time.
736    async fn get_value_with_lease_by_prefix(
737        &self,
738        key_prefix: &str,
739        executor: &mut Executor<'_>,
740    ) -> Result<(Vec<(String, Timestamp)>, Timestamp)> {
741        let key_prefix = format!("{}%", key_prefix).as_bytes().to_vec();
742        let query = template_query(&self.sql_set.get_value_with_lease_by_prefix).bind(key_prefix);
743        let res = executor
744            .query(query, &self.sql_set.get_value_with_lease_by_prefix)
745            .await?;
746
747        let mut values_with_leases = vec![];
748        let mut current = Timestamp::default();
749        for row in res {
750            let current_time_str = row.try_get(1).unwrap_or_default();
751            current = match Timestamp::from_str(current_time_str, None) {
752                Ok(ts) => ts,
753                Err(_) => UnexpectedSnafu {
754                    err_msg: format!("Invalid timestamp: {}", current_time_str),
755                }
756                .fail()?,
757            };
758
759            let value_and_expire_time = String::from_utf8_lossy(row.try_get(0).unwrap_or_default());
760            let (value, expire_time) = parse_value_and_expire_time(&value_and_expire_time)?;
761
762            values_with_leases.push((value, expire_time));
763        }
764        Ok((values_with_leases, current))
765    }
766
767    async fn update_value_with_lease(
768        &self,
769        key: &str,
770        prev: &str,
771        updated: &str,
772        lease_ttl: u64,
773        executor: &mut Executor<'_>,
774    ) -> Result<()> {
775        let key = key.as_bytes();
776        let prev = prev.as_bytes();
777        let updated = updated.as_bytes();
778
779        let query = template_query(&self.sql_set.update_value_with_lease)
780            .bind(updated)
781            .bind(lease_ttl as f64)
782            .bind(key)
783            .bind(prev);
784        let res = executor
785            .execute(query, &self.sql_set.update_value_with_lease)
786            .await?;
787
788        ensure!(
789            res == 1,
790            UnexpectedSnafu {
791                err_msg: format!("Failed to update key: {}", String::from_utf8_lossy(key)),
792            }
793        );
794
795        Ok(())
796    }
797
798    /// Returns `true` if the insertion is successful
799    async fn put_value_with_lease(
800        &self,
801        key: &str,
802        value: &str,
803        lease_ttl_secs: u64,
804        executor: &mut Executor<'_>,
805    ) -> Result<bool> {
806        let key = key.as_bytes();
807        let lease_ttl_secs = lease_ttl_secs as f64;
808        let query = template_query(&self.sql_set.put_value_with_lease)
809            .bind(key)
810            .bind(value)
811            .bind(lease_ttl_secs);
812        let res = executor
813            .execute(query, &self.sql_set.put_value_with_lease)
814            .await?;
815        Ok(res == 1)
816    }
817
818    /// Returns `true` if the deletion is successful.
819    /// Caution: Should only delete the key if the lease is expired.
820    async fn delete_value(&self, key: &str, executor: &mut Executor<'_>) -> Result<bool> {
821        let key = key.as_bytes();
822        let query = template_query(&self.sql_set.delete_value).bind(key);
823        let res = executor.execute(query, &self.sql_set.delete_value).await?;
824
825        Ok(res == 1)
826    }
827
828    /// Attempts to acquire leadership by executing a campaign. This function continuously checks
829    /// if the current lease is still valid.
830    async fn do_campaign(&self) -> Result<()> {
831        let lease = {
832            let client = self.client.lock().await;
833            let mut executor = Executor::Default(client);
834            self.get_value_with_lease(&self.election_key(), &mut executor)
835                .await?
836        };
837
838        let is_leader = self.is_leader();
839        // If current leader value is the same as the leader value in the remote lease,
840        // it means the current leader is still valid.
841        let is_current_leader = lease
842            .as_ref()
843            .map(|lease| lease.leader_value == self.leader_value)
844            .unwrap_or(false);
845        match (self.lease_check(&lease), is_leader, is_current_leader) {
846            // If the leader lease is valid and I'm the leader, renew the lease.
847            (Ok(_), true, true) => {
848                let mut client = self.client.lock().await;
849                let txn = client.transaction().await?;
850                let mut executor = Executor::Txn(txn);
851                let query = template_query(&self.sql_set.campaign);
852                executor.query(query, &self.sql_set.campaign).await?;
853                // Safety: Checked if lease is not None above.
854                self.renew_lease(executor, lease.unwrap()).await?;
855            }
856            // If the leader lease expires and I'm the leader, notify the leader watcher and step down.
857            // Another instance should be elected as the leader in this case.
858            (Err(err), true, _) => {
859                warn!(err; "Leader lease expired, step down...");
860                self.step_down_without_lock().await?;
861            }
862            (Ok(_), true, false) => {
863                warn!("Leader lease expired, step down...");
864                self.step_down_without_lock().await?;
865            }
866            // If the leader lease expires and I'm not the leader, elect myself.
867            (Err(err), false, _) => {
868                warn!(err; "Leader lease expired, elect myself.");
869                let mut client = self.client.lock().await;
870                let txn = client.transaction().await?;
871                let mut executor = Executor::Txn(txn);
872                let query = template_query(&self.sql_set.campaign);
873                executor.query(query, &self.sql_set.campaign).await?;
874                self.elected(executor, lease).await?;
875            }
876            // If the leader lease is valid and I'm the leader, but I don't think I'm the leader.
877            // Just re-elect myself.
878            (Ok(_), false, true) => {
879                warn!("I should be the leader, but I don't think so. Something went wrong.");
880                let mut client = self.client.lock().await;
881                let txn = client.transaction().await?;
882                let mut executor = Executor::Txn(txn);
883                let query = template_query(&self.sql_set.campaign);
884                executor.query(query, &self.sql_set.campaign).await?;
885                // Safety: Checked if lease is not None above.
886                self.renew_lease(executor, lease.unwrap()).await?;
887            }
888            // If the leader lease is valid and I'm not the leader, do nothing.
889            (Ok(_), false, false) => {}
890        }
891        Ok(())
892    }
893
894    /// Renew the lease
895    async fn renew_lease(&self, mut executor: Executor<'_>, lease: Lease) -> Result<()> {
896        let key = self.election_key();
897        self.update_value_with_lease(
898            &key,
899            &lease.origin,
900            &self.leader_value,
901            self.meta_lease_ttl.as_secs(),
902            &mut executor,
903        )
904        .await?;
905        executor.commit().await?;
906
907        if !self.is_leader() {
908            let key = self.election_key();
909            let leader_key = RdsLeaderKey {
910                name: self.leader_value.clone().into_bytes(),
911                key: key.clone().into_bytes(),
912                ..Default::default()
913            };
914            send_leader_change_and_set_flags(
915                &self.is_leader,
916                &self.leader_infancy,
917                &self.leader_watcher,
918                LeaderChangeMessage::Elected(Arc::new(leader_key)),
919            );
920        }
921
922        Ok(())
923    }
924
925    /// Performs a lease check during the election process.
926    ///
927    /// This function performs the following checks and actions:
928    ///
929    /// - **Case 1**: If the current instance is not the leader but the lease has expired, it raises an error
930    ///   to re-initiate the campaign. If the leader failed to renew the lease, its session will expire and the lock
931    ///   will be released.
932    /// - **Case 2**: If all checks pass, the function returns without performing any actions.
933    fn lease_check(&self, lease: &Option<Lease>) -> Result<Lease> {
934        let lease = lease.as_ref().context(ElectionNoLeaderSnafu)?;
935        // Case 1: Lease expired
936        ensure!(
937            lease.expire_time > lease.current,
938            ElectionLeaderLeaseExpiredSnafu
939        );
940        // Case 2: Everything is fine
941        Ok(lease.clone())
942    }
943
944    /// Still consider itself as the leader locally but failed to acquire the lock. Step down without deleting the key.
945    async fn step_down_without_lock(&self) -> Result<()> {
946        let key = self.election_key().into_bytes();
947        let leader_key = RdsLeaderKey {
948            name: self.leader_value.clone().into_bytes(),
949            key: key.clone(),
950            ..Default::default()
951        };
952        send_leader_change_and_set_flags(
953            &self.is_leader,
954            &self.leader_infancy,
955            &self.leader_watcher,
956            LeaderChangeMessage::StepDown(Arc::new(leader_key)),
957        );
958        Ok(())
959    }
960
961    /// Elected as leader. The leader should put the key and notify the leader watcher.
962    /// Caution: Should only elected while holding the lock.
963    async fn elected(
964        &self,
965        mut executor: Executor<'_>,
966        expected_lease: Option<Lease>,
967    ) -> Result<()> {
968        let key = self.election_key();
969        let leader_key = RdsLeaderKey {
970            name: self.leader_value.clone().into_bytes(),
971            key: key.clone().into_bytes(),
972            ..Default::default()
973        };
974        let remote_lease = self.get_value_with_lease(&key, &mut executor).await?;
975        ensure!(
976            expected_lease.map(|lease| lease.origin) == remote_lease.map(|lease| lease.origin),
977            ElectionLeaderLeaseChangedSnafu
978        );
979        self.delete_value(&key, &mut executor).await?;
980        self.put_value_with_lease(
981            &key,
982            &self.leader_value,
983            self.meta_lease_ttl.as_secs(),
984            &mut executor,
985        )
986        .await?;
987        executor.commit().await?;
988
989        send_leader_change_and_set_flags(
990            &self.is_leader,
991            &self.leader_infancy,
992            &self.leader_watcher,
993            LeaderChangeMessage::Elected(Arc::new(leader_key)),
994        );
995        Ok(())
996    }
997}
998
999#[cfg(test)]
1000mod tests {
1001    use std::{assert_matches, env};
1002
1003    use common_telemetry::init_default_ut_logging;
1004    use sqlx::MySqlPool;
1005
1006    use super::*;
1007    use crate::{error, maybe_skip_mysql_integration_test};
1008
1009    async fn create_mysql_client(
1010        table_name: Option<&str>,
1011        execution_timeout: Duration,
1012        wait_timeout: Duration,
1013    ) -> Result<Mutex<ElectionMysqlClient>> {
1014        init_default_ut_logging();
1015        let endpoint = env::var("GT_MYSQL_ENDPOINTS").unwrap_or_default();
1016        if endpoint.is_empty() {
1017            return UnexpectedSnafu {
1018                err_msg: "MySQL endpoint is empty".to_string(),
1019            }
1020            .fail();
1021        }
1022        let pool = MySqlPool::connect(&endpoint).await.unwrap();
1023        let mut client = ElectionMysqlClient::new(
1024            pool,
1025            execution_timeout,
1026            execution_timeout,
1027            Duration::from_secs(1),
1028            wait_timeout,
1029            table_name.unwrap_or("default_greptime_metakv-election"),
1030        );
1031        client.maybe_init_client().await?;
1032        if table_name.is_some() {
1033            client.ensure_table_exists().await?;
1034        }
1035        Ok(Mutex::new(client))
1036    }
1037
1038    async fn drop_table(client: &Mutex<ElectionMysqlClient>, table_name: &str) {
1039        let mut client = client.lock().await;
1040        let sql = format!("DROP TABLE IF EXISTS `{}`;", table_name);
1041        client.execute(template_query(&sql), &sql).await.unwrap();
1042    }
1043
1044    #[tokio::test]
1045    async fn test_mysql_crud() {
1046        maybe_skip_mysql_integration_test!();
1047        let key = "test_key".to_string();
1048        let value = "test_value".to_string();
1049
1050        let uuid = uuid::Uuid::new_v4().to_string();
1051        let table_name = "test_mysql_crud_greptime-metakv";
1052        let candidate_lease_ttl = Duration::from_secs(10);
1053        let meta_lease_ttl = Duration::from_secs(2);
1054
1055        let execution_timeout = Duration::from_secs(10);
1056        let idle_session_timeout = Duration::from_secs(0);
1057        let client = create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1058            .await
1059            .unwrap();
1060
1061        {
1062            let mut a = client.lock().await;
1063            let txn = a.transaction().await.unwrap();
1064            let mut executor = Executor::Txn(txn);
1065            let raw_query = format!("SELECT * FROM `{}` FOR UPDATE;", table_name);
1066            let query = template_query(&raw_query);
1067            let _ = executor.query(query, &raw_query).await.unwrap();
1068        }
1069
1070        let (tx, _) = broadcast::channel(100);
1071        let mysql_election = MySqlElection {
1072            leader_value: "test_leader".to_string(),
1073            client,
1074            is_leader: AtomicBool::new(false),
1075            leader_infancy: AtomicBool::new(true),
1076            leader_watcher: tx,
1077            store_key_prefix: uuid,
1078            candidate_lease_ttl,
1079            meta_lease_ttl,
1080            sql_set: ElectionSqlFactory::new(table_name).build(),
1081        };
1082        let client = mysql_election.client.lock().await;
1083        let mut executor = Executor::Default(client);
1084        let res = mysql_election
1085            .put_value_with_lease(&key, &value, 10, &mut executor)
1086            .await
1087            .unwrap();
1088        assert!(res);
1089
1090        let lease = mysql_election
1091            .get_value_with_lease(&key, &mut executor)
1092            .await
1093            .unwrap()
1094            .unwrap();
1095        assert_eq!(lease.leader_value, value);
1096
1097        mysql_election
1098            .update_value_with_lease(&key, &lease.origin, &value, 10, &mut executor)
1099            .await
1100            .unwrap();
1101
1102        let res = mysql_election
1103            .delete_value(&key, &mut executor)
1104            .await
1105            .unwrap();
1106        assert!(res);
1107
1108        let res = mysql_election
1109            .get_value_with_lease(&key, &mut executor)
1110            .await
1111            .unwrap();
1112        assert!(res.is_none());
1113
1114        for i in 0..10 {
1115            let key = format!("test_key_{}", i);
1116            let value = format!("test_value_{}", i);
1117            mysql_election
1118                .put_value_with_lease(&key, &value, 10, &mut executor)
1119                .await
1120                .unwrap();
1121        }
1122
1123        let key_prefix = "test_key".to_string();
1124        let (res, _) = mysql_election
1125            .get_value_with_lease_by_prefix(&key_prefix, &mut executor)
1126            .await
1127            .unwrap();
1128        assert_eq!(res.len(), 10);
1129
1130        for i in 0..10 {
1131            let key = format!("test_key_{}", i);
1132            let res = mysql_election
1133                .delete_value(&key, &mut executor)
1134                .await
1135                .unwrap();
1136            assert!(res);
1137        }
1138
1139        let (res, current) = mysql_election
1140            .get_value_with_lease_by_prefix(&key_prefix, &mut executor)
1141            .await
1142            .unwrap();
1143        assert!(res.is_empty());
1144        assert!(current == Timestamp::default());
1145
1146        // Should drop manually.
1147        std::mem::drop(executor);
1148        drop_table(&mysql_election.client, table_name).await;
1149    }
1150
1151    async fn candidate(
1152        leader_value: String,
1153        candidate_lease_ttl: Duration,
1154        store_key_prefix: String,
1155        table_name: String,
1156    ) {
1157        let meta_lease_ttl = Duration::from_secs(2);
1158        let execution_timeout = Duration::from_secs(10);
1159        let idle_session_timeout = Duration::from_secs(0);
1160        let client =
1161            create_mysql_client(Some(&table_name), execution_timeout, idle_session_timeout)
1162                .await
1163                .unwrap();
1164
1165        let (tx, _) = broadcast::channel(100);
1166        let mysql_election = MySqlElection {
1167            leader_value,
1168            client,
1169            is_leader: AtomicBool::new(false),
1170            leader_infancy: AtomicBool::new(true),
1171            leader_watcher: tx,
1172            store_key_prefix,
1173            candidate_lease_ttl,
1174            meta_lease_ttl,
1175            sql_set: ElectionSqlFactory::new(&table_name).build(),
1176        };
1177
1178        let node_info = MetasrvNodeInfo {
1179            addr: "test_addr".to_string(),
1180            version: "test_version".to_string(),
1181            git_commit: "test_git_commit".to_string(),
1182            start_time_ms: 0,
1183            total_cpu_millicores: 0,
1184            total_memory_bytes: 0,
1185            cpu_usage_millicores: 0,
1186            memory_usage_bytes: 0,
1187            hostname: "test_hostname".to_string(),
1188        };
1189        mysql_election.register_candidate(&node_info).await.unwrap();
1190    }
1191
1192    #[tokio::test]
1193    async fn test_candidate_registration() {
1194        maybe_skip_mysql_integration_test!();
1195        let leader_value_prefix = "test_leader".to_string();
1196        let candidate_lease_ttl = Duration::from_secs(2);
1197        let execution_timeout = Duration::from_secs(10);
1198        let meta_lease_ttl = Duration::from_secs(2);
1199        let idle_session_timeout = Duration::from_secs(0);
1200        let uuid = uuid::Uuid::new_v4().to_string();
1201        let table_name = "test_candidate_registration_greptime-metakv";
1202        let mut handles = vec![];
1203        let client = create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1204            .await
1205            .unwrap();
1206
1207        for i in 0..10 {
1208            let leader_value = format!("{}{}", leader_value_prefix, i);
1209            let handle = tokio::spawn(candidate(
1210                leader_value,
1211                candidate_lease_ttl,
1212                uuid.clone(),
1213                table_name.to_string(),
1214            ));
1215            handles.push(handle);
1216        }
1217        // Wait for candidates to register themselves and renew their leases at least once.
1218        tokio::time::sleep(candidate_lease_ttl / 2 + Duration::from_secs(1)).await;
1219
1220        let (tx, _) = broadcast::channel(100);
1221        let leader_value = "test_leader".to_string();
1222        let mysql_election = MySqlElection {
1223            leader_value,
1224            client,
1225            is_leader: AtomicBool::new(false),
1226            leader_infancy: AtomicBool::new(true),
1227            leader_watcher: tx,
1228            store_key_prefix: uuid.clone(),
1229            candidate_lease_ttl,
1230            meta_lease_ttl,
1231            sql_set: ElectionSqlFactory::new(table_name).build(),
1232        };
1233
1234        let candidates = mysql_election.all_candidates().await.unwrap();
1235        assert_eq!(candidates.len(), 10);
1236
1237        for handle in handles {
1238            handle.abort();
1239        }
1240
1241        // Wait for the candidate leases to expire.
1242        tokio::time::sleep(candidate_lease_ttl + Duration::from_secs(1)).await;
1243        let candidates = mysql_election.all_candidates().await.unwrap();
1244        assert!(candidates.is_empty());
1245
1246        // Garbage collection
1247        let client = mysql_election.client.lock().await;
1248        let mut executor = Executor::Default(client);
1249        for i in 0..10 {
1250            let key = format!("{}{}{}{}", uuid, CANDIDATES_ROOT, leader_value_prefix, i);
1251            let res = mysql_election
1252                .delete_value(&key, &mut executor)
1253                .await
1254                .unwrap();
1255            assert!(res);
1256        }
1257
1258        // Should drop manually.
1259        std::mem::drop(executor);
1260        drop_table(&mysql_election.client, table_name).await;
1261    }
1262
1263    async fn elected(
1264        election: &MySqlElection,
1265        table_name: &str,
1266        expected_lease: Option<Lease>,
1267    ) -> Result<()> {
1268        let mut client = election.client.lock().await;
1269        let txn = client.transaction().await.unwrap();
1270        let mut executor = Executor::Txn(txn);
1271        let raw_query = format!("SELECT * FROM `{}` FOR UPDATE;", table_name);
1272        let query = template_query(&raw_query);
1273        let _ = executor.query(query, &raw_query).await.unwrap();
1274        election.elected(executor, expected_lease).await
1275    }
1276
1277    async fn get_lease(election: &MySqlElection) -> Option<Lease> {
1278        let client = election.client.lock().await;
1279        let mut executor = Executor::Default(client);
1280        election
1281            .get_value_with_lease(&election.election_key(), &mut executor)
1282            .await
1283            .unwrap()
1284    }
1285
1286    #[tokio::test]
1287    async fn test_elected_with_incorrect_lease_fails() {
1288        maybe_skip_mysql_integration_test!();
1289        let leader_value = "test_leader".to_string();
1290        let candidate_lease_ttl = Duration::from_secs(5);
1291        let meta_lease_ttl = Duration::from_secs(2);
1292        let execution_timeout = Duration::from_secs(10);
1293        let idle_session_timeout = Duration::from_secs(0);
1294        let uuid = uuid::Uuid::new_v4().to_string();
1295        let table_name = "test_elected_failed_greptime-metakv";
1296        let client = create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1297            .await
1298            .unwrap();
1299
1300        let (tx, _) = broadcast::channel(100);
1301        let leader_mysql_election = MySqlElection {
1302            leader_value: leader_value.clone(),
1303            client,
1304            is_leader: AtomicBool::new(false),
1305            leader_infancy: AtomicBool::new(true),
1306            leader_watcher: tx,
1307            store_key_prefix: uuid,
1308            candidate_lease_ttl,
1309            meta_lease_ttl,
1310            sql_set: ElectionSqlFactory::new(table_name).build(),
1311        };
1312
1313        let incorrect_lease = Lease::default();
1314        let err = elected(&leader_mysql_election, table_name, Some(incorrect_lease))
1315            .await
1316            .unwrap_err();
1317        assert_matches!(err, error::Error::ElectionLeaderLeaseChanged { .. });
1318        let lease = get_lease(&leader_mysql_election).await;
1319        assert!(lease.is_none());
1320        drop_table(&leader_mysql_election.client, table_name).await;
1321    }
1322
1323    #[tokio::test]
1324    async fn test_reelection_with_idle_session_timeout() {
1325        maybe_skip_mysql_integration_test!();
1326        let leader_value = "test_leader".to_string();
1327        let uuid = uuid::Uuid::new_v4().to_string();
1328        let table_name = "test_reelection_greptime-metakv";
1329        let candidate_lease_ttl = Duration::from_secs(5);
1330        let meta_lease_ttl = Duration::from_secs(5);
1331        let execution_timeout = Duration::from_secs(10);
1332        let idle_session_timeout = Duration::from_secs(2);
1333        let client = create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1334            .await
1335            .unwrap();
1336
1337        let (tx, _) = broadcast::channel(100);
1338        let leader_mysql_election = MySqlElection {
1339            leader_value: leader_value.clone(),
1340            client,
1341            is_leader: AtomicBool::new(false),
1342            leader_infancy: AtomicBool::new(true),
1343            leader_watcher: tx,
1344            store_key_prefix: uuid,
1345            candidate_lease_ttl,
1346            meta_lease_ttl,
1347            sql_set: ElectionSqlFactory::new(table_name).build(),
1348        };
1349
1350        elected(&leader_mysql_election, table_name, None)
1351            .await
1352            .unwrap();
1353        let lease = get_lease(&leader_mysql_election).await.unwrap();
1354        assert_eq!(lease.leader_value, leader_value);
1355        assert!(lease.expire_time > lease.current);
1356        assert!(leader_mysql_election.is_leader());
1357        // Wait for mysql server close the inactive connection.
1358        tokio::time::sleep(Duration::from_millis(2100)).await;
1359        // Should be failed.
1360        leader_mysql_election
1361            .client
1362            .lock()
1363            .await
1364            .query(sqlx::query("SELECT 1"), "SELECT 1")
1365            .await
1366            .unwrap_err();
1367        // Reset the client.
1368        leader_mysql_election
1369            .client
1370            .lock()
1371            .await
1372            .reset_client()
1373            .await
1374            .unwrap();
1375
1376        // Should able to re-elected.
1377        elected(&leader_mysql_election, table_name, Some(lease.clone()))
1378            .await
1379            .unwrap();
1380        let lease = get_lease(&leader_mysql_election).await.unwrap();
1381        assert_eq!(lease.leader_value, leader_value);
1382        assert!(lease.expire_time > lease.current);
1383        assert!(leader_mysql_election.is_leader());
1384        drop_table(&leader_mysql_election.client, table_name).await;
1385    }
1386
1387    #[tokio::test]
1388    async fn test_elected_and_step_down() {
1389        maybe_skip_mysql_integration_test!();
1390        let leader_value = "test_leader".to_string();
1391        let candidate_lease_ttl = Duration::from_secs(5);
1392        let meta_lease_ttl = Duration::from_secs(2);
1393        let execution_timeout = Duration::from_secs(10);
1394        let idle_session_timeout = Duration::from_secs(0);
1395        let uuid = uuid::Uuid::new_v4().to_string();
1396        let table_name = "test_elected_and_step_down_greptime-metakv";
1397        let client = create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1398            .await
1399            .unwrap();
1400
1401        let (tx, mut rx) = broadcast::channel(100);
1402        let leader_mysql_election = MySqlElection {
1403            leader_value: leader_value.clone(),
1404            client,
1405            is_leader: AtomicBool::new(false),
1406            leader_infancy: AtomicBool::new(true),
1407            leader_watcher: tx,
1408            store_key_prefix: uuid,
1409            candidate_lease_ttl,
1410            meta_lease_ttl,
1411            sql_set: ElectionSqlFactory::new(table_name).build(),
1412        };
1413
1414        elected(&leader_mysql_election, table_name, None)
1415            .await
1416            .unwrap();
1417        let lease = get_lease(&leader_mysql_election).await.unwrap();
1418        assert_eq!(lease.leader_value, leader_value);
1419        assert!(lease.expire_time > lease.current);
1420        assert!(leader_mysql_election.is_leader());
1421
1422        match rx.recv().await {
1423            Ok(LeaderChangeMessage::Elected(key)) => {
1424                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1425                assert_eq!(
1426                    String::from_utf8_lossy(key.key()),
1427                    leader_mysql_election.election_key()
1428                );
1429                assert_eq!(key.lease_id(), i64::default());
1430                assert_eq!(key.revision(), i64::default());
1431            }
1432            _ => panic!("Expected LeaderChangeMessage::Elected"),
1433        }
1434
1435        leader_mysql_election
1436            .step_down_without_lock()
1437            .await
1438            .unwrap();
1439        let lease = get_lease(&leader_mysql_election).await.unwrap();
1440        assert_eq!(lease.leader_value, leader_value);
1441        assert!(!leader_mysql_election.is_leader());
1442
1443        match rx.recv().await {
1444            Ok(LeaderChangeMessage::StepDown(key)) => {
1445                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1446                assert_eq!(
1447                    String::from_utf8_lossy(key.key()),
1448                    leader_mysql_election.election_key()
1449                );
1450                assert_eq!(key.lease_id(), i64::default());
1451                assert_eq!(key.revision(), i64::default());
1452            }
1453            _ => panic!("Expected LeaderChangeMessage::StepDown"),
1454        }
1455
1456        elected(&leader_mysql_election, table_name, Some(lease.clone()))
1457            .await
1458            .unwrap();
1459        let lease = get_lease(&leader_mysql_election).await.unwrap();
1460        assert_eq!(lease.leader_value, leader_value);
1461        assert!(lease.expire_time > lease.current);
1462        assert!(leader_mysql_election.is_leader());
1463
1464        match rx.recv().await {
1465            Ok(LeaderChangeMessage::Elected(key)) => {
1466                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1467                assert_eq!(
1468                    String::from_utf8_lossy(key.key()),
1469                    leader_mysql_election.election_key()
1470                );
1471                assert_eq!(key.lease_id(), i64::default());
1472                assert_eq!(key.revision(), i64::default());
1473            }
1474            _ => panic!("Expected LeaderChangeMessage::Elected"),
1475        }
1476
1477        drop_table(&leader_mysql_election.client, table_name).await;
1478    }
1479
1480    #[tokio::test]
1481    async fn test_campaign() {
1482        maybe_skip_mysql_integration_test!();
1483        let leader_value = "test_leader".to_string();
1484        let uuid = uuid::Uuid::new_v4().to_string();
1485        let table_name = "test_leader_action_greptime-metakv";
1486        let candidate_lease_ttl = Duration::from_secs(5);
1487        let meta_lease_ttl = Duration::from_secs(2);
1488        let execution_timeout = Duration::from_secs(10);
1489        let idle_session_timeout = Duration::from_secs(0);
1490        let client = create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1491            .await
1492            .unwrap();
1493
1494        let (tx, mut rx) = broadcast::channel(100);
1495        let leader_mysql_election = MySqlElection {
1496            leader_value: leader_value.clone(),
1497            client,
1498            is_leader: AtomicBool::new(false),
1499            leader_infancy: AtomicBool::new(true),
1500            leader_watcher: tx,
1501            store_key_prefix: uuid,
1502            candidate_lease_ttl,
1503            meta_lease_ttl,
1504            sql_set: ElectionSqlFactory::new(table_name).build(),
1505        };
1506
1507        // Step 1: No leader exists, campaign and elected.
1508        leader_mysql_election.do_campaign().await.unwrap();
1509        let lease = get_lease(&leader_mysql_election).await.unwrap();
1510        assert_eq!(lease.leader_value, leader_value);
1511        assert!(lease.expire_time > lease.current);
1512        assert!(leader_mysql_election.is_leader());
1513
1514        match rx.recv().await {
1515            Ok(LeaderChangeMessage::Elected(key)) => {
1516                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1517                assert_eq!(
1518                    String::from_utf8_lossy(key.key()),
1519                    leader_mysql_election.election_key()
1520                );
1521                assert_eq!(key.lease_id(), i64::default());
1522                assert_eq!(key.revision(), i64::default());
1523            }
1524            _ => panic!("Expected LeaderChangeMessage::Elected"),
1525        }
1526
1527        // Step 2: As a leader, renew the lease.
1528        leader_mysql_election.do_campaign().await.unwrap();
1529        let new_lease = get_lease(&leader_mysql_election).await.unwrap();
1530        assert_eq!(lease.leader_value, leader_value);
1531        // The lease should be renewed.
1532        assert!(new_lease.expire_time > lease.expire_time);
1533        assert!(new_lease.expire_time > new_lease.current);
1534        assert!(leader_mysql_election.is_leader());
1535
1536        // Step 3: Something wrong, the leader lease expired.
1537        tokio::time::sleep(meta_lease_ttl + Duration::from_secs(1)).await;
1538        leader_mysql_election.do_campaign().await.unwrap();
1539        let lease = get_lease(&leader_mysql_election).await.unwrap();
1540        assert_eq!(lease.leader_value, leader_value);
1541        assert!(lease.expire_time <= lease.current);
1542        assert!(!leader_mysql_election.is_leader());
1543
1544        match rx.recv().await {
1545            Ok(LeaderChangeMessage::StepDown(key)) => {
1546                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1547                assert_eq!(
1548                    String::from_utf8_lossy(key.key()),
1549                    leader_mysql_election.election_key()
1550                );
1551                assert_eq!(key.lease_id(), i64::default());
1552                assert_eq!(key.revision(), i64::default());
1553            }
1554            _ => panic!("Expected LeaderChangeMessage::StepDown"),
1555        }
1556
1557        // Step 4: Re-elect itself.
1558        leader_mysql_election.do_campaign().await.unwrap();
1559        let lease = get_lease(&leader_mysql_election).await.unwrap();
1560        assert_eq!(lease.leader_value, leader_value);
1561        assert!(lease.expire_time > lease.current);
1562        assert!(leader_mysql_election.is_leader());
1563
1564        match rx.recv().await {
1565            Ok(LeaderChangeMessage::Elected(key)) => {
1566                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1567                assert_eq!(
1568                    String::from_utf8_lossy(key.key()),
1569                    leader_mysql_election.election_key()
1570                );
1571                assert_eq!(key.lease_id(), i64::default());
1572                assert_eq!(key.revision(), i64::default());
1573            }
1574            _ => panic!("Expected LeaderChangeMessage::Elected"),
1575        }
1576
1577        // Step 5: Something wrong, the leader key is deleted by other followers.
1578        {
1579            let client = leader_mysql_election.client.lock().await;
1580            let mut executor = Executor::Default(client);
1581            leader_mysql_election
1582                .delete_value(&leader_mysql_election.election_key(), &mut executor)
1583                .await
1584                .unwrap();
1585        }
1586        leader_mysql_election.do_campaign().await.unwrap();
1587        let res = get_lease(&leader_mysql_election).await;
1588        assert!(res.is_none());
1589        assert!(!leader_mysql_election.is_leader());
1590
1591        match rx.recv().await {
1592            Ok(LeaderChangeMessage::StepDown(key)) => {
1593                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1594                assert_eq!(
1595                    String::from_utf8_lossy(key.key()),
1596                    leader_mysql_election.election_key()
1597                );
1598                assert_eq!(key.lease_id(), i64::default());
1599                assert_eq!(key.revision(), i64::default());
1600            }
1601            _ => panic!("Expected LeaderChangeMessage::StepDown"),
1602        }
1603
1604        // Step 6: Re-elect itself.
1605        leader_mysql_election.do_campaign().await.unwrap();
1606        let lease = get_lease(&leader_mysql_election).await.unwrap();
1607        assert_eq!(lease.leader_value, leader_value);
1608        assert!(lease.expire_time > lease.current);
1609        assert!(leader_mysql_election.is_leader());
1610
1611        match rx.recv().await {
1612            Ok(LeaderChangeMessage::Elected(key)) => {
1613                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1614                assert_eq!(
1615                    String::from_utf8_lossy(key.key()),
1616                    leader_mysql_election.election_key()
1617                );
1618                assert_eq!(key.lease_id(), i64::default());
1619                assert_eq!(key.revision(), i64::default());
1620            }
1621            _ => panic!("Expected LeaderChangeMessage::Elected"),
1622        }
1623
1624        // Step 7: Something wrong, the leader key changed by others.
1625        let another_leader_key = "another_leader";
1626        {
1627            let client = leader_mysql_election.client.lock().await;
1628            let mut executor = Executor::Default(client);
1629            leader_mysql_election
1630                .delete_value(&leader_mysql_election.election_key(), &mut executor)
1631                .await
1632                .unwrap();
1633            leader_mysql_election
1634                .put_value_with_lease(
1635                    &leader_mysql_election.election_key(),
1636                    another_leader_key,
1637                    10,
1638                    &mut executor,
1639                )
1640                .await
1641                .unwrap();
1642        }
1643        leader_mysql_election.do_campaign().await.unwrap();
1644        let lease = get_lease(&leader_mysql_election).await.unwrap();
1645        // Different from pg, mysql will not delete the key, just step down.
1646        assert_eq!(lease.leader_value, another_leader_key);
1647        assert!(lease.expire_time > lease.current);
1648        assert!(!leader_mysql_election.is_leader());
1649
1650        match rx.recv().await {
1651            Ok(LeaderChangeMessage::StepDown(key)) => {
1652                assert_eq!(String::from_utf8_lossy(key.name()), leader_value);
1653                assert_eq!(
1654                    String::from_utf8_lossy(key.key()),
1655                    leader_mysql_election.election_key()
1656                );
1657                assert_eq!(key.lease_id(), i64::default());
1658                assert_eq!(key.revision(), i64::default());
1659            }
1660            _ => panic!("Expected LeaderChangeMessage::StepDown"),
1661        }
1662
1663        drop_table(&leader_mysql_election.client, table_name).await;
1664    }
1665
1666    #[tokio::test]
1667    async fn test_reset_campaign() {
1668        maybe_skip_mysql_integration_test!();
1669        common_telemetry::init_default_ut_logging();
1670        let leader_value = "test_leader".to_string();
1671        let uuid = uuid::Uuid::new_v4().to_string();
1672        let table_name = "test_reset_campaign_greptime-metakv";
1673        let candidate_lease_ttl = Duration::from_secs(5);
1674        let meta_lease_ttl = Duration::from_secs(2);
1675        let execution_timeout = Duration::from_secs(10);
1676        let idle_session_timeout = Duration::from_secs(0);
1677        let client = create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1678            .await
1679            .unwrap();
1680
1681        let (tx, _) = broadcast::channel(100);
1682        let leader_mysql_election = MySqlElection {
1683            leader_value,
1684            client,
1685            is_leader: AtomicBool::new(false),
1686            leader_infancy: AtomicBool::new(true),
1687            leader_watcher: tx,
1688            store_key_prefix: uuid,
1689            candidate_lease_ttl,
1690            meta_lease_ttl,
1691            sql_set: ElectionSqlFactory::new(table_name).build(),
1692        };
1693        leader_mysql_election
1694            .is_leader
1695            .store(true, Ordering::Relaxed);
1696        leader_mysql_election.reset_campaign().await;
1697        assert!(!leader_mysql_election.is_leader());
1698        drop_table(&leader_mysql_election.client, table_name).await;
1699    }
1700
1701    #[tokio::test]
1702    async fn test_follower_action() {
1703        maybe_skip_mysql_integration_test!();
1704        common_telemetry::init_default_ut_logging();
1705        let candidate_lease_ttl = Duration::from_secs(5);
1706        let meta_lease_ttl = Duration::from_secs(1);
1707        let execution_timeout = Duration::from_secs(10);
1708        let idle_session_timeout = Duration::from_secs(0);
1709        let uuid = uuid::Uuid::new_v4().to_string();
1710        let table_name = "test_follower_action_greptime-metakv";
1711
1712        let follower_client =
1713            create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1714                .await
1715                .unwrap();
1716        let (tx, mut rx) = broadcast::channel(100);
1717        let follower_mysql_election = MySqlElection {
1718            leader_value: "test_follower".to_string(),
1719            client: follower_client,
1720            is_leader: AtomicBool::new(false),
1721            leader_infancy: AtomicBool::new(true),
1722            leader_watcher: tx,
1723            store_key_prefix: uuid.clone(),
1724            candidate_lease_ttl,
1725            meta_lease_ttl,
1726            sql_set: ElectionSqlFactory::new(table_name).build(),
1727        };
1728
1729        let leader_client =
1730            create_mysql_client(Some(table_name), execution_timeout, idle_session_timeout)
1731                .await
1732                .unwrap();
1733        let (tx, _) = broadcast::channel(100);
1734        let leader_mysql_election = MySqlElection {
1735            leader_value: "test_leader".to_string(),
1736            client: leader_client,
1737            is_leader: AtomicBool::new(false),
1738            leader_infancy: AtomicBool::new(true),
1739            leader_watcher: tx,
1740            store_key_prefix: uuid,
1741            candidate_lease_ttl,
1742            meta_lease_ttl,
1743            sql_set: ElectionSqlFactory::new(table_name).build(),
1744        };
1745
1746        leader_mysql_election.do_campaign().await.unwrap();
1747
1748        // Step 1: As a follower, the leader exists and the lease is not expired. Do nothing.
1749        follower_mysql_election.do_campaign().await.unwrap();
1750
1751        // Step 2: As a follower, the leader exists but the lease expired. Re-elect itself.
1752        tokio::time::sleep(meta_lease_ttl + Duration::from_secs(1)).await;
1753        follower_mysql_election.do_campaign().await.unwrap();
1754        assert!(follower_mysql_election.is_leader());
1755
1756        match rx.recv().await {
1757            Ok(LeaderChangeMessage::Elected(key)) => {
1758                assert_eq!(String::from_utf8_lossy(key.name()), "test_follower");
1759                assert_eq!(
1760                    String::from_utf8_lossy(key.key()),
1761                    follower_mysql_election.election_key()
1762                );
1763                assert_eq!(key.lease_id(), i64::default());
1764                assert_eq!(key.revision(), i64::default());
1765            }
1766            _ => panic!("Expected LeaderChangeMessage::Elected"),
1767        }
1768
1769        drop_table(&follower_mysql_election.client, table_name).await;
1770    }
1771
1772    #[tokio::test]
1773    async fn test_wait_timeout() {
1774        maybe_skip_mysql_integration_test!();
1775        common_telemetry::init_default_ut_logging();
1776        let execution_timeout = Duration::from_secs(10);
1777        let idle_session_timeout = Duration::from_secs(1);
1778
1779        let client = create_mysql_client(None, execution_timeout, idle_session_timeout)
1780            .await
1781            .unwrap();
1782        tokio::time::sleep(Duration::from_millis(1100)).await;
1783        // Wait for the idle session timeout.
1784        let err = client
1785            .lock()
1786            .await
1787            .query(sqlx::query("SELECT 1"), "SELECT 1")
1788            .await
1789            .unwrap_err();
1790        assert_matches!(err, error::Error::MySqlExecution { .. });
1791        // Reset the client and try again.
1792        client.lock().await.reset_client().await.unwrap();
1793        let _ = client
1794            .lock()
1795            .await
1796            .query(sqlx::query("SELECT 1"), "SELECT 1")
1797            .await
1798            .unwrap();
1799    }
1800}