diff --git a/config/config.md b/config/config.md index 8f17a6cf61..075378d025 100644 --- a/config/config.md +++ b/config/config.md @@ -41,6 +41,7 @@ | `mysql.addr` | String | `127.0.0.1:4002` | The addr to bind the MySQL server. | | `mysql.runtime_size` | Integer | `2` | The number of server worker threads. | | `mysql.keep_alive` | String | `0s` | Server-side keep-alive time.
Set to 0 (default) to disable. | +| `mysql.prepared_stmt_cache_size` | Integer | `10000` | Maximum entries in the MySQL prepared statement cache; default is 10,000. | | `mysql.tls` | -- | -- | -- | | `mysql.tls.mode` | String | `disable` | TLS mode, refer to https://www.postgresql.org/docs/current/libpq-ssl.html
- `disable` (default value)
- `prefer`
- `require`
- `verify-ca`
- `verify-full` | | `mysql.tls.cert_path` | String | Unset | Certificate file path. | @@ -248,6 +249,7 @@ | `mysql.addr` | String | `127.0.0.1:4002` | The addr to bind the MySQL server. | | `mysql.runtime_size` | Integer | `2` | The number of server worker threads. | | `mysql.keep_alive` | String | `0s` | Server-side keep-alive time.
Set to 0 (default) to disable. | +| `mysql.prepared_stmt_cache_size` | Integer | `10000` | Maximum entries in the MySQL prepared statement cache; default is 10,000. | | `mysql.tls` | -- | -- | -- | | `mysql.tls.mode` | String | `disable` | TLS mode, refer to https://www.postgresql.org/docs/current/libpq-ssl.html
- `disable` (default value)
- `prefer`
- `require`
- `verify-ca`
- `verify-full` | | `mysql.tls.cert_path` | String | Unset | Certificate file path. | diff --git a/config/frontend.example.toml b/config/frontend.example.toml index 621eddc8a7..6241d7346a 100644 --- a/config/frontend.example.toml +++ b/config/frontend.example.toml @@ -90,6 +90,8 @@ runtime_size = 2 ## Server-side keep-alive time. ## Set to 0 (default) to disable. keep_alive = "0s" +## Maximum entries in the MySQL prepared statement cache; default is 10,000. +prepared_stmt_cache_size = 10000 # MySQL server TLS options. [mysql.tls] diff --git a/config/standalone.example.toml b/config/standalone.example.toml index a315ca75d2..9d82d884c6 100644 --- a/config/standalone.example.toml +++ b/config/standalone.example.toml @@ -85,7 +85,8 @@ runtime_size = 2 ## Server-side keep-alive time. ## Set to 0 (default) to disable. keep_alive = "0s" - +## Maximum entries in the MySQL prepared statement cache; default is 10,000. +prepared_stmt_cache_size= 10000 # MySQL server TLS options. [mysql.tls] diff --git a/src/frontend/src/server.rs b/src/frontend/src/server.rs index 3e91395fde..0c6493f79b 100644 --- a/src/frontend/src/server.rs +++ b/src/frontend/src/server.rs @@ -230,6 +230,7 @@ where tls_server_config, opts.keep_alive.as_secs(), opts.reject_no_database.unwrap_or(false), + opts.prepared_stmt_cache_size, )), Some(instance.process_manager().clone()), ); diff --git a/src/frontend/src/service_config/mysql.rs b/src/frontend/src/service_config/mysql.rs index 20753554ff..f55dd78de8 100644 --- a/src/frontend/src/service_config/mysql.rs +++ b/src/frontend/src/service_config/mysql.rs @@ -29,6 +29,7 @@ pub struct MysqlOptions { #[serde(default = "Default::default")] #[serde(with = "humantime_serde")] pub keep_alive: std::time::Duration, + pub prepared_stmt_cache_size: usize, } impl Default for MysqlOptions { @@ -40,6 +41,7 @@ impl Default for MysqlOptions { tls: TlsOption::default(), reject_no_database: None, keep_alive: std::time::Duration::from_secs(0), + prepared_stmt_cache_size: 10000, } } } diff --git a/src/servers/src/mysql/handler.rs b/src/servers/src/mysql/handler.rs index 0a5e7909a0..8cc858507c 100644 --- a/src/servers/src/mysql/handler.rs +++ b/src/servers/src/mysql/handler.rs @@ -83,6 +83,7 @@ pub struct MysqlInstanceShim { prepared_stmts: Arc>>, prepared_stmts_counter: AtomicU32, process_id: u32, + prepared_stmt_cache_size: usize, } impl MysqlInstanceShim { @@ -91,6 +92,7 @@ impl MysqlInstanceShim { user_provider: Option, client_addr: SocketAddr, process_id: u32, + prepared_stmt_cache_size: usize, ) -> MysqlInstanceShim { // init a random salt let mut bs = vec![0u8; 20]; @@ -118,6 +120,7 @@ impl MysqlInstanceShim { prepared_stmts: Default::default(), prepared_stmts_counter: AtomicU32::new(1), process_id, + prepared_stmt_cache_size, } } @@ -159,9 +162,24 @@ impl MysqlInstanceShim { } /// Save query and logical plan with a given statement key - fn save_plan(&self, plan: SqlPlan, stmt_key: String) { + fn save_plan(&self, plan: SqlPlan, stmt_key: String) -> Result<()> { let mut prepared_stmts = self.prepared_stmts.write(); + let max_capacity = self.prepared_stmt_cache_size; + + let is_update = prepared_stmts.contains_key(&stmt_key); + + if !is_update && prepared_stmts.len() >= max_capacity { + return error::InternalSnafu { + err_msg: format!( + "Prepared statement cache is full, max capacity: {}", + max_capacity + ), + } + .fail(); + } + let _ = prepared_stmts.insert(stmt_key, plan); + Ok(()) } /// Retrieve the query and logical plan by a given statement key @@ -237,7 +255,11 @@ impl MysqlInstanceShim { schema: None, }, stmt_key, - ); + ) + .map_err(|e| { + error!(e; "Failed to save prepared statement"); + e + })?; } else { self.save_plan( SqlPlan { @@ -247,7 +269,11 @@ impl MysqlInstanceShim { schema, }, stmt_key, - ); + ) + .map_err(|e| { + error!(e; "Failed to save prepared statement"); + e + })?; } Ok((params, columns)) diff --git a/src/servers/src/mysql/server.rs b/src/servers/src/mysql/server.rs index 57aef8796f..9f042de765 100644 --- a/src/servers/src/mysql/server.rs +++ b/src/servers/src/mysql/server.rs @@ -77,6 +77,8 @@ pub struct MysqlSpawnConfig { keep_alive_secs: u64, // other shim config reject_no_database: bool, + // prepared statement cache capacity + prepared_stmt_cache_size: usize, } impl MysqlSpawnConfig { @@ -85,12 +87,14 @@ impl MysqlSpawnConfig { tls: Arc, keep_alive_secs: u64, reject_no_database: bool, + prepared_stmt_cache_size: usize, ) -> MysqlSpawnConfig { MysqlSpawnConfig { force_tls, tls, keep_alive_secs, reject_no_database, + prepared_stmt_cache_size, } } @@ -201,6 +205,7 @@ impl MysqlServer { spawn_ref.user_provider(), stream.peer_addr()?, process_id, + spawn_config.prepared_stmt_cache_size, ); let (mut r, w) = stream.into_split(); let mut w = BufWriter::with_capacity(DEFAULT_RESULT_SET_WRITE_BUFFER_SIZE, w); diff --git a/src/servers/tests/mysql/mysql_server_test.rs b/src/servers/tests/mysql/mysql_server_test.rs index e6abdf22d2..f89da0043d 100644 --- a/src/servers/tests/mysql/mysql_server_test.rs +++ b/src/servers/tests/mysql/mysql_server_test.rs @@ -72,6 +72,7 @@ fn create_mysql_server(table: TableRef, opts: MysqlOpts<'_>) -> Result