@@ -12,12 +12,21 @@ use tracing::{info, warn};
1212
1313use crate :: auth:: { AuthUserConfig , BackendAuthConfig , FrontendAuthConfig } ;
1414use crate :: protocol:: redis:: { RedisCommand , RespValue } ;
15+ use crate :: slowlog:: Slowlog ;
1516
1617/// Environment variable controlling the default worker thread count when a
1718/// cluster omits the `thread` field.
1819pub const ENV_DEFAULT_THREADS : & str = "ASTER_DEFAULT_THREAD" ;
1920const DUMP_VALUE_DEFAULT : & str = "default" ;
2021
22+ fn default_slowlog_log_slower_than ( ) -> i64 {
23+ 10_000
24+ }
25+
26+ fn default_slowlog_max_len ( ) -> usize {
27+ 128
28+ }
29+
2130#[ derive( Debug , Clone , Deserialize , Serialize ) ]
2231pub struct Config {
2332 #[ serde( default ) ]
@@ -130,6 +139,10 @@ pub struct ClusterConfig {
130139 pub backend_auth : Option < BackendAuthConfig > ,
131140 #[ serde( default ) ]
132141 pub backend_password : Option < String > ,
142+ #[ serde( default = "default_slowlog_log_slower_than" ) ]
143+ pub slowlog_log_slower_than : i64 ,
144+ #[ serde( default = "default_slowlog_max_len" ) ]
145+ pub slowlog_max_len : usize ,
133146}
134147
135148impl ClusterConfig {
@@ -161,6 +174,12 @@ impl ClusterConfig {
161174 self . name
162175 ) ;
163176 }
177+ if self . slowlog_log_slower_than < -1 {
178+ bail ! (
179+ "cluster {} slowlog-log-slower-than must be >= -1" ,
180+ self . name
181+ ) ;
182+ }
164183 Ok ( ( ) )
165184 }
166185
@@ -290,6 +309,7 @@ fn atomic_to_option(value: i64) -> Option<u64> {
290309struct ClusterEntry {
291310 index : usize ,
292311 runtime : Arc < ClusterRuntime > ,
312+ slowlog : Arc < Slowlog > ,
293313}
294314
295315#[ derive( Debug ) ]
@@ -304,14 +324,20 @@ impl ConfigManager {
304324 let mut clusters = HashMap :: new ( ) ;
305325 for ( index, cluster) in config. clusters ( ) . iter ( ) . enumerate ( ) {
306326 let key = cluster. name . to_ascii_lowercase ( ) ;
327+ let runtime = Arc :: new ( ClusterRuntime :: new (
328+ cluster. read_timeout ,
329+ cluster. write_timeout ,
330+ ) ) ;
331+ let slowlog = Arc :: new ( Slowlog :: new (
332+ cluster. slowlog_log_slower_than ,
333+ cluster. slowlog_max_len ,
334+ ) ) ;
307335 clusters. insert (
308336 key,
309337 ClusterEntry {
310338 index,
311- runtime : Arc :: new ( ClusterRuntime :: new (
312- cluster. read_timeout ,
313- cluster. write_timeout ,
314- ) ) ,
339+ runtime,
340+ slowlog,
315341 } ,
316342 ) ;
317343 }
@@ -329,6 +355,12 @@ impl ConfigManager {
329355 . map ( |entry| entry. runtime . clone ( ) )
330356 }
331357
358+ pub fn slowlog_for ( & self , name : & str ) -> Option < Arc < Slowlog > > {
359+ self . clusters
360+ . get ( & name. to_ascii_lowercase ( ) )
361+ . map ( |entry| entry. slowlog . clone ( ) )
362+ }
363+
332364 pub async fn handle_command ( & self , command : & RedisCommand ) -> Option < RespValue > {
333365 if !command. command_name ( ) . eq_ignore_ascii_case ( b"CONFIG" ) {
334366 return None ;
@@ -429,6 +461,28 @@ impl ConfigManager {
429461 "cluster write_timeout updated via CONFIG SET"
430462 ) ;
431463 }
464+ ClusterField :: SlowlogLogSlowerThan => {
465+ let parsed = parse_slowlog_threshold ( value) ?;
466+ entry. slowlog . set_threshold ( parsed) ;
467+ let mut guard = self . config . write ( ) ;
468+ guard. clusters_mut ( ) [ entry. index ] . slowlog_log_slower_than = parsed;
469+ info ! (
470+ cluster = cluster_name,
471+ value = value,
472+ "cluster slowlog_log_slower_than updated via CONFIG SET"
473+ ) ;
474+ }
475+ ClusterField :: SlowlogMaxLen => {
476+ let parsed = parse_slowlog_len ( value) ?;
477+ entry. slowlog . set_max_len ( parsed) ;
478+ let mut guard = self . config . write ( ) ;
479+ guard. clusters_mut ( ) [ entry. index ] . slowlog_max_len = parsed;
480+ info ! (
481+ cluster = cluster_name,
482+ value = value,
483+ "cluster slowlog_max_len updated via CONFIG SET"
484+ ) ;
485+ }
432486 }
433487 Ok ( ( ) )
434488 }
@@ -457,6 +511,14 @@ impl ConfigManager {
457511 format ! ( "cluster.{}.write-timeout" , name) ,
458512 option_to_string ( runtime. write_timeout ( ) ) ,
459513 ) ) ;
514+ entries. push ( (
515+ format ! ( "cluster.{}.slowlog-log-slower-than" , name) ,
516+ entry. slowlog . threshold ( ) . to_string ( ) ,
517+ ) ) ;
518+ entries. push ( (
519+ format ! ( "cluster.{}.slowlog-max-len" , name) ,
520+ entry. slowlog . max_len ( ) . to_string ( ) ,
521+ ) ) ;
460522 }
461523 }
462524 entries. sort_by ( |a, b| a. 0 . cmp ( & b. 0 ) ) ;
@@ -491,6 +553,8 @@ fn parse_key(key: &str) -> Result<(String, ClusterField)> {
491553 let field = match field. to_ascii_lowercase ( ) . as_str ( ) {
492554 "read-timeout" => ClusterField :: ReadTimeout ,
493555 "write-timeout" => ClusterField :: WriteTimeout ,
556+ "slowlog-log-slower-than" => ClusterField :: SlowlogLogSlowerThan ,
557+ "slowlog-max-len" => ClusterField :: SlowlogMaxLen ,
494558 unknown => bail ! ( "unknown cluster field '{}'" , unknown) ,
495559 } ;
496560 Ok ( ( cluster. to_string ( ) , field) )
@@ -507,6 +571,28 @@ fn parse_timeout_value(value: &str) -> Result<Option<u64>> {
507571 Ok ( Some ( parsed) )
508572}
509573
574+ fn parse_slowlog_threshold ( value : & str ) -> Result < i64 > {
575+ let parsed: i64 = value
576+ . trim ( )
577+ . parse ( )
578+ . with_context ( || format ! ( "invalid slowlog-log-slower-than value '{}'" , value) ) ?;
579+ if parsed < -1 {
580+ bail ! ( "slowlog-log-slower-than must be >= -1" ) ;
581+ }
582+ Ok ( parsed)
583+ }
584+
585+ fn parse_slowlog_len ( value : & str ) -> Result < usize > {
586+ let parsed: i64 = value
587+ . trim ( )
588+ . parse ( )
589+ . with_context ( || format ! ( "invalid slowlog-max-len value '{}'" , value) ) ?;
590+ if parsed < 0 {
591+ bail ! ( "slowlog-max-len must be >= 0" ) ;
592+ }
593+ usize:: try_from ( parsed) . map_err ( |_| anyhow ! ( "slowlog-max-len is too large" ) )
594+ }
595+
510596fn option_to_string ( value : Option < u64 > ) -> String {
511597 value
512598 . map ( |v| v. to_string ( ) )
@@ -530,6 +616,8 @@ fn err_response<T: ToString>(message: T) -> RespValue {
530616enum ClusterField {
531617 ReadTimeout ,
532618 WriteTimeout ,
619+ SlowlogLogSlowerThan ,
620+ SlowlogMaxLen ,
533621}
534622
535623fn wildcard_match ( pattern : & str , target : & str ) -> bool {
0 commit comments