1use std::collections::HashMap;
4use std::fs;
5use std::path::PathBuf;
6use std::time::Duration;
7
8use tokio::process::Command;
9
10use crate::cli::RedisCli;
11use crate::error::{Error, Result};
12
13#[derive(Debug, Clone)]
38pub struct RedisServerConfig {
39 pub port: u16,
42 pub bind: String,
44 pub protected_mode: bool,
46 pub tcp_backlog: Option<u32>,
48 pub unixsocket: Option<PathBuf>,
50 pub unixsocketperm: Option<u32>,
52 pub timeout: Option<u32>,
54 pub tcp_keepalive: Option<u32>,
56
57 pub tls_port: Option<u16>,
60 pub tls_cert_file: Option<PathBuf>,
62 pub tls_key_file: Option<PathBuf>,
64 pub tls_key_file_pass: Option<String>,
66 pub tls_ca_cert_file: Option<PathBuf>,
68 pub tls_ca_cert_dir: Option<PathBuf>,
70 pub tls_auth_clients: Option<bool>,
72 pub tls_client_cert_file: Option<PathBuf>,
74 pub tls_client_key_file: Option<PathBuf>,
76 pub tls_client_key_file_pass: Option<String>,
78 pub tls_dh_params_file: Option<PathBuf>,
80 pub tls_ciphers: Option<String>,
82 pub tls_ciphersuites: Option<String>,
84 pub tls_protocols: Option<String>,
86 pub tls_prefer_server_ciphers: Option<bool>,
88 pub tls_session_caching: Option<bool>,
90 pub tls_session_cache_size: Option<u32>,
92 pub tls_session_cache_timeout: Option<u32>,
94 pub tls_replication: Option<bool>,
96 pub tls_cluster: Option<bool>,
98
99 pub daemonize: bool,
102 pub dir: PathBuf,
104 pub logfile: Option<String>,
106 pub loglevel: LogLevel,
108 pub databases: Option<u32>,
110
111 pub maxmemory: Option<String>,
114 pub maxmemory_policy: Option<String>,
116 pub maxmemory_samples: Option<u32>,
118 pub maxmemory_clients: Option<String>,
120 pub maxmemory_eviction_tenacity: Option<u32>,
122 pub maxclients: Option<u32>,
124 pub lfu_log_factor: Option<u32>,
126 pub lfu_decay_time: Option<u32>,
128 pub active_expire_effort: Option<u32>,
130
131 pub lazyfree_lazy_eviction: Option<bool>,
134 pub lazyfree_lazy_expire: Option<bool>,
136 pub lazyfree_lazy_server_del: Option<bool>,
138 pub lazyfree_lazy_user_del: Option<bool>,
140 pub lazyfree_lazy_user_flush: Option<bool>,
142
143 pub save: SavePolicy,
146 pub appendonly: bool,
148 pub appendfsync: Option<AppendFsync>,
150 pub appendfilename: Option<String>,
152 pub appenddirname: Option<PathBuf>,
154 pub aof_use_rdb_preamble: Option<bool>,
156 pub aof_load_truncated: Option<bool>,
158 pub aof_load_corrupt_tail_max_size: Option<String>,
160 pub aof_rewrite_incremental_fsync: Option<bool>,
162 pub aof_timestamp_enabled: Option<bool>,
164 pub auto_aof_rewrite_percentage: Option<u32>,
166 pub auto_aof_rewrite_min_size: Option<String>,
168 pub no_appendfsync_on_rewrite: Option<bool>,
170
171 pub replicaof: Option<(String, u16)>,
174 pub masterauth: Option<String>,
176 pub masteruser: Option<String>,
178 pub repl_backlog_size: Option<String>,
180 pub repl_backlog_ttl: Option<u32>,
182 pub repl_disable_tcp_nodelay: Option<bool>,
184 pub repl_diskless_load: Option<ReplDisklessLoad>,
186 pub repl_diskless_sync: Option<bool>,
188 pub repl_diskless_sync_delay: Option<u32>,
190 pub repl_diskless_sync_max_replicas: Option<u32>,
192 pub repl_ping_replica_period: Option<u32>,
194 pub repl_timeout: Option<u32>,
196 pub replica_announce_ip: Option<String>,
198 pub replica_announce_port: Option<u16>,
200 pub replica_announced: Option<bool>,
202 pub replica_full_sync_buffer_limit: Option<String>,
204 pub replica_ignore_disk_write_errors: Option<bool>,
206 pub replica_ignore_maxmemory: Option<bool>,
208 pub replica_lazy_flush: Option<bool>,
210 pub replica_priority: Option<u32>,
212 pub replica_read_only: Option<bool>,
214 pub replica_serve_stale_data: Option<bool>,
216 pub min_replicas_to_write: Option<u32>,
218 pub min_replicas_max_lag: Option<u32>,
220
221 pub password: Option<String>,
224 pub acl_file: Option<PathBuf>,
226
227 pub cluster_enabled: bool,
230 pub cluster_node_timeout: Option<u64>,
232 pub cluster_config_file: Option<PathBuf>,
234 pub cluster_require_full_coverage: Option<bool>,
236 pub cluster_allow_reads_when_down: Option<bool>,
238 pub cluster_allow_pubsubshard_when_down: Option<bool>,
240 pub cluster_allow_replica_migration: Option<bool>,
242 pub cluster_migration_barrier: Option<u32>,
244 pub cluster_replica_no_failover: Option<bool>,
246 pub cluster_replica_validity_factor: Option<u32>,
248 pub cluster_announce_ip: Option<String>,
250 pub cluster_announce_port: Option<u16>,
252 pub cluster_announce_bus_port: Option<u16>,
254 pub cluster_announce_tls_port: Option<u16>,
256 pub cluster_announce_hostname: Option<String>,
258 pub cluster_announce_human_nodename: Option<String>,
260 pub cluster_port: Option<u16>,
262 pub cluster_preferred_endpoint_type: Option<String>,
264 pub cluster_link_sendbuf_limit: Option<u64>,
266 pub cluster_compatibility_sample_ratio: Option<u32>,
268 pub cluster_slot_migration_handoff_max_lag_bytes: Option<u64>,
270 pub cluster_slot_migration_write_pause_timeout: Option<u64>,
272 pub cluster_slot_stats_enabled: Option<bool>,
274
275 pub hash_max_listpack_entries: Option<u32>,
278 pub hash_max_listpack_value: Option<u32>,
280 pub list_max_listpack_size: Option<i32>,
282 pub list_compress_depth: Option<u32>,
284 pub set_max_intset_entries: Option<u32>,
286 pub set_max_listpack_entries: Option<u32>,
288 pub set_max_listpack_value: Option<u32>,
290 pub zset_max_listpack_entries: Option<u32>,
292 pub zset_max_listpack_value: Option<u32>,
294 pub hll_sparse_max_bytes: Option<u32>,
296 pub stream_node_max_bytes: Option<u32>,
298 pub stream_node_max_entries: Option<u32>,
300 pub stream_idmp_duration: Option<u64>,
302 pub stream_idmp_maxsize: Option<u64>,
304
305 pub loadmodule: Vec<(PathBuf, Vec<String>)>,
308
309 pub hz: Option<u32>,
312 pub io_threads: Option<u32>,
314 pub io_threads_do_reads: Option<bool>,
316 pub notify_keyspace_events: Option<String>,
318
319 pub slowlog_log_slower_than: Option<i64>,
322 pub slowlog_max_len: Option<u32>,
324
325 pub latency_monitor_threshold: Option<u64>,
328 pub latency_tracking: Option<bool>,
330 pub latency_tracking_info_percentiles: Option<String>,
332
333 pub activedefrag: Option<bool>,
336 pub active_defrag_ignore_bytes: Option<String>,
338 pub active_defrag_threshold_lower: Option<u32>,
340 pub active_defrag_threshold_upper: Option<u32>,
342 pub active_defrag_cycle_min: Option<u32>,
344 pub active_defrag_cycle_max: Option<u32>,
346 pub active_defrag_max_scan_fields: Option<u32>,
348
349 pub syslog_enabled: Option<bool>,
352 pub syslog_ident: Option<String>,
354 pub syslog_facility: Option<String>,
356 pub supervised: Option<String>,
358 pub always_show_logo: Option<bool>,
360 pub set_proc_title: Option<bool>,
362 pub proc_title_template: Option<String>,
364
365 pub acl_pubsub_default: Option<String>,
368 pub acllog_max_len: Option<u32>,
370 pub enable_debug_command: Option<String>,
372 pub enable_module_command: Option<String>,
374 pub enable_protected_configs: Option<String>,
376 pub rename_command: Vec<(String, String)>,
378 pub sanitize_dump_payload: Option<String>,
380 pub hide_user_data_from_log: Option<bool>,
382
383 pub bind_source_addr: Option<String>,
386 pub busy_reply_threshold: Option<u64>,
388 pub client_output_buffer_limit: Vec<String>,
390 pub client_query_buffer_limit: Option<String>,
392 pub proto_max_bulk_len: Option<String>,
394 pub max_new_connections_per_cycle: Option<u32>,
396 pub max_new_tls_connections_per_cycle: Option<u32>,
398 pub socket_mark_id: Option<u32>,
400
401 pub dbfilename: Option<String>,
404 pub rdbcompression: Option<bool>,
406 pub rdbchecksum: Option<bool>,
408 pub rdb_save_incremental_fsync: Option<bool>,
410 pub rdb_del_sync_files: Option<bool>,
412 pub stop_writes_on_bgsave_error: Option<bool>,
414
415 pub shutdown_on_sigint: Option<String>,
418 pub shutdown_on_sigterm: Option<String>,
420 pub shutdown_timeout: Option<u32>,
422
423 pub activerehashing: Option<bool>,
426 pub crash_log_enabled: Option<bool>,
428 pub crash_memcheck_enabled: Option<bool>,
430 pub disable_thp: Option<bool>,
432 pub dynamic_hz: Option<bool>,
434 pub ignore_warnings: Option<String>,
436 pub include: Vec<PathBuf>,
438 pub jemalloc_bg_thread: Option<bool>,
440 pub locale_collate: Option<String>,
442 pub lua_time_limit: Option<u64>,
444 pub oom_score_adj: Option<String>,
446 pub oom_score_adj_values: Option<String>,
448 pub propagation_error_behavior: Option<String>,
450 pub tracking_table_max_keys: Option<u64>,
452
453 pub extra: HashMap<String, String>,
456
457 pub redis_server_bin: String,
460 pub redis_cli_bin: String,
462
463 pub no_stack_modules: bool,
466}
467
468#[derive(Debug, Clone, Copy)]
470pub enum AppendFsync {
471 Always,
473 Everysec,
475 No,
477}
478
479impl std::fmt::Display for AppendFsync {
480 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
481 match self {
482 AppendFsync::Always => f.write_str("always"),
483 AppendFsync::Everysec => f.write_str("everysec"),
484 AppendFsync::No => f.write_str("no"),
485 }
486 }
487}
488
489#[derive(Debug, Clone, Copy)]
494pub enum ReplDisklessLoad {
495 Disabled,
497 OnEmptyDb,
499 Swapdb,
501}
502
503impl std::fmt::Display for ReplDisklessLoad {
504 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
505 match self {
506 ReplDisklessLoad::Disabled => f.write_str("disabled"),
507 ReplDisklessLoad::OnEmptyDb => f.write_str("on-empty-db"),
508 ReplDisklessLoad::Swapdb => f.write_str("swapdb"),
509 }
510 }
511}
512
513#[derive(Debug, Clone, Default)]
518pub enum SavePolicy {
519 #[default]
521 Disabled,
522 Default,
524 Custom(Vec<(u64, u64)>),
526}
527
528#[derive(Debug, Clone, Copy)]
530pub enum LogLevel {
531 Debug,
533 Verbose,
535 Notice,
537 Warning,
539}
540
541impl std::fmt::Display for LogLevel {
542 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543 match self {
544 LogLevel::Debug => f.write_str("debug"),
545 LogLevel::Verbose => f.write_str("verbose"),
546 LogLevel::Notice => f.write_str("notice"),
547 LogLevel::Warning => f.write_str("warning"),
548 }
549 }
550}
551
552impl Default for RedisServerConfig {
553 fn default() -> Self {
554 Self {
555 port: 6379,
556 bind: "127.0.0.1".into(),
557 protected_mode: false,
558 tcp_backlog: None,
559 unixsocket: None,
560 unixsocketperm: None,
561 timeout: None,
562 tcp_keepalive: None,
563 tls_port: None,
564 tls_cert_file: None,
565 tls_key_file: None,
566 tls_key_file_pass: None,
567 tls_ca_cert_file: None,
568 tls_ca_cert_dir: None,
569 tls_auth_clients: None,
570 tls_client_cert_file: None,
571 tls_client_key_file: None,
572 tls_client_key_file_pass: None,
573 tls_dh_params_file: None,
574 tls_ciphers: None,
575 tls_ciphersuites: None,
576 tls_protocols: None,
577 tls_prefer_server_ciphers: None,
578 tls_session_caching: None,
579 tls_session_cache_size: None,
580 tls_session_cache_timeout: None,
581 tls_replication: None,
582 tls_cluster: None,
583 daemonize: true,
584 dir: std::env::temp_dir().join("redis-server-wrapper"),
585 logfile: None,
586 loglevel: LogLevel::Notice,
587 databases: None,
588 maxmemory: None,
589 maxmemory_policy: None,
590 maxmemory_samples: None,
591 maxmemory_clients: None,
592 maxmemory_eviction_tenacity: None,
593 maxclients: None,
594 lfu_log_factor: None,
595 lfu_decay_time: None,
596 active_expire_effort: None,
597 lazyfree_lazy_eviction: None,
598 lazyfree_lazy_expire: None,
599 lazyfree_lazy_server_del: None,
600 lazyfree_lazy_user_del: None,
601 lazyfree_lazy_user_flush: None,
602 save: SavePolicy::Disabled,
603 appendonly: false,
604 appendfsync: None,
605 appendfilename: None,
606 appenddirname: None,
607 aof_use_rdb_preamble: None,
608 aof_load_truncated: None,
609 aof_load_corrupt_tail_max_size: None,
610 aof_rewrite_incremental_fsync: None,
611 aof_timestamp_enabled: None,
612 auto_aof_rewrite_percentage: None,
613 auto_aof_rewrite_min_size: None,
614 no_appendfsync_on_rewrite: None,
615 replicaof: None,
616 masterauth: None,
617 masteruser: None,
618 repl_backlog_size: None,
619 repl_backlog_ttl: None,
620 repl_disable_tcp_nodelay: None,
621 repl_diskless_load: None,
622 repl_diskless_sync: None,
623 repl_diskless_sync_delay: None,
624 repl_diskless_sync_max_replicas: None,
625 repl_ping_replica_period: None,
626 repl_timeout: None,
627 replica_announce_ip: None,
628 replica_announce_port: None,
629 replica_announced: None,
630 replica_full_sync_buffer_limit: None,
631 replica_ignore_disk_write_errors: None,
632 replica_ignore_maxmemory: None,
633 replica_lazy_flush: None,
634 replica_priority: None,
635 replica_read_only: None,
636 replica_serve_stale_data: None,
637 min_replicas_to_write: None,
638 min_replicas_max_lag: None,
639 password: None,
640 acl_file: None,
641 cluster_enabled: false,
642 cluster_node_timeout: None,
643 cluster_config_file: None,
644 cluster_require_full_coverage: None,
645 cluster_allow_reads_when_down: None,
646 cluster_allow_pubsubshard_when_down: None,
647 cluster_allow_replica_migration: None,
648 cluster_migration_barrier: None,
649 cluster_replica_no_failover: None,
650 cluster_replica_validity_factor: None,
651 cluster_announce_ip: None,
652 cluster_announce_port: None,
653 cluster_announce_bus_port: None,
654 cluster_announce_tls_port: None,
655 cluster_announce_hostname: None,
656 cluster_announce_human_nodename: None,
657 cluster_port: None,
658 cluster_preferred_endpoint_type: None,
659 cluster_link_sendbuf_limit: None,
660 cluster_compatibility_sample_ratio: None,
661 cluster_slot_migration_handoff_max_lag_bytes: None,
662 cluster_slot_migration_write_pause_timeout: None,
663 cluster_slot_stats_enabled: None,
664 hash_max_listpack_entries: None,
665 hash_max_listpack_value: None,
666 list_max_listpack_size: None,
667 list_compress_depth: None,
668 set_max_intset_entries: None,
669 set_max_listpack_entries: None,
670 set_max_listpack_value: None,
671 zset_max_listpack_entries: None,
672 zset_max_listpack_value: None,
673 hll_sparse_max_bytes: None,
674 stream_node_max_bytes: None,
675 stream_node_max_entries: None,
676 stream_idmp_duration: None,
677 stream_idmp_maxsize: None,
678 loadmodule: Vec::new(),
679 hz: None,
680 io_threads: None,
681 io_threads_do_reads: None,
682 notify_keyspace_events: None,
683 slowlog_log_slower_than: None,
684 slowlog_max_len: None,
685 latency_monitor_threshold: None,
686 latency_tracking: None,
687 latency_tracking_info_percentiles: None,
688 activedefrag: None,
689 active_defrag_ignore_bytes: None,
690 active_defrag_threshold_lower: None,
691 active_defrag_threshold_upper: None,
692 active_defrag_cycle_min: None,
693 active_defrag_cycle_max: None,
694 active_defrag_max_scan_fields: None,
695 syslog_enabled: None,
696 syslog_ident: None,
697 syslog_facility: None,
698 supervised: None,
699 always_show_logo: None,
700 set_proc_title: None,
701 proc_title_template: None,
702 acl_pubsub_default: None,
703 acllog_max_len: None,
704 enable_debug_command: None,
705 enable_module_command: None,
706 enable_protected_configs: None,
707 rename_command: Vec::new(),
708 sanitize_dump_payload: None,
709 hide_user_data_from_log: None,
710 bind_source_addr: None,
711 busy_reply_threshold: None,
712 client_output_buffer_limit: Vec::new(),
713 client_query_buffer_limit: None,
714 proto_max_bulk_len: None,
715 max_new_connections_per_cycle: None,
716 max_new_tls_connections_per_cycle: None,
717 socket_mark_id: None,
718 dbfilename: None,
719 rdbcompression: None,
720 rdbchecksum: None,
721 rdb_save_incremental_fsync: None,
722 rdb_del_sync_files: None,
723 stop_writes_on_bgsave_error: None,
724 shutdown_on_sigint: None,
725 shutdown_on_sigterm: None,
726 shutdown_timeout: None,
727 activerehashing: None,
728 crash_log_enabled: None,
729 crash_memcheck_enabled: None,
730 disable_thp: None,
731 dynamic_hz: None,
732 ignore_warnings: None,
733 include: Vec::new(),
734 jemalloc_bg_thread: None,
735 locale_collate: None,
736 lua_time_limit: None,
737 oom_score_adj: None,
738 oom_score_adj_values: None,
739 propagation_error_behavior: None,
740 tracking_table_max_keys: None,
741 extra: HashMap::new(),
742 redis_server_bin: crate::stack::detect_server_bin(),
743 redis_cli_bin: "redis-cli".into(),
744 no_stack_modules: false,
745 }
746 }
747}
748
749pub struct RedisServer {
751 config: RedisServerConfig,
752}
753
754impl RedisServer {
755 pub fn new() -> Self {
757 Self {
758 config: RedisServerConfig::default(),
759 }
760 }
761
762 pub fn port(mut self, port: u16) -> Self {
766 self.config.port = port;
767 self
768 }
769
770 pub fn bind(mut self, bind: impl Into<String>) -> Self {
772 self.config.bind = bind.into();
773 self
774 }
775
776 pub fn protected_mode(mut self, protected: bool) -> Self {
778 self.config.protected_mode = protected;
779 self
780 }
781
782 pub fn tcp_backlog(mut self, backlog: u32) -> Self {
784 self.config.tcp_backlog = Some(backlog);
785 self
786 }
787
788 pub fn unixsocket(mut self, path: impl Into<PathBuf>) -> Self {
790 self.config.unixsocket = Some(path.into());
791 self
792 }
793
794 pub fn unixsocketperm(mut self, perm: u32) -> Self {
796 self.config.unixsocketperm = Some(perm);
797 self
798 }
799
800 pub fn timeout(mut self, seconds: u32) -> Self {
802 self.config.timeout = Some(seconds);
803 self
804 }
805
806 pub fn tcp_keepalive(mut self, seconds: u32) -> Self {
808 self.config.tcp_keepalive = Some(seconds);
809 self
810 }
811
812 pub fn tls_port(mut self, port: u16) -> Self {
816 self.config.tls_port = Some(port);
817 self
818 }
819
820 pub fn tls_cert_file(mut self, path: impl Into<PathBuf>) -> Self {
822 self.config.tls_cert_file = Some(path.into());
823 self
824 }
825
826 pub fn tls_key_file(mut self, path: impl Into<PathBuf>) -> Self {
828 self.config.tls_key_file = Some(path.into());
829 self
830 }
831
832 pub fn tls_ca_cert_file(mut self, path: impl Into<PathBuf>) -> Self {
834 self.config.tls_ca_cert_file = Some(path.into());
835 self
836 }
837
838 pub fn tls_auth_clients(mut self, require: bool) -> Self {
840 self.config.tls_auth_clients = Some(require);
841 self
842 }
843
844 pub fn tls_key_file_pass(mut self, pass: impl Into<String>) -> Self {
846 self.config.tls_key_file_pass = Some(pass.into());
847 self
848 }
849
850 pub fn tls_ca_cert_dir(mut self, path: impl Into<PathBuf>) -> Self {
852 self.config.tls_ca_cert_dir = Some(path.into());
853 self
854 }
855
856 pub fn tls_client_cert_file(mut self, path: impl Into<PathBuf>) -> Self {
858 self.config.tls_client_cert_file = Some(path.into());
859 self
860 }
861
862 pub fn tls_client_key_file(mut self, path: impl Into<PathBuf>) -> Self {
864 self.config.tls_client_key_file = Some(path.into());
865 self
866 }
867
868 pub fn tls_client_key_file_pass(mut self, pass: impl Into<String>) -> Self {
870 self.config.tls_client_key_file_pass = Some(pass.into());
871 self
872 }
873
874 pub fn tls_dh_params_file(mut self, path: impl Into<PathBuf>) -> Self {
876 self.config.tls_dh_params_file = Some(path.into());
877 self
878 }
879
880 pub fn tls_ciphers(mut self, ciphers: impl Into<String>) -> Self {
882 self.config.tls_ciphers = Some(ciphers.into());
883 self
884 }
885
886 pub fn tls_ciphersuites(mut self, suites: impl Into<String>) -> Self {
888 self.config.tls_ciphersuites = Some(suites.into());
889 self
890 }
891
892 pub fn tls_protocols(mut self, protocols: impl Into<String>) -> Self {
894 self.config.tls_protocols = Some(protocols.into());
895 self
896 }
897
898 pub fn tls_prefer_server_ciphers(mut self, prefer: bool) -> Self {
900 self.config.tls_prefer_server_ciphers = Some(prefer);
901 self
902 }
903
904 pub fn tls_session_caching(mut self, enable: bool) -> Self {
906 self.config.tls_session_caching = Some(enable);
907 self
908 }
909
910 pub fn tls_session_cache_size(mut self, size: u32) -> Self {
912 self.config.tls_session_cache_size = Some(size);
913 self
914 }
915
916 pub fn tls_session_cache_timeout(mut self, seconds: u32) -> Self {
918 self.config.tls_session_cache_timeout = Some(seconds);
919 self
920 }
921
922 pub fn tls_replication(mut self, enable: bool) -> Self {
924 self.config.tls_replication = Some(enable);
925 self
926 }
927
928 pub fn tls_cluster(mut self, enable: bool) -> Self {
930 self.config.tls_cluster = Some(enable);
931 self
932 }
933
934 pub fn dir(mut self, dir: impl Into<PathBuf>) -> Self {
938 self.config.dir = dir.into();
939 self
940 }
941
942 pub fn loglevel(mut self, level: LogLevel) -> Self {
944 self.config.loglevel = level;
945 self
946 }
947
948 pub fn logfile(mut self, path: impl Into<String>) -> Self {
950 self.config.logfile = Some(path.into());
951 self
952 }
953
954 pub fn databases(mut self, n: u32) -> Self {
956 self.config.databases = Some(n);
957 self
958 }
959
960 pub fn maxmemory(mut self, limit: impl Into<String>) -> Self {
964 self.config.maxmemory = Some(limit.into());
965 self
966 }
967
968 pub fn maxmemory_policy(mut self, policy: impl Into<String>) -> Self {
970 self.config.maxmemory_policy = Some(policy.into());
971 self
972 }
973
974 pub fn maxmemory_samples(mut self, n: u32) -> Self {
976 self.config.maxmemory_samples = Some(n);
977 self
978 }
979
980 pub fn maxmemory_clients(mut self, limit: impl Into<String>) -> Self {
982 self.config.maxmemory_clients = Some(limit.into());
983 self
984 }
985
986 pub fn maxmemory_eviction_tenacity(mut self, tenacity: u32) -> Self {
988 self.config.maxmemory_eviction_tenacity = Some(tenacity);
989 self
990 }
991
992 pub fn maxclients(mut self, n: u32) -> Self {
994 self.config.maxclients = Some(n);
995 self
996 }
997
998 pub fn lfu_log_factor(mut self, factor: u32) -> Self {
1000 self.config.lfu_log_factor = Some(factor);
1001 self
1002 }
1003
1004 pub fn lfu_decay_time(mut self, minutes: u32) -> Self {
1006 self.config.lfu_decay_time = Some(minutes);
1007 self
1008 }
1009
1010 pub fn active_expire_effort(mut self, effort: u32) -> Self {
1012 self.config.active_expire_effort = Some(effort);
1013 self
1014 }
1015
1016 pub fn lazyfree_lazy_eviction(mut self, enable: bool) -> Self {
1020 self.config.lazyfree_lazy_eviction = Some(enable);
1021 self
1022 }
1023
1024 pub fn lazyfree_lazy_expire(mut self, enable: bool) -> Self {
1026 self.config.lazyfree_lazy_expire = Some(enable);
1027 self
1028 }
1029
1030 pub fn lazyfree_lazy_server_del(mut self, enable: bool) -> Self {
1032 self.config.lazyfree_lazy_server_del = Some(enable);
1033 self
1034 }
1035
1036 pub fn lazyfree_lazy_user_del(mut self, enable: bool) -> Self {
1038 self.config.lazyfree_lazy_user_del = Some(enable);
1039 self
1040 }
1041
1042 pub fn lazyfree_lazy_user_flush(mut self, enable: bool) -> Self {
1044 self.config.lazyfree_lazy_user_flush = Some(enable);
1045 self
1046 }
1047
1048 pub fn save(mut self, save: bool) -> Self {
1055 self.config.save = if save {
1056 SavePolicy::Default
1057 } else {
1058 SavePolicy::Disabled
1059 };
1060 self
1061 }
1062
1063 pub fn save_schedule(mut self, schedule: Vec<(u64, u64)>) -> Self {
1067 self.config.save = SavePolicy::Custom(schedule);
1068 self
1069 }
1070
1071 pub fn appendonly(mut self, appendonly: bool) -> Self {
1073 self.config.appendonly = appendonly;
1074 self
1075 }
1076
1077 pub fn appendfsync(mut self, policy: AppendFsync) -> Self {
1079 self.config.appendfsync = Some(policy);
1080 self
1081 }
1082
1083 pub fn appendfilename(mut self, name: impl Into<String>) -> Self {
1085 self.config.appendfilename = Some(name.into());
1086 self
1087 }
1088
1089 pub fn appenddirname(mut self, name: impl Into<PathBuf>) -> Self {
1091 self.config.appenddirname = Some(name.into());
1092 self
1093 }
1094
1095 pub fn aof_use_rdb_preamble(mut self, enable: bool) -> Self {
1097 self.config.aof_use_rdb_preamble = Some(enable);
1098 self
1099 }
1100
1101 pub fn aof_load_truncated(mut self, enable: bool) -> Self {
1103 self.config.aof_load_truncated = Some(enable);
1104 self
1105 }
1106
1107 pub fn aof_load_corrupt_tail_max_size(mut self, size: impl Into<String>) -> Self {
1109 self.config.aof_load_corrupt_tail_max_size = Some(size.into());
1110 self
1111 }
1112
1113 pub fn aof_rewrite_incremental_fsync(mut self, enable: bool) -> Self {
1115 self.config.aof_rewrite_incremental_fsync = Some(enable);
1116 self
1117 }
1118
1119 pub fn aof_timestamp_enabled(mut self, enable: bool) -> Self {
1121 self.config.aof_timestamp_enabled = Some(enable);
1122 self
1123 }
1124
1125 pub fn auto_aof_rewrite_percentage(mut self, pct: u32) -> Self {
1127 self.config.auto_aof_rewrite_percentage = Some(pct);
1128 self
1129 }
1130
1131 pub fn auto_aof_rewrite_min_size(mut self, size: impl Into<String>) -> Self {
1133 self.config.auto_aof_rewrite_min_size = Some(size.into());
1134 self
1135 }
1136
1137 pub fn no_appendfsync_on_rewrite(mut self, enable: bool) -> Self {
1139 self.config.no_appendfsync_on_rewrite = Some(enable);
1140 self
1141 }
1142
1143 pub fn replicaof(mut self, host: impl Into<String>, port: u16) -> Self {
1147 self.config.replicaof = Some((host.into(), port));
1148 self
1149 }
1150
1151 pub fn masterauth(mut self, password: impl Into<String>) -> Self {
1153 self.config.masterauth = Some(password.into());
1154 self
1155 }
1156
1157 pub fn masteruser(mut self, user: impl Into<String>) -> Self {
1159 self.config.masteruser = Some(user.into());
1160 self
1161 }
1162
1163 pub fn repl_backlog_size(mut self, size: impl Into<String>) -> Self {
1165 self.config.repl_backlog_size = Some(size.into());
1166 self
1167 }
1168
1169 pub fn repl_backlog_ttl(mut self, seconds: u32) -> Self {
1171 self.config.repl_backlog_ttl = Some(seconds);
1172 self
1173 }
1174
1175 pub fn repl_disable_tcp_nodelay(mut self, disable: bool) -> Self {
1177 self.config.repl_disable_tcp_nodelay = Some(disable);
1178 self
1179 }
1180
1181 pub fn repl_diskless_load(mut self, policy: ReplDisklessLoad) -> Self {
1183 self.config.repl_diskless_load = Some(policy);
1184 self
1185 }
1186
1187 pub fn repl_diskless_sync(mut self, enable: bool) -> Self {
1189 self.config.repl_diskless_sync = Some(enable);
1190 self
1191 }
1192
1193 pub fn repl_diskless_sync_delay(mut self, seconds: u32) -> Self {
1195 self.config.repl_diskless_sync_delay = Some(seconds);
1196 self
1197 }
1198
1199 pub fn repl_diskless_sync_max_replicas(mut self, n: u32) -> Self {
1201 self.config.repl_diskless_sync_max_replicas = Some(n);
1202 self
1203 }
1204
1205 pub fn repl_ping_replica_period(mut self, seconds: u32) -> Self {
1207 self.config.repl_ping_replica_period = Some(seconds);
1208 self
1209 }
1210
1211 pub fn repl_timeout(mut self, seconds: u32) -> Self {
1213 self.config.repl_timeout = Some(seconds);
1214 self
1215 }
1216
1217 pub fn replica_announce_ip(mut self, ip: impl Into<String>) -> Self {
1219 self.config.replica_announce_ip = Some(ip.into());
1220 self
1221 }
1222
1223 pub fn replica_announce_port(mut self, port: u16) -> Self {
1225 self.config.replica_announce_port = Some(port);
1226 self
1227 }
1228
1229 pub fn replica_announced(mut self, announced: bool) -> Self {
1231 self.config.replica_announced = Some(announced);
1232 self
1233 }
1234
1235 pub fn replica_full_sync_buffer_limit(mut self, size: impl Into<String>) -> Self {
1237 self.config.replica_full_sync_buffer_limit = Some(size.into());
1238 self
1239 }
1240
1241 pub fn replica_ignore_disk_write_errors(mut self, ignore: bool) -> Self {
1243 self.config.replica_ignore_disk_write_errors = Some(ignore);
1244 self
1245 }
1246
1247 pub fn replica_ignore_maxmemory(mut self, ignore: bool) -> Self {
1249 self.config.replica_ignore_maxmemory = Some(ignore);
1250 self
1251 }
1252
1253 pub fn replica_lazy_flush(mut self, enable: bool) -> Self {
1255 self.config.replica_lazy_flush = Some(enable);
1256 self
1257 }
1258
1259 pub fn replica_priority(mut self, priority: u32) -> Self {
1261 self.config.replica_priority = Some(priority);
1262 self
1263 }
1264
1265 pub fn replica_read_only(mut self, read_only: bool) -> Self {
1267 self.config.replica_read_only = Some(read_only);
1268 self
1269 }
1270
1271 pub fn replica_serve_stale_data(mut self, serve: bool) -> Self {
1273 self.config.replica_serve_stale_data = Some(serve);
1274 self
1275 }
1276
1277 pub fn min_replicas_to_write(mut self, n: u32) -> Self {
1279 self.config.min_replicas_to_write = Some(n);
1280 self
1281 }
1282
1283 pub fn min_replicas_max_lag(mut self, seconds: u32) -> Self {
1285 self.config.min_replicas_max_lag = Some(seconds);
1286 self
1287 }
1288
1289 pub fn password(mut self, password: impl Into<String>) -> Self {
1293 self.config.password = Some(password.into());
1294 self
1295 }
1296
1297 pub fn acl_file(mut self, path: impl Into<PathBuf>) -> Self {
1299 self.config.acl_file = Some(path.into());
1300 self
1301 }
1302
1303 pub fn cluster_enabled(mut self, enabled: bool) -> Self {
1307 self.config.cluster_enabled = enabled;
1308 self
1309 }
1310
1311 pub fn cluster_node_timeout(mut self, ms: u64) -> Self {
1313 self.config.cluster_node_timeout = Some(ms);
1314 self
1315 }
1316
1317 pub fn cluster_config_file(mut self, path: impl Into<PathBuf>) -> Self {
1319 self.config.cluster_config_file = Some(path.into());
1320 self
1321 }
1322
1323 pub fn cluster_require_full_coverage(mut self, require: bool) -> Self {
1325 self.config.cluster_require_full_coverage = Some(require);
1326 self
1327 }
1328
1329 pub fn cluster_allow_reads_when_down(mut self, allow: bool) -> Self {
1331 self.config.cluster_allow_reads_when_down = Some(allow);
1332 self
1333 }
1334
1335 pub fn cluster_allow_pubsubshard_when_down(mut self, allow: bool) -> Self {
1337 self.config.cluster_allow_pubsubshard_when_down = Some(allow);
1338 self
1339 }
1340
1341 pub fn cluster_allow_replica_migration(mut self, allow: bool) -> Self {
1343 self.config.cluster_allow_replica_migration = Some(allow);
1344 self
1345 }
1346
1347 pub fn cluster_migration_barrier(mut self, barrier: u32) -> Self {
1349 self.config.cluster_migration_barrier = Some(barrier);
1350 self
1351 }
1352
1353 pub fn cluster_replica_no_failover(mut self, no_failover: bool) -> Self {
1355 self.config.cluster_replica_no_failover = Some(no_failover);
1356 self
1357 }
1358
1359 pub fn cluster_replica_validity_factor(mut self, factor: u32) -> Self {
1361 self.config.cluster_replica_validity_factor = Some(factor);
1362 self
1363 }
1364
1365 pub fn cluster_announce_ip(mut self, ip: impl Into<String>) -> Self {
1367 self.config.cluster_announce_ip = Some(ip.into());
1368 self
1369 }
1370
1371 pub fn cluster_announce_port(mut self, port: u16) -> Self {
1373 self.config.cluster_announce_port = Some(port);
1374 self
1375 }
1376
1377 pub fn cluster_announce_bus_port(mut self, port: u16) -> Self {
1379 self.config.cluster_announce_bus_port = Some(port);
1380 self
1381 }
1382
1383 pub fn cluster_announce_tls_port(mut self, port: u16) -> Self {
1385 self.config.cluster_announce_tls_port = Some(port);
1386 self
1387 }
1388
1389 pub fn cluster_announce_hostname(mut self, hostname: impl Into<String>) -> Self {
1391 self.config.cluster_announce_hostname = Some(hostname.into());
1392 self
1393 }
1394
1395 pub fn cluster_announce_human_nodename(mut self, name: impl Into<String>) -> Self {
1397 self.config.cluster_announce_human_nodename = Some(name.into());
1398 self
1399 }
1400
1401 pub fn cluster_port(mut self, port: u16) -> Self {
1403 self.config.cluster_port = Some(port);
1404 self
1405 }
1406
1407 pub fn cluster_preferred_endpoint_type(mut self, endpoint_type: impl Into<String>) -> Self {
1409 self.config.cluster_preferred_endpoint_type = Some(endpoint_type.into());
1410 self
1411 }
1412
1413 pub fn cluster_link_sendbuf_limit(mut self, limit: u64) -> Self {
1415 self.config.cluster_link_sendbuf_limit = Some(limit);
1416 self
1417 }
1418
1419 pub fn cluster_compatibility_sample_ratio(mut self, ratio: u32) -> Self {
1421 self.config.cluster_compatibility_sample_ratio = Some(ratio);
1422 self
1423 }
1424
1425 pub fn cluster_slot_migration_handoff_max_lag_bytes(mut self, bytes: u64) -> Self {
1427 self.config.cluster_slot_migration_handoff_max_lag_bytes = Some(bytes);
1428 self
1429 }
1430
1431 pub fn cluster_slot_migration_write_pause_timeout(mut self, ms: u64) -> Self {
1433 self.config.cluster_slot_migration_write_pause_timeout = Some(ms);
1434 self
1435 }
1436
1437 pub fn cluster_slot_stats_enabled(mut self, enable: bool) -> Self {
1439 self.config.cluster_slot_stats_enabled = Some(enable);
1440 self
1441 }
1442
1443 pub fn hash_max_listpack_entries(mut self, n: u32) -> Self {
1447 self.config.hash_max_listpack_entries = Some(n);
1448 self
1449 }
1450
1451 pub fn hash_max_listpack_value(mut self, n: u32) -> Self {
1453 self.config.hash_max_listpack_value = Some(n);
1454 self
1455 }
1456
1457 pub fn list_max_listpack_size(mut self, n: i32) -> Self {
1462 self.config.list_max_listpack_size = Some(n);
1463 self
1464 }
1465
1466 pub fn list_compress_depth(mut self, n: u32) -> Self {
1470 self.config.list_compress_depth = Some(n);
1471 self
1472 }
1473
1474 pub fn set_max_intset_entries(mut self, n: u32) -> Self {
1476 self.config.set_max_intset_entries = Some(n);
1477 self
1478 }
1479
1480 pub fn set_max_listpack_entries(mut self, n: u32) -> Self {
1482 self.config.set_max_listpack_entries = Some(n);
1483 self
1484 }
1485
1486 pub fn set_max_listpack_value(mut self, n: u32) -> Self {
1488 self.config.set_max_listpack_value = Some(n);
1489 self
1490 }
1491
1492 pub fn zset_max_listpack_entries(mut self, n: u32) -> Self {
1494 self.config.zset_max_listpack_entries = Some(n);
1495 self
1496 }
1497
1498 pub fn zset_max_listpack_value(mut self, n: u32) -> Self {
1500 self.config.zset_max_listpack_value = Some(n);
1501 self
1502 }
1503
1504 pub fn hll_sparse_max_bytes(mut self, n: u32) -> Self {
1506 self.config.hll_sparse_max_bytes = Some(n);
1507 self
1508 }
1509
1510 pub fn stream_node_max_bytes(mut self, n: u32) -> Self {
1512 self.config.stream_node_max_bytes = Some(n);
1513 self
1514 }
1515
1516 pub fn stream_node_max_entries(mut self, n: u32) -> Self {
1518 self.config.stream_node_max_entries = Some(n);
1519 self
1520 }
1521
1522 pub fn stream_idmp_duration(mut self, ms: u64) -> Self {
1524 self.config.stream_idmp_duration = Some(ms);
1525 self
1526 }
1527
1528 pub fn stream_idmp_maxsize(mut self, n: u64) -> Self {
1530 self.config.stream_idmp_maxsize = Some(n);
1531 self
1532 }
1533
1534 pub fn loadmodule(mut self, path: impl Into<PathBuf>) -> Self {
1538 self.config.loadmodule.push((path.into(), Vec::new()));
1539 self
1540 }
1541
1542 pub fn loadmodule_with_args(
1548 mut self,
1549 path: impl Into<PathBuf>,
1550 args: impl IntoIterator<Item = impl Into<String>>,
1551 ) -> Self {
1552 self.config
1553 .loadmodule
1554 .push((path.into(), args.into_iter().map(Into::into).collect()));
1555 self
1556 }
1557
1558 pub fn hz(mut self, hz: u32) -> Self {
1562 self.config.hz = Some(hz);
1563 self
1564 }
1565
1566 pub fn io_threads(mut self, n: u32) -> Self {
1568 self.config.io_threads = Some(n);
1569 self
1570 }
1571
1572 pub fn io_threads_do_reads(mut self, enable: bool) -> Self {
1574 self.config.io_threads_do_reads = Some(enable);
1575 self
1576 }
1577
1578 pub fn notify_keyspace_events(mut self, events: impl Into<String>) -> Self {
1580 self.config.notify_keyspace_events = Some(events.into());
1581 self
1582 }
1583
1584 pub fn slowlog_log_slower_than(mut self, us: i64) -> Self {
1588 self.config.slowlog_log_slower_than = Some(us);
1589 self
1590 }
1591
1592 pub fn slowlog_max_len(mut self, n: u32) -> Self {
1594 self.config.slowlog_max_len = Some(n);
1595 self
1596 }
1597
1598 pub fn latency_monitor_threshold(mut self, ms: u64) -> Self {
1602 self.config.latency_monitor_threshold = Some(ms);
1603 self
1604 }
1605
1606 pub fn latency_tracking(mut self, enable: bool) -> Self {
1608 self.config.latency_tracking = Some(enable);
1609 self
1610 }
1611
1612 pub fn latency_tracking_info_percentiles(mut self, percentiles: impl Into<String>) -> Self {
1614 self.config.latency_tracking_info_percentiles = Some(percentiles.into());
1615 self
1616 }
1617
1618 pub fn activedefrag(mut self, enable: bool) -> Self {
1622 self.config.activedefrag = Some(enable);
1623 self
1624 }
1625
1626 pub fn active_defrag_ignore_bytes(mut self, bytes: impl Into<String>) -> Self {
1628 self.config.active_defrag_ignore_bytes = Some(bytes.into());
1629 self
1630 }
1631
1632 pub fn active_defrag_threshold_lower(mut self, pct: u32) -> Self {
1634 self.config.active_defrag_threshold_lower = Some(pct);
1635 self
1636 }
1637
1638 pub fn active_defrag_threshold_upper(mut self, pct: u32) -> Self {
1640 self.config.active_defrag_threshold_upper = Some(pct);
1641 self
1642 }
1643
1644 pub fn active_defrag_cycle_min(mut self, pct: u32) -> Self {
1646 self.config.active_defrag_cycle_min = Some(pct);
1647 self
1648 }
1649
1650 pub fn active_defrag_cycle_max(mut self, pct: u32) -> Self {
1652 self.config.active_defrag_cycle_max = Some(pct);
1653 self
1654 }
1655
1656 pub fn active_defrag_max_scan_fields(mut self, n: u32) -> Self {
1658 self.config.active_defrag_max_scan_fields = Some(n);
1659 self
1660 }
1661
1662 pub fn syslog_enabled(mut self, enable: bool) -> Self {
1666 self.config.syslog_enabled = Some(enable);
1667 self
1668 }
1669
1670 pub fn syslog_ident(mut self, ident: impl Into<String>) -> Self {
1672 self.config.syslog_ident = Some(ident.into());
1673 self
1674 }
1675
1676 pub fn syslog_facility(mut self, facility: impl Into<String>) -> Self {
1678 self.config.syslog_facility = Some(facility.into());
1679 self
1680 }
1681
1682 pub fn supervised(mut self, mode: impl Into<String>) -> Self {
1684 self.config.supervised = Some(mode.into());
1685 self
1686 }
1687
1688 pub fn always_show_logo(mut self, enable: bool) -> Self {
1690 self.config.always_show_logo = Some(enable);
1691 self
1692 }
1693
1694 pub fn set_proc_title(mut self, enable: bool) -> Self {
1696 self.config.set_proc_title = Some(enable);
1697 self
1698 }
1699
1700 pub fn proc_title_template(mut self, template: impl Into<String>) -> Self {
1702 self.config.proc_title_template = Some(template.into());
1703 self
1704 }
1705
1706 pub fn acl_pubsub_default(mut self, default: impl Into<String>) -> Self {
1710 self.config.acl_pubsub_default = Some(default.into());
1711 self
1712 }
1713
1714 pub fn acllog_max_len(mut self, n: u32) -> Self {
1716 self.config.acllog_max_len = Some(n);
1717 self
1718 }
1719
1720 pub fn enable_debug_command(mut self, mode: impl Into<String>) -> Self {
1722 self.config.enable_debug_command = Some(mode.into());
1723 self
1724 }
1725
1726 pub fn enable_module_command(mut self, mode: impl Into<String>) -> Self {
1728 self.config.enable_module_command = Some(mode.into());
1729 self
1730 }
1731
1732 pub fn enable_protected_configs(mut self, mode: impl Into<String>) -> Self {
1734 self.config.enable_protected_configs = Some(mode.into());
1735 self
1736 }
1737
1738 pub fn rename_command(
1740 mut self,
1741 command: impl Into<String>,
1742 new_name: impl Into<String>,
1743 ) -> Self {
1744 self.config
1745 .rename_command
1746 .push((command.into(), new_name.into()));
1747 self
1748 }
1749
1750 pub fn sanitize_dump_payload(mut self, mode: impl Into<String>) -> Self {
1752 self.config.sanitize_dump_payload = Some(mode.into());
1753 self
1754 }
1755
1756 pub fn hide_user_data_from_log(mut self, enable: bool) -> Self {
1758 self.config.hide_user_data_from_log = Some(enable);
1759 self
1760 }
1761
1762 pub fn bind_source_addr(mut self, addr: impl Into<String>) -> Self {
1766 self.config.bind_source_addr = Some(addr.into());
1767 self
1768 }
1769
1770 pub fn busy_reply_threshold(mut self, ms: u64) -> Self {
1772 self.config.busy_reply_threshold = Some(ms);
1773 self
1774 }
1775
1776 pub fn client_output_buffer_limit(mut self, limit: impl Into<String>) -> Self {
1778 self.config.client_output_buffer_limit.push(limit.into());
1779 self
1780 }
1781
1782 pub fn client_query_buffer_limit(mut self, limit: impl Into<String>) -> Self {
1784 self.config.client_query_buffer_limit = Some(limit.into());
1785 self
1786 }
1787
1788 pub fn proto_max_bulk_len(mut self, len: impl Into<String>) -> Self {
1790 self.config.proto_max_bulk_len = Some(len.into());
1791 self
1792 }
1793
1794 pub fn max_new_connections_per_cycle(mut self, n: u32) -> Self {
1796 self.config.max_new_connections_per_cycle = Some(n);
1797 self
1798 }
1799
1800 pub fn max_new_tls_connections_per_cycle(mut self, n: u32) -> Self {
1802 self.config.max_new_tls_connections_per_cycle = Some(n);
1803 self
1804 }
1805
1806 pub fn socket_mark_id(mut self, id: u32) -> Self {
1808 self.config.socket_mark_id = Some(id);
1809 self
1810 }
1811
1812 pub fn dbfilename(mut self, name: impl Into<String>) -> Self {
1816 self.config.dbfilename = Some(name.into());
1817 self
1818 }
1819
1820 pub fn rdbcompression(mut self, enable: bool) -> Self {
1822 self.config.rdbcompression = Some(enable);
1823 self
1824 }
1825
1826 pub fn rdbchecksum(mut self, enable: bool) -> Self {
1828 self.config.rdbchecksum = Some(enable);
1829 self
1830 }
1831
1832 pub fn rdb_save_incremental_fsync(mut self, enable: bool) -> Self {
1834 self.config.rdb_save_incremental_fsync = Some(enable);
1835 self
1836 }
1837
1838 pub fn rdb_del_sync_files(mut self, enable: bool) -> Self {
1840 self.config.rdb_del_sync_files = Some(enable);
1841 self
1842 }
1843
1844 pub fn stop_writes_on_bgsave_error(mut self, enable: bool) -> Self {
1846 self.config.stop_writes_on_bgsave_error = Some(enable);
1847 self
1848 }
1849
1850 pub fn shutdown_on_sigint(mut self, behavior: impl Into<String>) -> Self {
1854 self.config.shutdown_on_sigint = Some(behavior.into());
1855 self
1856 }
1857
1858 pub fn shutdown_on_sigterm(mut self, behavior: impl Into<String>) -> Self {
1860 self.config.shutdown_on_sigterm = Some(behavior.into());
1861 self
1862 }
1863
1864 pub fn shutdown_timeout(mut self, seconds: u32) -> Self {
1866 self.config.shutdown_timeout = Some(seconds);
1867 self
1868 }
1869
1870 pub fn activerehashing(mut self, enable: bool) -> Self {
1874 self.config.activerehashing = Some(enable);
1875 self
1876 }
1877
1878 pub fn crash_log_enabled(mut self, enable: bool) -> Self {
1880 self.config.crash_log_enabled = Some(enable);
1881 self
1882 }
1883
1884 pub fn crash_memcheck_enabled(mut self, enable: bool) -> Self {
1886 self.config.crash_memcheck_enabled = Some(enable);
1887 self
1888 }
1889
1890 pub fn disable_thp(mut self, enable: bool) -> Self {
1892 self.config.disable_thp = Some(enable);
1893 self
1894 }
1895
1896 pub fn dynamic_hz(mut self, enable: bool) -> Self {
1898 self.config.dynamic_hz = Some(enable);
1899 self
1900 }
1901
1902 pub fn ignore_warnings(mut self, warning: impl Into<String>) -> Self {
1904 self.config.ignore_warnings = Some(warning.into());
1905 self
1906 }
1907
1908 pub fn include(mut self, path: impl Into<PathBuf>) -> Self {
1910 self.config.include.push(path.into());
1911 self
1912 }
1913
1914 pub fn jemalloc_bg_thread(mut self, enable: bool) -> Self {
1916 self.config.jemalloc_bg_thread = Some(enable);
1917 self
1918 }
1919
1920 pub fn locale_collate(mut self, locale: impl Into<String>) -> Self {
1922 self.config.locale_collate = Some(locale.into());
1923 self
1924 }
1925
1926 pub fn lua_time_limit(mut self, ms: u64) -> Self {
1928 self.config.lua_time_limit = Some(ms);
1929 self
1930 }
1931
1932 pub fn oom_score_adj(mut self, mode: impl Into<String>) -> Self {
1934 self.config.oom_score_adj = Some(mode.into());
1935 self
1936 }
1937
1938 pub fn oom_score_adj_values(mut self, values: impl Into<String>) -> Self {
1940 self.config.oom_score_adj_values = Some(values.into());
1941 self
1942 }
1943
1944 pub fn propagation_error_behavior(mut self, behavior: impl Into<String>) -> Self {
1946 self.config.propagation_error_behavior = Some(behavior.into());
1947 self
1948 }
1949
1950 pub fn tracking_table_max_keys(mut self, n: u64) -> Self {
1952 self.config.tracking_table_max_keys = Some(n);
1953 self
1954 }
1955
1956 pub fn redis_server_bin(mut self, bin: impl Into<String>) -> Self {
1960 self.config.redis_server_bin = bin.into();
1961 self
1962 }
1963
1964 pub fn redis_cli_bin(mut self, bin: impl Into<String>) -> Self {
1966 self.config.redis_cli_bin = bin.into();
1967 self
1968 }
1969
1970 pub fn no_stack_modules(mut self) -> Self {
1976 self.config.no_stack_modules = true;
1977 self
1978 }
1979
1980 pub fn extra(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1982 self.config.extra.insert(key.into(), value.into());
1983 self
1984 }
1985
1986 pub async fn start(self) -> Result<RedisServerHandle> {
1991 if which::which(&self.config.redis_server_bin).is_err() {
1992 return Err(Error::BinaryNotFound {
1993 binary: self.config.redis_server_bin.clone(),
1994 });
1995 }
1996 if which::which(&self.config.redis_cli_bin).is_err() {
1997 return Err(Error::BinaryNotFound {
1998 binary: self.config.redis_cli_bin.clone(),
1999 });
2000 }
2001
2002 let node_dir = self.config.dir.join(format!("node-{}", self.config.port));
2003
2004 let stale_pidfile = node_dir.join("redis.pid");
2008 if let Some(stale_pid) = crate::process::read_pidfile(&stale_pidfile)
2009 && crate::process::pid_alive(stale_pid)
2010 {
2011 crate::process::force_kill(stale_pid);
2012 }
2013
2014 fs::create_dir_all(&node_dir)?;
2015
2016 let conf_path = node_dir.join("redis.conf");
2017 let conf_content = self.generate_config(&node_dir);
2018 fs::write(&conf_path, conf_content)?;
2019
2020 let module_args = if self.config.no_stack_modules {
2021 Vec::new()
2022 } else {
2023 crate::stack::detect_stack_modules(&self.config.redis_server_bin)
2024 };
2025 let status = Command::new(&self.config.redis_server_bin)
2026 .arg(&conf_path)
2027 .args(&module_args)
2028 .stdout(std::process::Stdio::null())
2029 .stderr(std::process::Stdio::null())
2030 .status()
2031 .await?;
2032
2033 if !status.success() {
2034 return Err(Error::ServerStart {
2035 port: self.config.port,
2036 });
2037 }
2038
2039 let tls_only = self.config.port == 0;
2044 let admin_port = if tls_only {
2045 self.config.tls_port.unwrap_or(self.config.port)
2046 } else {
2047 self.config.port
2048 };
2049 let mut cli = RedisCli::new()
2050 .bin(&self.config.redis_cli_bin)
2051 .host(&self.config.bind)
2052 .port(admin_port);
2053 if let Some(ref pw) = self.config.password {
2054 cli = cli.password(pw);
2055 }
2056 if tls_only && self.config.tls_cert_file.is_some() && self.config.tls_key_file.is_some() {
2057 cli = cli.tls(true);
2058 if let Some(ref ca) = self.config.tls_ca_cert_file {
2059 cli = cli.cacert(ca);
2060 } else {
2061 cli = cli.insecure(true);
2062 }
2063 if let Some(ref cert) = self.config.tls_cert_file {
2064 cli = cli.cert(cert);
2065 }
2066 if let Some(ref key) = self.config.tls_key_file {
2067 cli = cli.key(key);
2068 }
2069 }
2070
2071 cli.wait_for_ready(Duration::from_secs(10)).await?;
2072
2073 let pid_path = node_dir.join("redis.pid");
2074 let pid: u32 = {
2075 let deadline = std::time::Instant::now() + std::time::Duration::from_secs(1);
2076 loop {
2077 if let Ok(s) = fs::read_to_string(&pid_path)
2078 && let Ok(p) = s.trim().parse::<u32>()
2079 {
2080 break p;
2081 }
2082 if std::time::Instant::now() >= deadline {
2083 return Err(Error::ServerStart {
2084 port: self.config.port,
2085 });
2086 }
2087 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
2088 }
2089 };
2090
2091 Ok(RedisServerHandle {
2092 config: self.config,
2093 cli,
2094 pid,
2095 detached: false,
2096 })
2097 }
2098
2099 fn generate_config(&self, node_dir: &std::path::Path) -> String {
2100 let yn = |b: bool| if b { "yes" } else { "no" };
2101
2102 let mut conf = format!(
2103 "port {port}\n\
2104 bind {bind}\n\
2105 daemonize {daemonize}\n\
2106 pidfile \"{dir}/redis.pid\"\n\
2107 dir \"{dir}\"\n\
2108 loglevel {level}\n\
2109 protected-mode {protected}\n",
2110 port = self.config.port,
2111 bind = self.config.bind,
2112 daemonize = yn(self.config.daemonize),
2113 dir = node_dir.display(),
2114 level = self.config.loglevel,
2115 protected = yn(self.config.protected_mode),
2116 );
2117
2118 let logfile = self
2119 .config
2120 .logfile
2121 .as_deref()
2122 .map(str::to_owned)
2123 .unwrap_or_else(|| format!("{}/redis.log", node_dir.display()));
2124 conf.push_str(&format!("logfile \"{logfile}\"\n"));
2125
2126 if let Some(backlog) = self.config.tcp_backlog {
2128 conf.push_str(&format!("tcp-backlog {backlog}\n"));
2129 }
2130 if let Some(ref path) = self.config.unixsocket {
2131 conf.push_str(&format!("unixsocket \"{}\"\n", path.display()));
2132 }
2133 if let Some(perm) = self.config.unixsocketperm {
2134 conf.push_str(&format!("unixsocketperm {perm}\n"));
2135 }
2136 if let Some(t) = self.config.timeout {
2137 conf.push_str(&format!("timeout {t}\n"));
2138 }
2139 if let Some(ka) = self.config.tcp_keepalive {
2140 conf.push_str(&format!("tcp-keepalive {ka}\n"));
2141 }
2142
2143 if let Some(port) = self.config.tls_port {
2145 conf.push_str(&format!("tls-port {port}\n"));
2146 }
2147 if let Some(ref path) = self.config.tls_cert_file {
2148 conf.push_str(&format!("tls-cert-file \"{}\"\n", path.display()));
2149 }
2150 if let Some(ref path) = self.config.tls_key_file {
2151 conf.push_str(&format!("tls-key-file \"{}\"\n", path.display()));
2152 }
2153 if let Some(ref pass) = self.config.tls_key_file_pass {
2154 conf.push_str(&format!("tls-key-file-pass {pass}\n"));
2155 }
2156 if let Some(ref path) = self.config.tls_ca_cert_file {
2157 conf.push_str(&format!("tls-ca-cert-file \"{}\"\n", path.display()));
2158 }
2159 if let Some(ref path) = self.config.tls_ca_cert_dir {
2160 conf.push_str(&format!("tls-ca-cert-dir \"{}\"\n", path.display()));
2161 }
2162 if let Some(auth) = self.config.tls_auth_clients {
2163 conf.push_str(&format!("tls-auth-clients {}\n", yn(auth)));
2164 }
2165 if let Some(ref path) = self.config.tls_client_cert_file {
2166 conf.push_str(&format!("tls-client-cert-file \"{}\"\n", path.display()));
2167 }
2168 if let Some(ref path) = self.config.tls_client_key_file {
2169 conf.push_str(&format!("tls-client-key-file \"{}\"\n", path.display()));
2170 }
2171 if let Some(ref pass) = self.config.tls_client_key_file_pass {
2172 conf.push_str(&format!("tls-client-key-file-pass {pass}\n"));
2173 }
2174 if let Some(ref path) = self.config.tls_dh_params_file {
2175 conf.push_str(&format!("tls-dh-params-file \"{}\"\n", path.display()));
2176 }
2177 if let Some(ref ciphers) = self.config.tls_ciphers {
2178 conf.push_str(&format!("tls-ciphers {ciphers}\n"));
2179 }
2180 if let Some(ref suites) = self.config.tls_ciphersuites {
2181 conf.push_str(&format!("tls-ciphersuites {suites}\n"));
2182 }
2183 if let Some(ref protocols) = self.config.tls_protocols {
2184 conf.push_str(&format!("tls-protocols {protocols}\n"));
2185 }
2186 if let Some(v) = self.config.tls_prefer_server_ciphers {
2187 conf.push_str(&format!("tls-prefer-server-ciphers {}\n", yn(v)));
2188 }
2189 if let Some(v) = self.config.tls_session_caching {
2190 conf.push_str(&format!("tls-session-caching {}\n", yn(v)));
2191 }
2192 if let Some(size) = self.config.tls_session_cache_size {
2193 conf.push_str(&format!("tls-session-cache-size {size}\n"));
2194 }
2195 if let Some(timeout) = self.config.tls_session_cache_timeout {
2196 conf.push_str(&format!("tls-session-cache-timeout {timeout}\n"));
2197 }
2198 if let Some(v) = self.config.tls_replication {
2199 conf.push_str(&format!("tls-replication {}\n", yn(v)));
2200 }
2201 if let Some(v) = self.config.tls_cluster {
2202 conf.push_str(&format!("tls-cluster {}\n", yn(v)));
2203 }
2204
2205 if let Some(n) = self.config.databases {
2207 conf.push_str(&format!("databases {n}\n"));
2208 }
2209
2210 if let Some(ref limit) = self.config.maxmemory {
2212 conf.push_str(&format!("maxmemory {limit}\n"));
2213 }
2214 if let Some(ref policy) = self.config.maxmemory_policy {
2215 conf.push_str(&format!("maxmemory-policy {policy}\n"));
2216 }
2217 if let Some(n) = self.config.maxmemory_samples {
2218 conf.push_str(&format!("maxmemory-samples {n}\n"));
2219 }
2220 if let Some(ref limit) = self.config.maxmemory_clients {
2221 conf.push_str(&format!("maxmemory-clients {limit}\n"));
2222 }
2223 if let Some(n) = self.config.maxmemory_eviction_tenacity {
2224 conf.push_str(&format!("maxmemory-eviction-tenacity {n}\n"));
2225 }
2226 if let Some(n) = self.config.maxclients {
2227 conf.push_str(&format!("maxclients {n}\n"));
2228 }
2229 if let Some(n) = self.config.lfu_log_factor {
2230 conf.push_str(&format!("lfu-log-factor {n}\n"));
2231 }
2232 if let Some(n) = self.config.lfu_decay_time {
2233 conf.push_str(&format!("lfu-decay-time {n}\n"));
2234 }
2235 if let Some(n) = self.config.active_expire_effort {
2236 conf.push_str(&format!("active-expire-effort {n}\n"));
2237 }
2238
2239 if let Some(v) = self.config.lazyfree_lazy_eviction {
2241 conf.push_str(&format!("lazyfree-lazy-eviction {}\n", yn(v)));
2242 }
2243 if let Some(v) = self.config.lazyfree_lazy_expire {
2244 conf.push_str(&format!("lazyfree-lazy-expire {}\n", yn(v)));
2245 }
2246 if let Some(v) = self.config.lazyfree_lazy_server_del {
2247 conf.push_str(&format!("lazyfree-lazy-server-del {}\n", yn(v)));
2248 }
2249 if let Some(v) = self.config.lazyfree_lazy_user_del {
2250 conf.push_str(&format!("lazyfree-lazy-user-del {}\n", yn(v)));
2251 }
2252 if let Some(v) = self.config.lazyfree_lazy_user_flush {
2253 conf.push_str(&format!("lazyfree-lazy-user-flush {}\n", yn(v)));
2254 }
2255
2256 match &self.config.save {
2258 SavePolicy::Disabled => conf.push_str("save \"\"\n"),
2259 SavePolicy::Default => {}
2260 SavePolicy::Custom(pairs) => {
2261 for (secs, changes) in pairs {
2262 conf.push_str(&format!("save {secs} {changes}\n"));
2263 }
2264 }
2265 }
2266 if self.config.appendonly {
2267 conf.push_str("appendonly yes\n");
2268 }
2269 if let Some(ref policy) = self.config.appendfsync {
2270 conf.push_str(&format!("appendfsync {policy}\n"));
2271 }
2272 if let Some(ref name) = self.config.appendfilename {
2273 conf.push_str(&format!("appendfilename \"{name}\"\n"));
2274 }
2275 if let Some(ref name) = self.config.appenddirname {
2276 conf.push_str(&format!("appenddirname \"{}\"\n", name.display()));
2277 }
2278 if let Some(v) = self.config.aof_use_rdb_preamble {
2279 conf.push_str(&format!("aof-use-rdb-preamble {}\n", yn(v)));
2280 }
2281 if let Some(v) = self.config.aof_load_truncated {
2282 conf.push_str(&format!("aof-load-truncated {}\n", yn(v)));
2283 }
2284 if let Some(ref size) = self.config.aof_load_corrupt_tail_max_size {
2285 conf.push_str(&format!("aof-load-corrupt-tail-max-size {size}\n"));
2286 }
2287 if let Some(v) = self.config.aof_rewrite_incremental_fsync {
2288 conf.push_str(&format!("aof-rewrite-incremental-fsync {}\n", yn(v)));
2289 }
2290 if let Some(v) = self.config.aof_timestamp_enabled {
2291 conf.push_str(&format!("aof-timestamp-enabled {}\n", yn(v)));
2292 }
2293 if let Some(pct) = self.config.auto_aof_rewrite_percentage {
2294 conf.push_str(&format!("auto-aof-rewrite-percentage {pct}\n"));
2295 }
2296 if let Some(ref size) = self.config.auto_aof_rewrite_min_size {
2297 conf.push_str(&format!("auto-aof-rewrite-min-size {size}\n"));
2298 }
2299 if let Some(v) = self.config.no_appendfsync_on_rewrite {
2300 conf.push_str(&format!("no-appendfsync-on-rewrite {}\n", yn(v)));
2301 }
2302
2303 if let Some((ref host, port)) = self.config.replicaof {
2305 conf.push_str(&format!("replicaof {host} {port}\n"));
2306 }
2307 if let Some(ref pw) = self.config.masterauth {
2308 conf.push_str(&format!("masterauth {pw}\n"));
2309 }
2310 if let Some(ref user) = self.config.masteruser {
2311 conf.push_str(&format!("masteruser {user}\n"));
2312 }
2313 if let Some(ref size) = self.config.repl_backlog_size {
2314 conf.push_str(&format!("repl-backlog-size {size}\n"));
2315 }
2316 if let Some(ttl) = self.config.repl_backlog_ttl {
2317 conf.push_str(&format!("repl-backlog-ttl {ttl}\n"));
2318 }
2319 if let Some(v) = self.config.repl_disable_tcp_nodelay {
2320 conf.push_str(&format!("repl-disable-tcp-nodelay {}\n", yn(v)));
2321 }
2322 if let Some(ref policy) = self.config.repl_diskless_load {
2323 conf.push_str(&format!("repl-diskless-load {policy}\n"));
2324 }
2325 if let Some(v) = self.config.repl_diskless_sync {
2326 conf.push_str(&format!("repl-diskless-sync {}\n", yn(v)));
2327 }
2328 if let Some(delay) = self.config.repl_diskless_sync_delay {
2329 conf.push_str(&format!("repl-diskless-sync-delay {delay}\n"));
2330 }
2331 if let Some(n) = self.config.repl_diskless_sync_max_replicas {
2332 conf.push_str(&format!("repl-diskless-sync-max-replicas {n}\n"));
2333 }
2334 if let Some(period) = self.config.repl_ping_replica_period {
2335 conf.push_str(&format!("repl-ping-replica-period {period}\n"));
2336 }
2337 if let Some(t) = self.config.repl_timeout {
2338 conf.push_str(&format!("repl-timeout {t}\n"));
2339 }
2340 if let Some(ref ip) = self.config.replica_announce_ip {
2341 conf.push_str(&format!("replica-announce-ip {ip}\n"));
2342 }
2343 if let Some(port) = self.config.replica_announce_port {
2344 conf.push_str(&format!("replica-announce-port {port}\n"));
2345 }
2346 if let Some(v) = self.config.replica_announced {
2347 conf.push_str(&format!("replica-announced {}\n", yn(v)));
2348 }
2349 if let Some(ref size) = self.config.replica_full_sync_buffer_limit {
2350 conf.push_str(&format!("replica-full-sync-buffer-limit {size}\n"));
2351 }
2352 if let Some(v) = self.config.replica_ignore_disk_write_errors {
2353 conf.push_str(&format!("replica-ignore-disk-write-errors {}\n", yn(v)));
2354 }
2355 if let Some(v) = self.config.replica_ignore_maxmemory {
2356 conf.push_str(&format!("replica-ignore-maxmemory {}\n", yn(v)));
2357 }
2358 if let Some(v) = self.config.replica_lazy_flush {
2359 conf.push_str(&format!("replica-lazy-flush {}\n", yn(v)));
2360 }
2361 if let Some(priority) = self.config.replica_priority {
2362 conf.push_str(&format!("replica-priority {priority}\n"));
2363 }
2364 if let Some(v) = self.config.replica_read_only {
2365 conf.push_str(&format!("replica-read-only {}\n", yn(v)));
2366 }
2367 if let Some(v) = self.config.replica_serve_stale_data {
2368 conf.push_str(&format!("replica-serve-stale-data {}\n", yn(v)));
2369 }
2370 if let Some(n) = self.config.min_replicas_to_write {
2371 conf.push_str(&format!("min-replicas-to-write {n}\n"));
2372 }
2373 if let Some(lag) = self.config.min_replicas_max_lag {
2374 conf.push_str(&format!("min-replicas-max-lag {lag}\n"));
2375 }
2376
2377 if let Some(ref pw) = self.config.password {
2379 conf.push_str(&format!("requirepass {pw}\n"));
2380 }
2381 if let Some(ref path) = self.config.acl_file {
2382 conf.push_str(&format!("aclfile \"{}\"\n", path.display()));
2383 }
2384
2385 if self.config.cluster_enabled {
2387 conf.push_str("cluster-enabled yes\n");
2388 if let Some(ref path) = self.config.cluster_config_file {
2389 conf.push_str(&format!("cluster-config-file \"{}\"\n", path.display()));
2390 } else {
2391 conf.push_str(&format!(
2392 "cluster-config-file \"{}/nodes.conf\"\n",
2393 node_dir.display()
2394 ));
2395 }
2396 if let Some(timeout) = self.config.cluster_node_timeout {
2397 conf.push_str(&format!("cluster-node-timeout {timeout}\n"));
2398 }
2399 if let Some(v) = self.config.cluster_require_full_coverage {
2400 conf.push_str(&format!("cluster-require-full-coverage {}\n", yn(v)));
2401 }
2402 if let Some(v) = self.config.cluster_allow_reads_when_down {
2403 conf.push_str(&format!("cluster-allow-reads-when-down {}\n", yn(v)));
2404 }
2405 if let Some(v) = self.config.cluster_allow_pubsubshard_when_down {
2406 conf.push_str(&format!("cluster-allow-pubsubshard-when-down {}\n", yn(v)));
2407 }
2408 if let Some(v) = self.config.cluster_allow_replica_migration {
2409 conf.push_str(&format!("cluster-allow-replica-migration {}\n", yn(v)));
2410 }
2411 if let Some(barrier) = self.config.cluster_migration_barrier {
2412 conf.push_str(&format!("cluster-migration-barrier {barrier}\n"));
2413 }
2414 if let Some(v) = self.config.cluster_replica_no_failover {
2415 conf.push_str(&format!("cluster-replica-no-failover {}\n", yn(v)));
2416 }
2417 if let Some(factor) = self.config.cluster_replica_validity_factor {
2418 conf.push_str(&format!("cluster-replica-validity-factor {factor}\n"));
2419 }
2420 if let Some(ref ip) = self.config.cluster_announce_ip {
2421 conf.push_str(&format!("cluster-announce-ip {ip}\n"));
2422 }
2423 if let Some(port) = self.config.cluster_announce_port {
2424 conf.push_str(&format!("cluster-announce-port {port}\n"));
2425 }
2426 if let Some(port) = self.config.cluster_announce_bus_port {
2427 conf.push_str(&format!("cluster-announce-bus-port {port}\n"));
2428 }
2429 if let Some(port) = self.config.cluster_announce_tls_port {
2430 conf.push_str(&format!("cluster-announce-tls-port {port}\n"));
2431 }
2432 if let Some(ref hostname) = self.config.cluster_announce_hostname {
2433 conf.push_str(&format!("cluster-announce-hostname {hostname}\n"));
2434 }
2435 if let Some(ref name) = self.config.cluster_announce_human_nodename {
2436 conf.push_str(&format!("cluster-announce-human-nodename {name}\n"));
2437 }
2438 if let Some(port) = self.config.cluster_port {
2439 conf.push_str(&format!("cluster-port {port}\n"));
2440 }
2441 if let Some(ref endpoint_type) = self.config.cluster_preferred_endpoint_type {
2442 conf.push_str(&format!(
2443 "cluster-preferred-endpoint-type {endpoint_type}\n"
2444 ));
2445 }
2446 if let Some(limit) = self.config.cluster_link_sendbuf_limit {
2447 conf.push_str(&format!("cluster-link-sendbuf-limit {limit}\n"));
2448 }
2449 if let Some(ratio) = self.config.cluster_compatibility_sample_ratio {
2450 conf.push_str(&format!("cluster-compatibility-sample-ratio {ratio}\n"));
2451 }
2452 if let Some(bytes) = self.config.cluster_slot_migration_handoff_max_lag_bytes {
2453 conf.push_str(&format!(
2454 "cluster-slot-migration-handoff-max-lag-bytes {bytes}\n"
2455 ));
2456 }
2457 if let Some(ms) = self.config.cluster_slot_migration_write_pause_timeout {
2458 conf.push_str(&format!(
2459 "cluster-slot-migration-write-pause-timeout {ms}\n"
2460 ));
2461 }
2462 if let Some(v) = self.config.cluster_slot_stats_enabled {
2463 conf.push_str(&format!("cluster-slot-stats-enabled {}\n", yn(v)));
2464 }
2465 }
2466
2467 if let Some(n) = self.config.hash_max_listpack_entries {
2469 conf.push_str(&format!("hash-max-listpack-entries {n}\n"));
2470 }
2471 if let Some(n) = self.config.hash_max_listpack_value {
2472 conf.push_str(&format!("hash-max-listpack-value {n}\n"));
2473 }
2474 if let Some(n) = self.config.list_max_listpack_size {
2475 conf.push_str(&format!("list-max-listpack-size {n}\n"));
2476 }
2477 if let Some(n) = self.config.list_compress_depth {
2478 conf.push_str(&format!("list-compress-depth {n}\n"));
2479 }
2480 if let Some(n) = self.config.set_max_intset_entries {
2481 conf.push_str(&format!("set-max-intset-entries {n}\n"));
2482 }
2483 if let Some(n) = self.config.set_max_listpack_entries {
2484 conf.push_str(&format!("set-max-listpack-entries {n}\n"));
2485 }
2486 if let Some(n) = self.config.set_max_listpack_value {
2487 conf.push_str(&format!("set-max-listpack-value {n}\n"));
2488 }
2489 if let Some(n) = self.config.zset_max_listpack_entries {
2490 conf.push_str(&format!("zset-max-listpack-entries {n}\n"));
2491 }
2492 if let Some(n) = self.config.zset_max_listpack_value {
2493 conf.push_str(&format!("zset-max-listpack-value {n}\n"));
2494 }
2495 if let Some(n) = self.config.hll_sparse_max_bytes {
2496 conf.push_str(&format!("hll-sparse-max-bytes {n}\n"));
2497 }
2498 if let Some(n) = self.config.stream_node_max_bytes {
2499 conf.push_str(&format!("stream-node-max-bytes {n}\n"));
2500 }
2501 if let Some(n) = self.config.stream_node_max_entries {
2502 conf.push_str(&format!("stream-node-max-entries {n}\n"));
2503 }
2504 if let Some(ms) = self.config.stream_idmp_duration {
2505 conf.push_str(&format!("stream-idmp-duration {ms}\n"));
2506 }
2507 if let Some(n) = self.config.stream_idmp_maxsize {
2508 conf.push_str(&format!("stream-idmp-maxsize {n}\n"));
2509 }
2510
2511 for (path, args) in &self.config.loadmodule {
2513 conf.push_str(&format!("loadmodule \"{}\"", path.display()));
2514 for arg in args {
2515 conf.push(' ');
2516 conf.push_str(arg);
2517 }
2518 conf.push('\n');
2519 }
2520
2521 if let Some(hz) = self.config.hz {
2523 conf.push_str(&format!("hz {hz}\n"));
2524 }
2525 if let Some(n) = self.config.io_threads {
2526 conf.push_str(&format!("io-threads {n}\n"));
2527 }
2528 if let Some(enable) = self.config.io_threads_do_reads {
2529 conf.push_str(&format!("io-threads-do-reads {}\n", yn(enable)));
2530 }
2531 if let Some(ref events) = self.config.notify_keyspace_events {
2532 conf.push_str(&format!("notify-keyspace-events {events}\n"));
2533 }
2534
2535 if let Some(us) = self.config.slowlog_log_slower_than {
2537 conf.push_str(&format!("slowlog-log-slower-than {us}\n"));
2538 }
2539 if let Some(n) = self.config.slowlog_max_len {
2540 conf.push_str(&format!("slowlog-max-len {n}\n"));
2541 }
2542
2543 if let Some(ms) = self.config.latency_monitor_threshold {
2545 conf.push_str(&format!("latency-monitor-threshold {ms}\n"));
2546 }
2547 if let Some(enable) = self.config.latency_tracking {
2548 conf.push_str(&format!("latency-tracking {}\n", yn(enable)));
2549 }
2550 if let Some(ref pcts) = self.config.latency_tracking_info_percentiles {
2551 conf.push_str(&format!("latency-tracking-info-percentiles \"{pcts}\"\n"));
2552 }
2553
2554 if let Some(enable) = self.config.activedefrag {
2556 conf.push_str(&format!("activedefrag {}\n", yn(enable)));
2557 }
2558 if let Some(ref bytes) = self.config.active_defrag_ignore_bytes {
2559 conf.push_str(&format!("active-defrag-ignore-bytes {bytes}\n"));
2560 }
2561 if let Some(pct) = self.config.active_defrag_threshold_lower {
2562 conf.push_str(&format!("active-defrag-threshold-lower {pct}\n"));
2563 }
2564 if let Some(pct) = self.config.active_defrag_threshold_upper {
2565 conf.push_str(&format!("active-defrag-threshold-upper {pct}\n"));
2566 }
2567 if let Some(pct) = self.config.active_defrag_cycle_min {
2568 conf.push_str(&format!("active-defrag-cycle-min {pct}\n"));
2569 }
2570 if let Some(pct) = self.config.active_defrag_cycle_max {
2571 conf.push_str(&format!("active-defrag-cycle-max {pct}\n"));
2572 }
2573 if let Some(n) = self.config.active_defrag_max_scan_fields {
2574 conf.push_str(&format!("active-defrag-max-scan-fields {n}\n"));
2575 }
2576
2577 if let Some(enable) = self.config.syslog_enabled {
2579 conf.push_str(&format!("syslog-enabled {}\n", yn(enable)));
2580 }
2581 if let Some(ref ident) = self.config.syslog_ident {
2582 conf.push_str(&format!("syslog-ident {ident}\n"));
2583 }
2584 if let Some(ref facility) = self.config.syslog_facility {
2585 conf.push_str(&format!("syslog-facility {facility}\n"));
2586 }
2587 if let Some(ref mode) = self.config.supervised {
2588 conf.push_str(&format!("supervised {mode}\n"));
2589 }
2590 if let Some(enable) = self.config.always_show_logo {
2591 conf.push_str(&format!("always-show-logo {}\n", yn(enable)));
2592 }
2593 if let Some(enable) = self.config.set_proc_title {
2594 conf.push_str(&format!("set-proc-title {}\n", yn(enable)));
2595 }
2596 if let Some(ref template) = self.config.proc_title_template {
2597 conf.push_str(&format!("proc-title-template \"{template}\"\n"));
2598 }
2599
2600 if let Some(ref default) = self.config.acl_pubsub_default {
2602 conf.push_str(&format!("acl-pubsub-default {default}\n"));
2603 }
2604 if let Some(n) = self.config.acllog_max_len {
2605 conf.push_str(&format!("acllog-max-len {n}\n"));
2606 }
2607 if let Some(ref mode) = self.config.enable_debug_command {
2608 conf.push_str(&format!("enable-debug-command {mode}\n"));
2609 }
2610 if let Some(ref mode) = self.config.enable_module_command {
2611 conf.push_str(&format!("enable-module-command {mode}\n"));
2612 }
2613 if let Some(ref mode) = self.config.enable_protected_configs {
2614 conf.push_str(&format!("enable-protected-configs {mode}\n"));
2615 }
2616 for (cmd, new_name) in &self.config.rename_command {
2617 conf.push_str(&format!("rename-command {cmd} \"{new_name}\"\n"));
2618 }
2619 if let Some(ref mode) = self.config.sanitize_dump_payload {
2620 conf.push_str(&format!("sanitize-dump-payload {mode}\n"));
2621 }
2622 if let Some(enable) = self.config.hide_user_data_from_log {
2623 conf.push_str(&format!("hide-user-data-from-log {}\n", yn(enable)));
2624 }
2625
2626 if let Some(ref addr) = self.config.bind_source_addr {
2628 conf.push_str(&format!("bind-source-addr {addr}\n"));
2629 }
2630 if let Some(ms) = self.config.busy_reply_threshold {
2631 conf.push_str(&format!("busy-reply-threshold {ms}\n"));
2632 }
2633 for limit in &self.config.client_output_buffer_limit {
2634 conf.push_str(&format!("client-output-buffer-limit {limit}\n"));
2635 }
2636 if let Some(ref limit) = self.config.client_query_buffer_limit {
2637 conf.push_str(&format!("client-query-buffer-limit {limit}\n"));
2638 }
2639 if let Some(ref len) = self.config.proto_max_bulk_len {
2640 conf.push_str(&format!("proto-max-bulk-len {len}\n"));
2641 }
2642 if let Some(n) = self.config.max_new_connections_per_cycle {
2643 conf.push_str(&format!("max-new-connections-per-cycle {n}\n"));
2644 }
2645 if let Some(n) = self.config.max_new_tls_connections_per_cycle {
2646 conf.push_str(&format!("max-new-tls-connections-per-cycle {n}\n"));
2647 }
2648 if let Some(id) = self.config.socket_mark_id {
2649 conf.push_str(&format!("socket-mark-id {id}\n"));
2650 }
2651
2652 if let Some(ref name) = self.config.dbfilename {
2654 conf.push_str(&format!("dbfilename {name}\n"));
2655 }
2656 if let Some(enable) = self.config.rdbcompression {
2657 conf.push_str(&format!("rdbcompression {}\n", yn(enable)));
2658 }
2659 if let Some(enable) = self.config.rdbchecksum {
2660 conf.push_str(&format!("rdbchecksum {}\n", yn(enable)));
2661 }
2662 if let Some(enable) = self.config.rdb_save_incremental_fsync {
2663 conf.push_str(&format!("rdb-save-incremental-fsync {}\n", yn(enable)));
2664 }
2665 if let Some(enable) = self.config.rdb_del_sync_files {
2666 conf.push_str(&format!("rdb-del-sync-files {}\n", yn(enable)));
2667 }
2668 if let Some(enable) = self.config.stop_writes_on_bgsave_error {
2669 conf.push_str(&format!("stop-writes-on-bgsave-error {}\n", yn(enable)));
2670 }
2671
2672 if let Some(ref behavior) = self.config.shutdown_on_sigint {
2674 conf.push_str(&format!("shutdown-on-sigint {behavior}\n"));
2675 }
2676 if let Some(ref behavior) = self.config.shutdown_on_sigterm {
2677 conf.push_str(&format!("shutdown-on-sigterm {behavior}\n"));
2678 }
2679 if let Some(seconds) = self.config.shutdown_timeout {
2680 conf.push_str(&format!("shutdown-timeout {seconds}\n"));
2681 }
2682
2683 if let Some(enable) = self.config.activerehashing {
2685 conf.push_str(&format!("activerehashing {}\n", yn(enable)));
2686 }
2687 if let Some(enable) = self.config.crash_log_enabled {
2688 conf.push_str(&format!("crash-log-enabled {}\n", yn(enable)));
2689 }
2690 if let Some(enable) = self.config.crash_memcheck_enabled {
2691 conf.push_str(&format!("crash-memcheck-enabled {}\n", yn(enable)));
2692 }
2693 if let Some(enable) = self.config.disable_thp {
2694 conf.push_str(&format!("disable-thp {}\n", yn(enable)));
2695 }
2696 if let Some(enable) = self.config.dynamic_hz {
2697 conf.push_str(&format!("dynamic-hz {}\n", yn(enable)));
2698 }
2699 if let Some(ref warning) = self.config.ignore_warnings {
2700 conf.push_str(&format!("ignore-warnings {warning}\n"));
2701 }
2702 for path in &self.config.include {
2703 conf.push_str(&format!("include \"{}\"\n", path.display()));
2704 }
2705 if let Some(enable) = self.config.jemalloc_bg_thread {
2706 conf.push_str(&format!("jemalloc-bg-thread {}\n", yn(enable)));
2707 }
2708 if let Some(ref locale) = self.config.locale_collate {
2709 conf.push_str(&format!("locale-collate {locale}\n"));
2710 }
2711 if let Some(ms) = self.config.lua_time_limit {
2712 conf.push_str(&format!("lua-time-limit {ms}\n"));
2713 }
2714 if let Some(ref mode) = self.config.oom_score_adj {
2715 conf.push_str(&format!("oom-score-adj {mode}\n"));
2716 }
2717 if let Some(ref values) = self.config.oom_score_adj_values {
2718 conf.push_str(&format!("oom-score-adj-values {values}\n"));
2719 }
2720 if let Some(ref behavior) = self.config.propagation_error_behavior {
2721 conf.push_str(&format!("propagation-error-behavior {behavior}\n"));
2722 }
2723 if let Some(n) = self.config.tracking_table_max_keys {
2724 conf.push_str(&format!("tracking-table-max-keys {n}\n"));
2725 }
2726
2727 for (key, value) in &self.config.extra {
2729 conf.push_str(&format!("{key} {value}\n"));
2730 }
2731
2732 conf
2733 }
2734}
2735
2736impl Default for RedisServer {
2737 fn default() -> Self {
2738 Self::new()
2739 }
2740}
2741
2742pub struct RedisServerHandle {
2744 config: RedisServerConfig,
2745 cli: RedisCli,
2746 pid: u32,
2747 detached: bool,
2748}
2749
2750impl RedisServerHandle {
2751 pub fn addr(&self) -> String {
2753 format!("{}:{}", self.config.bind, self.config.port)
2754 }
2755
2756 pub fn port(&self) -> u16 {
2758 self.config.port
2759 }
2760
2761 pub fn host(&self) -> &str {
2763 &self.config.bind
2764 }
2765
2766 pub fn pid(&self) -> u32 {
2768 self.pid
2769 }
2770
2771 pub async fn is_alive(&self) -> bool {
2773 self.cli.ping().await
2774 }
2775
2776 pub fn cli(&self) -> &RedisCli {
2778 &self.cli
2779 }
2780
2781 pub async fn run(&self, args: &[&str]) -> Result<String> {
2783 self.cli.run(args).await
2784 }
2785
2786 pub fn detach(mut self) {
2788 self.detached = true;
2789 }
2790
2791 pub fn stop(&self) {
2798 self.cli.shutdown();
2800 std::thread::sleep(std::time::Duration::from_millis(500));
2802 if crate::process::pid_alive(self.pid) {
2804 crate::process::force_kill(self.pid);
2805 }
2806 crate::process::kill_by_port(self.config.port);
2808 }
2809
2810 pub async fn wait_for_ready(&self, timeout: Duration) -> Result<()> {
2812 self.cli.wait_for_ready(timeout).await
2813 }
2814}
2815
2816impl Drop for RedisServerHandle {
2817 fn drop(&mut self) {
2818 if !self.detached {
2819 self.stop();
2820 }
2821 }
2822}
2823
2824#[cfg(test)]
2825mod tests {
2826 use super::*;
2827
2828 #[test]
2829 fn default_config() {
2830 let s = RedisServer::new();
2831 assert_eq!(s.config.port, 6379);
2832 assert_eq!(s.config.bind, "127.0.0.1");
2833 assert!(matches!(s.config.save, SavePolicy::Disabled));
2834 }
2835
2836 #[test]
2837 fn builder_chain() {
2838 let s = RedisServer::new()
2839 .port(6400)
2840 .bind("0.0.0.0")
2841 .save(true)
2842 .appendonly(true)
2843 .password("secret")
2844 .logfile("/tmp/redis.log")
2845 .loglevel(LogLevel::Warning)
2846 .extra("maxmemory", "100mb");
2847
2848 assert_eq!(s.config.port, 6400);
2849 assert_eq!(s.config.bind, "0.0.0.0");
2850 assert!(matches!(s.config.save, SavePolicy::Default));
2851 assert!(s.config.appendonly);
2852 assert_eq!(s.config.password.as_deref(), Some("secret"));
2853 assert_eq!(s.config.logfile.as_deref(), Some("/tmp/redis.log"));
2854 assert_eq!(s.config.extra.get("maxmemory").unwrap(), "100mb");
2855 }
2856
2857 #[test]
2858 fn save_schedule() {
2859 let s = RedisServer::new().save_schedule(vec![(900, 1), (300, 10)]);
2860 match &s.config.save {
2861 SavePolicy::Custom(pairs) => {
2862 assert_eq!(pairs, &[(900, 1), (300, 10)]);
2863 }
2864 _ => panic!("expected SavePolicy::Custom"),
2865 }
2866 }
2867
2868 #[test]
2869 fn aof_tuning() {
2870 let s = RedisServer::new()
2871 .appendonly(true)
2872 .appendfsync(AppendFsync::Always)
2873 .appendfilename("my.aof")
2874 .aof_use_rdb_preamble(true)
2875 .auto_aof_rewrite_percentage(100)
2876 .auto_aof_rewrite_min_size("64mb")
2877 .no_appendfsync_on_rewrite(true);
2878
2879 assert!(s.config.appendonly);
2880 assert!(matches!(s.config.appendfsync, Some(AppendFsync::Always)));
2881 assert_eq!(s.config.appendfilename.as_deref(), Some("my.aof"));
2882 assert_eq!(s.config.aof_use_rdb_preamble, Some(true));
2883 assert_eq!(s.config.auto_aof_rewrite_percentage, Some(100));
2884 assert_eq!(s.config.auto_aof_rewrite_min_size.as_deref(), Some("64mb"));
2885 assert_eq!(s.config.no_appendfsync_on_rewrite, Some(true));
2886 }
2887
2888 #[test]
2889 fn memory_eviction_and_lazyfree() {
2890 let s = RedisServer::new()
2891 .maxmemory("256mb")
2892 .maxmemory_policy("allkeys-lfu")
2893 .maxmemory_samples(10)
2894 .maxmemory_clients("0")
2895 .maxmemory_eviction_tenacity(50)
2896 .lfu_log_factor(10)
2897 .lfu_decay_time(1)
2898 .active_expire_effort(25)
2899 .lazyfree_lazy_eviction(true)
2900 .lazyfree_lazy_expire(true)
2901 .lazyfree_lazy_server_del(true)
2902 .lazyfree_lazy_user_del(false)
2903 .lazyfree_lazy_user_flush(true);
2904
2905 assert_eq!(s.config.maxmemory.as_deref(), Some("256mb"));
2906 assert_eq!(s.config.maxmemory_policy.as_deref(), Some("allkeys-lfu"));
2907 assert_eq!(s.config.maxmemory_samples, Some(10));
2908 assert_eq!(s.config.maxmemory_clients.as_deref(), Some("0"));
2909 assert_eq!(s.config.maxmemory_eviction_tenacity, Some(50));
2910 assert_eq!(s.config.lfu_log_factor, Some(10));
2911 assert_eq!(s.config.lfu_decay_time, Some(1));
2912 assert_eq!(s.config.active_expire_effort, Some(25));
2913 assert_eq!(s.config.lazyfree_lazy_eviction, Some(true));
2914 assert_eq!(s.config.lazyfree_lazy_expire, Some(true));
2915 assert_eq!(s.config.lazyfree_lazy_server_del, Some(true));
2916 assert_eq!(s.config.lazyfree_lazy_user_del, Some(false));
2917 assert_eq!(s.config.lazyfree_lazy_user_flush, Some(true));
2918 }
2919
2920 #[test]
2921 fn replication_tuning() {
2922 let s = RedisServer::new()
2923 .replicaof("127.0.0.1", 6379)
2924 .masterauth("secret")
2925 .masteruser("repl-user")
2926 .repl_backlog_size("1mb")
2927 .repl_backlog_ttl(3600)
2928 .repl_disable_tcp_nodelay(true)
2929 .repl_diskless_load(ReplDisklessLoad::Swapdb)
2930 .repl_diskless_sync(true)
2931 .repl_diskless_sync_delay(5)
2932 .repl_diskless_sync_max_replicas(3)
2933 .repl_ping_replica_period(10)
2934 .repl_timeout(60)
2935 .replica_announce_ip("10.0.0.1")
2936 .replica_announce_port(6380)
2937 .replica_announced(true)
2938 .replica_full_sync_buffer_limit("256mb")
2939 .replica_ignore_disk_write_errors(false)
2940 .replica_ignore_maxmemory(true)
2941 .replica_lazy_flush(true)
2942 .replica_priority(100)
2943 .replica_read_only(true)
2944 .replica_serve_stale_data(false)
2945 .min_replicas_to_write(2)
2946 .min_replicas_max_lag(10);
2947
2948 assert_eq!(s.config.replicaof, Some(("127.0.0.1".into(), 6379)));
2949 assert_eq!(s.config.masterauth.as_deref(), Some("secret"));
2950 assert_eq!(s.config.masteruser.as_deref(), Some("repl-user"));
2951 assert_eq!(s.config.repl_backlog_size.as_deref(), Some("1mb"));
2952 assert_eq!(s.config.repl_backlog_ttl, Some(3600));
2953 assert_eq!(s.config.repl_disable_tcp_nodelay, Some(true));
2954 assert!(matches!(
2955 s.config.repl_diskless_load,
2956 Some(ReplDisklessLoad::Swapdb)
2957 ));
2958 assert_eq!(s.config.repl_diskless_sync, Some(true));
2959 assert_eq!(s.config.repl_diskless_sync_delay, Some(5));
2960 assert_eq!(s.config.repl_diskless_sync_max_replicas, Some(3));
2961 assert_eq!(s.config.repl_ping_replica_period, Some(10));
2962 assert_eq!(s.config.repl_timeout, Some(60));
2963 assert_eq!(s.config.replica_announce_ip.as_deref(), Some("10.0.0.1"));
2964 assert_eq!(s.config.replica_announce_port, Some(6380));
2965 assert_eq!(s.config.replica_announced, Some(true));
2966 assert_eq!(
2967 s.config.replica_full_sync_buffer_limit.as_deref(),
2968 Some("256mb")
2969 );
2970 assert_eq!(s.config.replica_ignore_disk_write_errors, Some(false));
2971 assert_eq!(s.config.replica_ignore_maxmemory, Some(true));
2972 assert_eq!(s.config.replica_lazy_flush, Some(true));
2973 assert_eq!(s.config.replica_priority, Some(100));
2974 assert_eq!(s.config.replica_read_only, Some(true));
2975 assert_eq!(s.config.replica_serve_stale_data, Some(false));
2976 assert_eq!(s.config.min_replicas_to_write, Some(2));
2977 assert_eq!(s.config.min_replicas_max_lag, Some(10));
2978 }
2979
2980 #[test]
2981 fn cluster_config() {
2982 let s = RedisServer::new()
2983 .port(7000)
2984 .cluster_enabled(true)
2985 .cluster_node_timeout(5000)
2986 .cluster_config_file("/tmp/nodes.conf")
2987 .cluster_require_full_coverage(false)
2988 .cluster_allow_reads_when_down(true)
2989 .cluster_allow_pubsubshard_when_down(true)
2990 .cluster_allow_replica_migration(true)
2991 .cluster_migration_barrier(1)
2992 .cluster_replica_no_failover(false)
2993 .cluster_replica_validity_factor(10)
2994 .cluster_announce_ip("10.0.0.1")
2995 .cluster_announce_port(7000)
2996 .cluster_announce_bus_port(17000)
2997 .cluster_announce_tls_port(7100)
2998 .cluster_announce_hostname("node1.example.com")
2999 .cluster_announce_human_nodename("node-1")
3000 .cluster_port(17000)
3001 .cluster_preferred_endpoint_type("ip")
3002 .cluster_link_sendbuf_limit(67108864)
3003 .cluster_compatibility_sample_ratio(50)
3004 .cluster_slot_migration_handoff_max_lag_bytes(1048576)
3005 .cluster_slot_migration_write_pause_timeout(5000)
3006 .cluster_slot_stats_enabled(true);
3007
3008 assert!(s.config.cluster_enabled);
3009 assert_eq!(s.config.cluster_node_timeout, Some(5000));
3010 assert_eq!(
3011 s.config.cluster_config_file,
3012 Some(PathBuf::from("/tmp/nodes.conf"))
3013 );
3014 assert_eq!(s.config.cluster_require_full_coverage, Some(false));
3015 assert_eq!(s.config.cluster_allow_reads_when_down, Some(true));
3016 assert_eq!(s.config.cluster_allow_pubsubshard_when_down, Some(true));
3017 assert_eq!(s.config.cluster_allow_replica_migration, Some(true));
3018 assert_eq!(s.config.cluster_migration_barrier, Some(1));
3019 assert_eq!(s.config.cluster_replica_no_failover, Some(false));
3020 assert_eq!(s.config.cluster_replica_validity_factor, Some(10));
3021 assert_eq!(s.config.cluster_announce_ip.as_deref(), Some("10.0.0.1"));
3022 assert_eq!(s.config.cluster_announce_port, Some(7000));
3023 assert_eq!(s.config.cluster_announce_bus_port, Some(17000));
3024 assert_eq!(s.config.cluster_announce_tls_port, Some(7100));
3025 assert_eq!(
3026 s.config.cluster_announce_hostname.as_deref(),
3027 Some("node1.example.com")
3028 );
3029 assert_eq!(
3030 s.config.cluster_announce_human_nodename.as_deref(),
3031 Some("node-1")
3032 );
3033 assert_eq!(s.config.cluster_port, Some(17000));
3034 assert_eq!(
3035 s.config.cluster_preferred_endpoint_type.as_deref(),
3036 Some("ip")
3037 );
3038 assert_eq!(s.config.cluster_link_sendbuf_limit, Some(67108864));
3039 assert_eq!(s.config.cluster_compatibility_sample_ratio, Some(50));
3040 assert_eq!(
3041 s.config.cluster_slot_migration_handoff_max_lag_bytes,
3042 Some(1048576)
3043 );
3044 assert_eq!(
3045 s.config.cluster_slot_migration_write_pause_timeout,
3046 Some(5000)
3047 );
3048 assert_eq!(s.config.cluster_slot_stats_enabled, Some(true));
3049 }
3050
3051 #[test]
3052 fn data_structure_tuning() {
3053 let s = RedisServer::new()
3054 .hash_max_listpack_entries(128)
3055 .hash_max_listpack_value(64)
3056 .list_max_listpack_size(-2)
3057 .list_compress_depth(1)
3058 .set_max_intset_entries(512)
3059 .set_max_listpack_entries(128)
3060 .set_max_listpack_value(64)
3061 .zset_max_listpack_entries(128)
3062 .zset_max_listpack_value(64)
3063 .hll_sparse_max_bytes(3000)
3064 .stream_node_max_bytes(4096)
3065 .stream_node_max_entries(100)
3066 .stream_idmp_duration(5000)
3067 .stream_idmp_maxsize(1000);
3068
3069 assert_eq!(s.config.hash_max_listpack_entries, Some(128));
3070 assert_eq!(s.config.hash_max_listpack_value, Some(64));
3071 assert_eq!(s.config.list_max_listpack_size, Some(-2));
3072 assert_eq!(s.config.list_compress_depth, Some(1));
3073 assert_eq!(s.config.set_max_intset_entries, Some(512));
3074 assert_eq!(s.config.set_max_listpack_entries, Some(128));
3075 assert_eq!(s.config.set_max_listpack_value, Some(64));
3076 assert_eq!(s.config.zset_max_listpack_entries, Some(128));
3077 assert_eq!(s.config.zset_max_listpack_value, Some(64));
3078 assert_eq!(s.config.hll_sparse_max_bytes, Some(3000));
3079 assert_eq!(s.config.stream_node_max_bytes, Some(4096));
3080 assert_eq!(s.config.stream_node_max_entries, Some(100));
3081 assert_eq!(s.config.stream_idmp_duration, Some(5000));
3082 assert_eq!(s.config.stream_idmp_maxsize, Some(1000));
3083 }
3084
3085 #[test]
3086 fn tls_config() {
3087 let s = RedisServer::new()
3088 .port(6400)
3089 .tls_port(6401)
3090 .tls_cert_file("/etc/tls/redis.crt")
3091 .tls_key_file("/etc/tls/redis.key")
3092 .tls_key_file_pass("keypass")
3093 .tls_ca_cert_file("/etc/tls/ca.crt")
3094 .tls_ca_cert_dir("/etc/tls/certs")
3095 .tls_auth_clients(true)
3096 .tls_client_cert_file("/etc/tls/client.crt")
3097 .tls_client_key_file("/etc/tls/client.key")
3098 .tls_client_key_file_pass("clientpass")
3099 .tls_dh_params_file("/etc/tls/dhparams.pem")
3100 .tls_ciphers("ECDHE-RSA-AES256-GCM-SHA384")
3101 .tls_ciphersuites("TLS_AES_256_GCM_SHA384")
3102 .tls_protocols("TLSv1.2 TLSv1.3")
3103 .tls_prefer_server_ciphers(true)
3104 .tls_session_caching(true)
3105 .tls_session_cache_size(20480)
3106 .tls_session_cache_timeout(300)
3107 .tls_replication(true)
3108 .tls_cluster(true);
3109
3110 assert_eq!(s.config.tls_port, Some(6401));
3111 assert_eq!(
3112 s.config.tls_cert_file.as_deref(),
3113 Some(std::path::Path::new("/etc/tls/redis.crt"))
3114 );
3115 assert_eq!(
3116 s.config.tls_key_file.as_deref(),
3117 Some(std::path::Path::new("/etc/tls/redis.key"))
3118 );
3119 assert_eq!(s.config.tls_key_file_pass.as_deref(), Some("keypass"));
3120 assert_eq!(
3121 s.config.tls_ca_cert_file.as_deref(),
3122 Some(std::path::Path::new("/etc/tls/ca.crt"))
3123 );
3124 assert_eq!(
3125 s.config.tls_ca_cert_dir.as_deref(),
3126 Some(std::path::Path::new("/etc/tls/certs"))
3127 );
3128 assert_eq!(s.config.tls_auth_clients, Some(true));
3129 assert_eq!(
3130 s.config.tls_client_cert_file.as_deref(),
3131 Some(std::path::Path::new("/etc/tls/client.crt"))
3132 );
3133 assert_eq!(
3134 s.config.tls_client_key_file.as_deref(),
3135 Some(std::path::Path::new("/etc/tls/client.key"))
3136 );
3137 assert_eq!(
3138 s.config.tls_client_key_file_pass.as_deref(),
3139 Some("clientpass")
3140 );
3141 assert_eq!(
3142 s.config.tls_dh_params_file.as_deref(),
3143 Some(std::path::Path::new("/etc/tls/dhparams.pem"))
3144 );
3145 assert_eq!(
3146 s.config.tls_ciphers.as_deref(),
3147 Some("ECDHE-RSA-AES256-GCM-SHA384")
3148 );
3149 assert_eq!(
3150 s.config.tls_ciphersuites.as_deref(),
3151 Some("TLS_AES_256_GCM_SHA384")
3152 );
3153 assert_eq!(s.config.tls_protocols.as_deref(), Some("TLSv1.2 TLSv1.3"));
3154 assert_eq!(s.config.tls_prefer_server_ciphers, Some(true));
3155 assert_eq!(s.config.tls_session_caching, Some(true));
3156 assert_eq!(s.config.tls_session_cache_size, Some(20480));
3157 assert_eq!(s.config.tls_session_cache_timeout, Some(300));
3158 assert_eq!(s.config.tls_replication, Some(true));
3159 assert_eq!(s.config.tls_cluster, Some(true));
3160 }
3161
3162 #[test]
3163 fn loadmodule_config() {
3164 let s = RedisServer::new().loadmodule("/x/mod.so");
3165 let config = s.generate_config(std::path::Path::new("/tmp/rsw-test"));
3166 assert!(config.contains("loadmodule \"/x/mod.so\"\n"));
3167 }
3168
3169 #[test]
3170 fn loadmodule_with_args_config() {
3171 let s = RedisServer::new()
3172 .loadmodule_with_args("/x/mod.so", ["stream-prefix", "ks:", "events"]);
3173 let config = s.generate_config(std::path::Path::new("/tmp/rsw-test"));
3174 assert!(config.contains("loadmodule \"/x/mod.so\" stream-prefix ks: events\n"));
3175 }
3176}