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>,
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
464#[derive(Debug, Clone, Copy)]
466pub enum AppendFsync {
467 Always,
469 Everysec,
471 No,
473}
474
475impl std::fmt::Display for AppendFsync {
476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477 match self {
478 AppendFsync::Always => f.write_str("always"),
479 AppendFsync::Everysec => f.write_str("everysec"),
480 AppendFsync::No => f.write_str("no"),
481 }
482 }
483}
484
485#[derive(Debug, Clone, Copy)]
490pub enum ReplDisklessLoad {
491 Disabled,
493 OnEmptyDb,
495 Swapdb,
497}
498
499impl std::fmt::Display for ReplDisklessLoad {
500 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501 match self {
502 ReplDisklessLoad::Disabled => f.write_str("disabled"),
503 ReplDisklessLoad::OnEmptyDb => f.write_str("on-empty-db"),
504 ReplDisklessLoad::Swapdb => f.write_str("swapdb"),
505 }
506 }
507}
508
509#[derive(Debug, Clone, Default)]
514pub enum SavePolicy {
515 #[default]
517 Disabled,
518 Default,
520 Custom(Vec<(u64, u64)>),
522}
523
524#[derive(Debug, Clone, Copy)]
526pub enum LogLevel {
527 Debug,
529 Verbose,
531 Notice,
533 Warning,
535}
536
537impl std::fmt::Display for LogLevel {
538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539 match self {
540 LogLevel::Debug => f.write_str("debug"),
541 LogLevel::Verbose => f.write_str("verbose"),
542 LogLevel::Notice => f.write_str("notice"),
543 LogLevel::Warning => f.write_str("warning"),
544 }
545 }
546}
547
548impl Default for RedisServerConfig {
549 fn default() -> Self {
550 Self {
551 port: 6379,
552 bind: "127.0.0.1".into(),
553 protected_mode: false,
554 tcp_backlog: None,
555 unixsocket: None,
556 unixsocketperm: None,
557 timeout: None,
558 tcp_keepalive: None,
559 tls_port: None,
560 tls_cert_file: None,
561 tls_key_file: None,
562 tls_key_file_pass: None,
563 tls_ca_cert_file: None,
564 tls_ca_cert_dir: None,
565 tls_auth_clients: None,
566 tls_client_cert_file: None,
567 tls_client_key_file: None,
568 tls_client_key_file_pass: None,
569 tls_dh_params_file: None,
570 tls_ciphers: None,
571 tls_ciphersuites: None,
572 tls_protocols: None,
573 tls_prefer_server_ciphers: None,
574 tls_session_caching: None,
575 tls_session_cache_size: None,
576 tls_session_cache_timeout: None,
577 tls_replication: None,
578 tls_cluster: None,
579 daemonize: true,
580 dir: std::env::temp_dir().join("redis-server-wrapper"),
581 logfile: None,
582 loglevel: LogLevel::Notice,
583 databases: None,
584 maxmemory: None,
585 maxmemory_policy: None,
586 maxmemory_samples: None,
587 maxmemory_clients: None,
588 maxmemory_eviction_tenacity: None,
589 maxclients: None,
590 lfu_log_factor: None,
591 lfu_decay_time: None,
592 active_expire_effort: None,
593 lazyfree_lazy_eviction: None,
594 lazyfree_lazy_expire: None,
595 lazyfree_lazy_server_del: None,
596 lazyfree_lazy_user_del: None,
597 lazyfree_lazy_user_flush: None,
598 save: SavePolicy::Disabled,
599 appendonly: false,
600 appendfsync: None,
601 appendfilename: None,
602 appenddirname: None,
603 aof_use_rdb_preamble: None,
604 aof_load_truncated: None,
605 aof_load_corrupt_tail_max_size: None,
606 aof_rewrite_incremental_fsync: None,
607 aof_timestamp_enabled: None,
608 auto_aof_rewrite_percentage: None,
609 auto_aof_rewrite_min_size: None,
610 no_appendfsync_on_rewrite: None,
611 replicaof: None,
612 masterauth: None,
613 masteruser: None,
614 repl_backlog_size: None,
615 repl_backlog_ttl: None,
616 repl_disable_tcp_nodelay: None,
617 repl_diskless_load: None,
618 repl_diskless_sync: None,
619 repl_diskless_sync_delay: None,
620 repl_diskless_sync_max_replicas: None,
621 repl_ping_replica_period: None,
622 repl_timeout: None,
623 replica_announce_ip: None,
624 replica_announce_port: None,
625 replica_announced: None,
626 replica_full_sync_buffer_limit: None,
627 replica_ignore_disk_write_errors: None,
628 replica_ignore_maxmemory: None,
629 replica_lazy_flush: None,
630 replica_priority: None,
631 replica_read_only: None,
632 replica_serve_stale_data: None,
633 min_replicas_to_write: None,
634 min_replicas_max_lag: None,
635 password: None,
636 acl_file: None,
637 cluster_enabled: false,
638 cluster_node_timeout: None,
639 cluster_config_file: None,
640 cluster_require_full_coverage: None,
641 cluster_allow_reads_when_down: None,
642 cluster_allow_pubsubshard_when_down: None,
643 cluster_allow_replica_migration: None,
644 cluster_migration_barrier: None,
645 cluster_replica_no_failover: None,
646 cluster_replica_validity_factor: None,
647 cluster_announce_ip: None,
648 cluster_announce_port: None,
649 cluster_announce_bus_port: None,
650 cluster_announce_tls_port: None,
651 cluster_announce_hostname: None,
652 cluster_announce_human_nodename: None,
653 cluster_port: None,
654 cluster_preferred_endpoint_type: None,
655 cluster_link_sendbuf_limit: None,
656 cluster_compatibility_sample_ratio: None,
657 cluster_slot_migration_handoff_max_lag_bytes: None,
658 cluster_slot_migration_write_pause_timeout: None,
659 cluster_slot_stats_enabled: None,
660 hash_max_listpack_entries: None,
661 hash_max_listpack_value: None,
662 list_max_listpack_size: None,
663 list_compress_depth: None,
664 set_max_intset_entries: None,
665 set_max_listpack_entries: None,
666 set_max_listpack_value: None,
667 zset_max_listpack_entries: None,
668 zset_max_listpack_value: None,
669 hll_sparse_max_bytes: None,
670 stream_node_max_bytes: None,
671 stream_node_max_entries: None,
672 stream_idmp_duration: None,
673 stream_idmp_maxsize: None,
674 loadmodule: Vec::new(),
675 hz: None,
676 io_threads: None,
677 io_threads_do_reads: None,
678 notify_keyspace_events: None,
679 slowlog_log_slower_than: None,
680 slowlog_max_len: None,
681 latency_monitor_threshold: None,
682 latency_tracking: None,
683 latency_tracking_info_percentiles: None,
684 activedefrag: None,
685 active_defrag_ignore_bytes: None,
686 active_defrag_threshold_lower: None,
687 active_defrag_threshold_upper: None,
688 active_defrag_cycle_min: None,
689 active_defrag_cycle_max: None,
690 active_defrag_max_scan_fields: None,
691 syslog_enabled: None,
692 syslog_ident: None,
693 syslog_facility: None,
694 supervised: None,
695 always_show_logo: None,
696 set_proc_title: None,
697 proc_title_template: None,
698 acl_pubsub_default: None,
699 acllog_max_len: None,
700 enable_debug_command: None,
701 enable_module_command: None,
702 enable_protected_configs: None,
703 rename_command: Vec::new(),
704 sanitize_dump_payload: None,
705 hide_user_data_from_log: None,
706 bind_source_addr: None,
707 busy_reply_threshold: None,
708 client_output_buffer_limit: Vec::new(),
709 client_query_buffer_limit: None,
710 proto_max_bulk_len: None,
711 max_new_connections_per_cycle: None,
712 max_new_tls_connections_per_cycle: None,
713 socket_mark_id: None,
714 dbfilename: None,
715 rdbcompression: None,
716 rdbchecksum: None,
717 rdb_save_incremental_fsync: None,
718 rdb_del_sync_files: None,
719 stop_writes_on_bgsave_error: None,
720 shutdown_on_sigint: None,
721 shutdown_on_sigterm: None,
722 shutdown_timeout: None,
723 activerehashing: None,
724 crash_log_enabled: None,
725 crash_memcheck_enabled: None,
726 disable_thp: None,
727 dynamic_hz: None,
728 ignore_warnings: None,
729 include: Vec::new(),
730 jemalloc_bg_thread: None,
731 locale_collate: None,
732 lua_time_limit: None,
733 oom_score_adj: None,
734 oom_score_adj_values: None,
735 propagation_error_behavior: None,
736 tracking_table_max_keys: None,
737 extra: HashMap::new(),
738 redis_server_bin: "redis-server".into(),
739 redis_cli_bin: "redis-cli".into(),
740 }
741 }
742}
743
744pub struct RedisServer {
746 config: RedisServerConfig,
747}
748
749impl RedisServer {
750 pub fn new() -> Self {
752 Self {
753 config: RedisServerConfig::default(),
754 }
755 }
756
757 pub fn port(mut self, port: u16) -> Self {
761 self.config.port = port;
762 self
763 }
764
765 pub fn bind(mut self, bind: impl Into<String>) -> Self {
767 self.config.bind = bind.into();
768 self
769 }
770
771 pub fn protected_mode(mut self, protected: bool) -> Self {
773 self.config.protected_mode = protected;
774 self
775 }
776
777 pub fn tcp_backlog(mut self, backlog: u32) -> Self {
779 self.config.tcp_backlog = Some(backlog);
780 self
781 }
782
783 pub fn unixsocket(mut self, path: impl Into<PathBuf>) -> Self {
785 self.config.unixsocket = Some(path.into());
786 self
787 }
788
789 pub fn unixsocketperm(mut self, perm: u32) -> Self {
791 self.config.unixsocketperm = Some(perm);
792 self
793 }
794
795 pub fn timeout(mut self, seconds: u32) -> Self {
797 self.config.timeout = Some(seconds);
798 self
799 }
800
801 pub fn tcp_keepalive(mut self, seconds: u32) -> Self {
803 self.config.tcp_keepalive = Some(seconds);
804 self
805 }
806
807 pub fn tls_port(mut self, port: u16) -> Self {
811 self.config.tls_port = Some(port);
812 self
813 }
814
815 pub fn tls_cert_file(mut self, path: impl Into<PathBuf>) -> Self {
817 self.config.tls_cert_file = Some(path.into());
818 self
819 }
820
821 pub fn tls_key_file(mut self, path: impl Into<PathBuf>) -> Self {
823 self.config.tls_key_file = Some(path.into());
824 self
825 }
826
827 pub fn tls_ca_cert_file(mut self, path: impl Into<PathBuf>) -> Self {
829 self.config.tls_ca_cert_file = Some(path.into());
830 self
831 }
832
833 pub fn tls_auth_clients(mut self, require: bool) -> Self {
835 self.config.tls_auth_clients = Some(require);
836 self
837 }
838
839 pub fn tls_key_file_pass(mut self, pass: impl Into<String>) -> Self {
841 self.config.tls_key_file_pass = Some(pass.into());
842 self
843 }
844
845 pub fn tls_ca_cert_dir(mut self, path: impl Into<PathBuf>) -> Self {
847 self.config.tls_ca_cert_dir = Some(path.into());
848 self
849 }
850
851 pub fn tls_client_cert_file(mut self, path: impl Into<PathBuf>) -> Self {
853 self.config.tls_client_cert_file = Some(path.into());
854 self
855 }
856
857 pub fn tls_client_key_file(mut self, path: impl Into<PathBuf>) -> Self {
859 self.config.tls_client_key_file = Some(path.into());
860 self
861 }
862
863 pub fn tls_client_key_file_pass(mut self, pass: impl Into<String>) -> Self {
865 self.config.tls_client_key_file_pass = Some(pass.into());
866 self
867 }
868
869 pub fn tls_dh_params_file(mut self, path: impl Into<PathBuf>) -> Self {
871 self.config.tls_dh_params_file = Some(path.into());
872 self
873 }
874
875 pub fn tls_ciphers(mut self, ciphers: impl Into<String>) -> Self {
877 self.config.tls_ciphers = Some(ciphers.into());
878 self
879 }
880
881 pub fn tls_ciphersuites(mut self, suites: impl Into<String>) -> Self {
883 self.config.tls_ciphersuites = Some(suites.into());
884 self
885 }
886
887 pub fn tls_protocols(mut self, protocols: impl Into<String>) -> Self {
889 self.config.tls_protocols = Some(protocols.into());
890 self
891 }
892
893 pub fn tls_prefer_server_ciphers(mut self, prefer: bool) -> Self {
895 self.config.tls_prefer_server_ciphers = Some(prefer);
896 self
897 }
898
899 pub fn tls_session_caching(mut self, enable: bool) -> Self {
901 self.config.tls_session_caching = Some(enable);
902 self
903 }
904
905 pub fn tls_session_cache_size(mut self, size: u32) -> Self {
907 self.config.tls_session_cache_size = Some(size);
908 self
909 }
910
911 pub fn tls_session_cache_timeout(mut self, seconds: u32) -> Self {
913 self.config.tls_session_cache_timeout = Some(seconds);
914 self
915 }
916
917 pub fn tls_replication(mut self, enable: bool) -> Self {
919 self.config.tls_replication = Some(enable);
920 self
921 }
922
923 pub fn tls_cluster(mut self, enable: bool) -> Self {
925 self.config.tls_cluster = Some(enable);
926 self
927 }
928
929 pub fn dir(mut self, dir: impl Into<PathBuf>) -> Self {
933 self.config.dir = dir.into();
934 self
935 }
936
937 pub fn loglevel(mut self, level: LogLevel) -> Self {
939 self.config.loglevel = level;
940 self
941 }
942
943 pub fn logfile(mut self, path: impl Into<String>) -> Self {
945 self.config.logfile = Some(path.into());
946 self
947 }
948
949 pub fn databases(mut self, n: u32) -> Self {
951 self.config.databases = Some(n);
952 self
953 }
954
955 pub fn maxmemory(mut self, limit: impl Into<String>) -> Self {
959 self.config.maxmemory = Some(limit.into());
960 self
961 }
962
963 pub fn maxmemory_policy(mut self, policy: impl Into<String>) -> Self {
965 self.config.maxmemory_policy = Some(policy.into());
966 self
967 }
968
969 pub fn maxmemory_samples(mut self, n: u32) -> Self {
971 self.config.maxmemory_samples = Some(n);
972 self
973 }
974
975 pub fn maxmemory_clients(mut self, limit: impl Into<String>) -> Self {
977 self.config.maxmemory_clients = Some(limit.into());
978 self
979 }
980
981 pub fn maxmemory_eviction_tenacity(mut self, tenacity: u32) -> Self {
983 self.config.maxmemory_eviction_tenacity = Some(tenacity);
984 self
985 }
986
987 pub fn maxclients(mut self, n: u32) -> Self {
989 self.config.maxclients = Some(n);
990 self
991 }
992
993 pub fn lfu_log_factor(mut self, factor: u32) -> Self {
995 self.config.lfu_log_factor = Some(factor);
996 self
997 }
998
999 pub fn lfu_decay_time(mut self, minutes: u32) -> Self {
1001 self.config.lfu_decay_time = Some(minutes);
1002 self
1003 }
1004
1005 pub fn active_expire_effort(mut self, effort: u32) -> Self {
1007 self.config.active_expire_effort = Some(effort);
1008 self
1009 }
1010
1011 pub fn lazyfree_lazy_eviction(mut self, enable: bool) -> Self {
1015 self.config.lazyfree_lazy_eviction = Some(enable);
1016 self
1017 }
1018
1019 pub fn lazyfree_lazy_expire(mut self, enable: bool) -> Self {
1021 self.config.lazyfree_lazy_expire = Some(enable);
1022 self
1023 }
1024
1025 pub fn lazyfree_lazy_server_del(mut self, enable: bool) -> Self {
1027 self.config.lazyfree_lazy_server_del = Some(enable);
1028 self
1029 }
1030
1031 pub fn lazyfree_lazy_user_del(mut self, enable: bool) -> Self {
1033 self.config.lazyfree_lazy_user_del = Some(enable);
1034 self
1035 }
1036
1037 pub fn lazyfree_lazy_user_flush(mut self, enable: bool) -> Self {
1039 self.config.lazyfree_lazy_user_flush = Some(enable);
1040 self
1041 }
1042
1043 pub fn save(mut self, save: bool) -> Self {
1050 self.config.save = if save {
1051 SavePolicy::Default
1052 } else {
1053 SavePolicy::Disabled
1054 };
1055 self
1056 }
1057
1058 pub fn save_schedule(mut self, schedule: Vec<(u64, u64)>) -> Self {
1062 self.config.save = SavePolicy::Custom(schedule);
1063 self
1064 }
1065
1066 pub fn appendonly(mut self, appendonly: bool) -> Self {
1068 self.config.appendonly = appendonly;
1069 self
1070 }
1071
1072 pub fn appendfsync(mut self, policy: AppendFsync) -> Self {
1074 self.config.appendfsync = Some(policy);
1075 self
1076 }
1077
1078 pub fn appendfilename(mut self, name: impl Into<String>) -> Self {
1080 self.config.appendfilename = Some(name.into());
1081 self
1082 }
1083
1084 pub fn appenddirname(mut self, name: impl Into<PathBuf>) -> Self {
1086 self.config.appenddirname = Some(name.into());
1087 self
1088 }
1089
1090 pub fn aof_use_rdb_preamble(mut self, enable: bool) -> Self {
1092 self.config.aof_use_rdb_preamble = Some(enable);
1093 self
1094 }
1095
1096 pub fn aof_load_truncated(mut self, enable: bool) -> Self {
1098 self.config.aof_load_truncated = Some(enable);
1099 self
1100 }
1101
1102 pub fn aof_load_corrupt_tail_max_size(mut self, size: impl Into<String>) -> Self {
1104 self.config.aof_load_corrupt_tail_max_size = Some(size.into());
1105 self
1106 }
1107
1108 pub fn aof_rewrite_incremental_fsync(mut self, enable: bool) -> Self {
1110 self.config.aof_rewrite_incremental_fsync = Some(enable);
1111 self
1112 }
1113
1114 pub fn aof_timestamp_enabled(mut self, enable: bool) -> Self {
1116 self.config.aof_timestamp_enabled = Some(enable);
1117 self
1118 }
1119
1120 pub fn auto_aof_rewrite_percentage(mut self, pct: u32) -> Self {
1122 self.config.auto_aof_rewrite_percentage = Some(pct);
1123 self
1124 }
1125
1126 pub fn auto_aof_rewrite_min_size(mut self, size: impl Into<String>) -> Self {
1128 self.config.auto_aof_rewrite_min_size = Some(size.into());
1129 self
1130 }
1131
1132 pub fn no_appendfsync_on_rewrite(mut self, enable: bool) -> Self {
1134 self.config.no_appendfsync_on_rewrite = Some(enable);
1135 self
1136 }
1137
1138 pub fn replicaof(mut self, host: impl Into<String>, port: u16) -> Self {
1142 self.config.replicaof = Some((host.into(), port));
1143 self
1144 }
1145
1146 pub fn masterauth(mut self, password: impl Into<String>) -> Self {
1148 self.config.masterauth = Some(password.into());
1149 self
1150 }
1151
1152 pub fn masteruser(mut self, user: impl Into<String>) -> Self {
1154 self.config.masteruser = Some(user.into());
1155 self
1156 }
1157
1158 pub fn repl_backlog_size(mut self, size: impl Into<String>) -> Self {
1160 self.config.repl_backlog_size = Some(size.into());
1161 self
1162 }
1163
1164 pub fn repl_backlog_ttl(mut self, seconds: u32) -> Self {
1166 self.config.repl_backlog_ttl = Some(seconds);
1167 self
1168 }
1169
1170 pub fn repl_disable_tcp_nodelay(mut self, disable: bool) -> Self {
1172 self.config.repl_disable_tcp_nodelay = Some(disable);
1173 self
1174 }
1175
1176 pub fn repl_diskless_load(mut self, policy: ReplDisklessLoad) -> Self {
1178 self.config.repl_diskless_load = Some(policy);
1179 self
1180 }
1181
1182 pub fn repl_diskless_sync(mut self, enable: bool) -> Self {
1184 self.config.repl_diskless_sync = Some(enable);
1185 self
1186 }
1187
1188 pub fn repl_diskless_sync_delay(mut self, seconds: u32) -> Self {
1190 self.config.repl_diskless_sync_delay = Some(seconds);
1191 self
1192 }
1193
1194 pub fn repl_diskless_sync_max_replicas(mut self, n: u32) -> Self {
1196 self.config.repl_diskless_sync_max_replicas = Some(n);
1197 self
1198 }
1199
1200 pub fn repl_ping_replica_period(mut self, seconds: u32) -> Self {
1202 self.config.repl_ping_replica_period = Some(seconds);
1203 self
1204 }
1205
1206 pub fn repl_timeout(mut self, seconds: u32) -> Self {
1208 self.config.repl_timeout = Some(seconds);
1209 self
1210 }
1211
1212 pub fn replica_announce_ip(mut self, ip: impl Into<String>) -> Self {
1214 self.config.replica_announce_ip = Some(ip.into());
1215 self
1216 }
1217
1218 pub fn replica_announce_port(mut self, port: u16) -> Self {
1220 self.config.replica_announce_port = Some(port);
1221 self
1222 }
1223
1224 pub fn replica_announced(mut self, announced: bool) -> Self {
1226 self.config.replica_announced = Some(announced);
1227 self
1228 }
1229
1230 pub fn replica_full_sync_buffer_limit(mut self, size: impl Into<String>) -> Self {
1232 self.config.replica_full_sync_buffer_limit = Some(size.into());
1233 self
1234 }
1235
1236 pub fn replica_ignore_disk_write_errors(mut self, ignore: bool) -> Self {
1238 self.config.replica_ignore_disk_write_errors = Some(ignore);
1239 self
1240 }
1241
1242 pub fn replica_ignore_maxmemory(mut self, ignore: bool) -> Self {
1244 self.config.replica_ignore_maxmemory = Some(ignore);
1245 self
1246 }
1247
1248 pub fn replica_lazy_flush(mut self, enable: bool) -> Self {
1250 self.config.replica_lazy_flush = Some(enable);
1251 self
1252 }
1253
1254 pub fn replica_priority(mut self, priority: u32) -> Self {
1256 self.config.replica_priority = Some(priority);
1257 self
1258 }
1259
1260 pub fn replica_read_only(mut self, read_only: bool) -> Self {
1262 self.config.replica_read_only = Some(read_only);
1263 self
1264 }
1265
1266 pub fn replica_serve_stale_data(mut self, serve: bool) -> Self {
1268 self.config.replica_serve_stale_data = Some(serve);
1269 self
1270 }
1271
1272 pub fn min_replicas_to_write(mut self, n: u32) -> Self {
1274 self.config.min_replicas_to_write = Some(n);
1275 self
1276 }
1277
1278 pub fn min_replicas_max_lag(mut self, seconds: u32) -> Self {
1280 self.config.min_replicas_max_lag = Some(seconds);
1281 self
1282 }
1283
1284 pub fn password(mut self, password: impl Into<String>) -> Self {
1288 self.config.password = Some(password.into());
1289 self
1290 }
1291
1292 pub fn acl_file(mut self, path: impl Into<PathBuf>) -> Self {
1294 self.config.acl_file = Some(path.into());
1295 self
1296 }
1297
1298 pub fn cluster_enabled(mut self, enabled: bool) -> Self {
1302 self.config.cluster_enabled = enabled;
1303 self
1304 }
1305
1306 pub fn cluster_node_timeout(mut self, ms: u64) -> Self {
1308 self.config.cluster_node_timeout = Some(ms);
1309 self
1310 }
1311
1312 pub fn cluster_config_file(mut self, path: impl Into<PathBuf>) -> Self {
1314 self.config.cluster_config_file = Some(path.into());
1315 self
1316 }
1317
1318 pub fn cluster_require_full_coverage(mut self, require: bool) -> Self {
1320 self.config.cluster_require_full_coverage = Some(require);
1321 self
1322 }
1323
1324 pub fn cluster_allow_reads_when_down(mut self, allow: bool) -> Self {
1326 self.config.cluster_allow_reads_when_down = Some(allow);
1327 self
1328 }
1329
1330 pub fn cluster_allow_pubsubshard_when_down(mut self, allow: bool) -> Self {
1332 self.config.cluster_allow_pubsubshard_when_down = Some(allow);
1333 self
1334 }
1335
1336 pub fn cluster_allow_replica_migration(mut self, allow: bool) -> Self {
1338 self.config.cluster_allow_replica_migration = Some(allow);
1339 self
1340 }
1341
1342 pub fn cluster_migration_barrier(mut self, barrier: u32) -> Self {
1344 self.config.cluster_migration_barrier = Some(barrier);
1345 self
1346 }
1347
1348 pub fn cluster_replica_no_failover(mut self, no_failover: bool) -> Self {
1350 self.config.cluster_replica_no_failover = Some(no_failover);
1351 self
1352 }
1353
1354 pub fn cluster_replica_validity_factor(mut self, factor: u32) -> Self {
1356 self.config.cluster_replica_validity_factor = Some(factor);
1357 self
1358 }
1359
1360 pub fn cluster_announce_ip(mut self, ip: impl Into<String>) -> Self {
1362 self.config.cluster_announce_ip = Some(ip.into());
1363 self
1364 }
1365
1366 pub fn cluster_announce_port(mut self, port: u16) -> Self {
1368 self.config.cluster_announce_port = Some(port);
1369 self
1370 }
1371
1372 pub fn cluster_announce_bus_port(mut self, port: u16) -> Self {
1374 self.config.cluster_announce_bus_port = Some(port);
1375 self
1376 }
1377
1378 pub fn cluster_announce_tls_port(mut self, port: u16) -> Self {
1380 self.config.cluster_announce_tls_port = Some(port);
1381 self
1382 }
1383
1384 pub fn cluster_announce_hostname(mut self, hostname: impl Into<String>) -> Self {
1386 self.config.cluster_announce_hostname = Some(hostname.into());
1387 self
1388 }
1389
1390 pub fn cluster_announce_human_nodename(mut self, name: impl Into<String>) -> Self {
1392 self.config.cluster_announce_human_nodename = Some(name.into());
1393 self
1394 }
1395
1396 pub fn cluster_port(mut self, port: u16) -> Self {
1398 self.config.cluster_port = Some(port);
1399 self
1400 }
1401
1402 pub fn cluster_preferred_endpoint_type(mut self, endpoint_type: impl Into<String>) -> Self {
1404 self.config.cluster_preferred_endpoint_type = Some(endpoint_type.into());
1405 self
1406 }
1407
1408 pub fn cluster_link_sendbuf_limit(mut self, limit: u64) -> Self {
1410 self.config.cluster_link_sendbuf_limit = Some(limit);
1411 self
1412 }
1413
1414 pub fn cluster_compatibility_sample_ratio(mut self, ratio: u32) -> Self {
1416 self.config.cluster_compatibility_sample_ratio = Some(ratio);
1417 self
1418 }
1419
1420 pub fn cluster_slot_migration_handoff_max_lag_bytes(mut self, bytes: u64) -> Self {
1422 self.config.cluster_slot_migration_handoff_max_lag_bytes = Some(bytes);
1423 self
1424 }
1425
1426 pub fn cluster_slot_migration_write_pause_timeout(mut self, ms: u64) -> Self {
1428 self.config.cluster_slot_migration_write_pause_timeout = Some(ms);
1429 self
1430 }
1431
1432 pub fn cluster_slot_stats_enabled(mut self, enable: bool) -> Self {
1434 self.config.cluster_slot_stats_enabled = Some(enable);
1435 self
1436 }
1437
1438 pub fn hash_max_listpack_entries(mut self, n: u32) -> Self {
1442 self.config.hash_max_listpack_entries = Some(n);
1443 self
1444 }
1445
1446 pub fn hash_max_listpack_value(mut self, n: u32) -> Self {
1448 self.config.hash_max_listpack_value = Some(n);
1449 self
1450 }
1451
1452 pub fn list_max_listpack_size(mut self, n: i32) -> Self {
1457 self.config.list_max_listpack_size = Some(n);
1458 self
1459 }
1460
1461 pub fn list_compress_depth(mut self, n: u32) -> Self {
1465 self.config.list_compress_depth = Some(n);
1466 self
1467 }
1468
1469 pub fn set_max_intset_entries(mut self, n: u32) -> Self {
1471 self.config.set_max_intset_entries = Some(n);
1472 self
1473 }
1474
1475 pub fn set_max_listpack_entries(mut self, n: u32) -> Self {
1477 self.config.set_max_listpack_entries = Some(n);
1478 self
1479 }
1480
1481 pub fn set_max_listpack_value(mut self, n: u32) -> Self {
1483 self.config.set_max_listpack_value = Some(n);
1484 self
1485 }
1486
1487 pub fn zset_max_listpack_entries(mut self, n: u32) -> Self {
1489 self.config.zset_max_listpack_entries = Some(n);
1490 self
1491 }
1492
1493 pub fn zset_max_listpack_value(mut self, n: u32) -> Self {
1495 self.config.zset_max_listpack_value = Some(n);
1496 self
1497 }
1498
1499 pub fn hll_sparse_max_bytes(mut self, n: u32) -> Self {
1501 self.config.hll_sparse_max_bytes = Some(n);
1502 self
1503 }
1504
1505 pub fn stream_node_max_bytes(mut self, n: u32) -> Self {
1507 self.config.stream_node_max_bytes = Some(n);
1508 self
1509 }
1510
1511 pub fn stream_node_max_entries(mut self, n: u32) -> Self {
1513 self.config.stream_node_max_entries = Some(n);
1514 self
1515 }
1516
1517 pub fn stream_idmp_duration(mut self, ms: u64) -> Self {
1519 self.config.stream_idmp_duration = Some(ms);
1520 self
1521 }
1522
1523 pub fn stream_idmp_maxsize(mut self, n: u64) -> Self {
1525 self.config.stream_idmp_maxsize = Some(n);
1526 self
1527 }
1528
1529 pub fn loadmodule(mut self, path: impl Into<PathBuf>) -> Self {
1533 self.config.loadmodule.push(path.into());
1534 self
1535 }
1536
1537 pub fn hz(mut self, hz: u32) -> Self {
1541 self.config.hz = Some(hz);
1542 self
1543 }
1544
1545 pub fn io_threads(mut self, n: u32) -> Self {
1547 self.config.io_threads = Some(n);
1548 self
1549 }
1550
1551 pub fn io_threads_do_reads(mut self, enable: bool) -> Self {
1553 self.config.io_threads_do_reads = Some(enable);
1554 self
1555 }
1556
1557 pub fn notify_keyspace_events(mut self, events: impl Into<String>) -> Self {
1559 self.config.notify_keyspace_events = Some(events.into());
1560 self
1561 }
1562
1563 pub fn slowlog_log_slower_than(mut self, us: i64) -> Self {
1567 self.config.slowlog_log_slower_than = Some(us);
1568 self
1569 }
1570
1571 pub fn slowlog_max_len(mut self, n: u32) -> Self {
1573 self.config.slowlog_max_len = Some(n);
1574 self
1575 }
1576
1577 pub fn latency_monitor_threshold(mut self, ms: u64) -> Self {
1581 self.config.latency_monitor_threshold = Some(ms);
1582 self
1583 }
1584
1585 pub fn latency_tracking(mut self, enable: bool) -> Self {
1587 self.config.latency_tracking = Some(enable);
1588 self
1589 }
1590
1591 pub fn latency_tracking_info_percentiles(mut self, percentiles: impl Into<String>) -> Self {
1593 self.config.latency_tracking_info_percentiles = Some(percentiles.into());
1594 self
1595 }
1596
1597 pub fn activedefrag(mut self, enable: bool) -> Self {
1601 self.config.activedefrag = Some(enable);
1602 self
1603 }
1604
1605 pub fn active_defrag_ignore_bytes(mut self, bytes: impl Into<String>) -> Self {
1607 self.config.active_defrag_ignore_bytes = Some(bytes.into());
1608 self
1609 }
1610
1611 pub fn active_defrag_threshold_lower(mut self, pct: u32) -> Self {
1613 self.config.active_defrag_threshold_lower = Some(pct);
1614 self
1615 }
1616
1617 pub fn active_defrag_threshold_upper(mut self, pct: u32) -> Self {
1619 self.config.active_defrag_threshold_upper = Some(pct);
1620 self
1621 }
1622
1623 pub fn active_defrag_cycle_min(mut self, pct: u32) -> Self {
1625 self.config.active_defrag_cycle_min = Some(pct);
1626 self
1627 }
1628
1629 pub fn active_defrag_cycle_max(mut self, pct: u32) -> Self {
1631 self.config.active_defrag_cycle_max = Some(pct);
1632 self
1633 }
1634
1635 pub fn active_defrag_max_scan_fields(mut self, n: u32) -> Self {
1637 self.config.active_defrag_max_scan_fields = Some(n);
1638 self
1639 }
1640
1641 pub fn syslog_enabled(mut self, enable: bool) -> Self {
1645 self.config.syslog_enabled = Some(enable);
1646 self
1647 }
1648
1649 pub fn syslog_ident(mut self, ident: impl Into<String>) -> Self {
1651 self.config.syslog_ident = Some(ident.into());
1652 self
1653 }
1654
1655 pub fn syslog_facility(mut self, facility: impl Into<String>) -> Self {
1657 self.config.syslog_facility = Some(facility.into());
1658 self
1659 }
1660
1661 pub fn supervised(mut self, mode: impl Into<String>) -> Self {
1663 self.config.supervised = Some(mode.into());
1664 self
1665 }
1666
1667 pub fn always_show_logo(mut self, enable: bool) -> Self {
1669 self.config.always_show_logo = Some(enable);
1670 self
1671 }
1672
1673 pub fn set_proc_title(mut self, enable: bool) -> Self {
1675 self.config.set_proc_title = Some(enable);
1676 self
1677 }
1678
1679 pub fn proc_title_template(mut self, template: impl Into<String>) -> Self {
1681 self.config.proc_title_template = Some(template.into());
1682 self
1683 }
1684
1685 pub fn acl_pubsub_default(mut self, default: impl Into<String>) -> Self {
1689 self.config.acl_pubsub_default = Some(default.into());
1690 self
1691 }
1692
1693 pub fn acllog_max_len(mut self, n: u32) -> Self {
1695 self.config.acllog_max_len = Some(n);
1696 self
1697 }
1698
1699 pub fn enable_debug_command(mut self, mode: impl Into<String>) -> Self {
1701 self.config.enable_debug_command = Some(mode.into());
1702 self
1703 }
1704
1705 pub fn enable_module_command(mut self, mode: impl Into<String>) -> Self {
1707 self.config.enable_module_command = Some(mode.into());
1708 self
1709 }
1710
1711 pub fn enable_protected_configs(mut self, mode: impl Into<String>) -> Self {
1713 self.config.enable_protected_configs = Some(mode.into());
1714 self
1715 }
1716
1717 pub fn rename_command(
1719 mut self,
1720 command: impl Into<String>,
1721 new_name: impl Into<String>,
1722 ) -> Self {
1723 self.config
1724 .rename_command
1725 .push((command.into(), new_name.into()));
1726 self
1727 }
1728
1729 pub fn sanitize_dump_payload(mut self, mode: impl Into<String>) -> Self {
1731 self.config.sanitize_dump_payload = Some(mode.into());
1732 self
1733 }
1734
1735 pub fn hide_user_data_from_log(mut self, enable: bool) -> Self {
1737 self.config.hide_user_data_from_log = Some(enable);
1738 self
1739 }
1740
1741 pub fn bind_source_addr(mut self, addr: impl Into<String>) -> Self {
1745 self.config.bind_source_addr = Some(addr.into());
1746 self
1747 }
1748
1749 pub fn busy_reply_threshold(mut self, ms: u64) -> Self {
1751 self.config.busy_reply_threshold = Some(ms);
1752 self
1753 }
1754
1755 pub fn client_output_buffer_limit(mut self, limit: impl Into<String>) -> Self {
1757 self.config.client_output_buffer_limit.push(limit.into());
1758 self
1759 }
1760
1761 pub fn client_query_buffer_limit(mut self, limit: impl Into<String>) -> Self {
1763 self.config.client_query_buffer_limit = Some(limit.into());
1764 self
1765 }
1766
1767 pub fn proto_max_bulk_len(mut self, len: impl Into<String>) -> Self {
1769 self.config.proto_max_bulk_len = Some(len.into());
1770 self
1771 }
1772
1773 pub fn max_new_connections_per_cycle(mut self, n: u32) -> Self {
1775 self.config.max_new_connections_per_cycle = Some(n);
1776 self
1777 }
1778
1779 pub fn max_new_tls_connections_per_cycle(mut self, n: u32) -> Self {
1781 self.config.max_new_tls_connections_per_cycle = Some(n);
1782 self
1783 }
1784
1785 pub fn socket_mark_id(mut self, id: u32) -> Self {
1787 self.config.socket_mark_id = Some(id);
1788 self
1789 }
1790
1791 pub fn dbfilename(mut self, name: impl Into<String>) -> Self {
1795 self.config.dbfilename = Some(name.into());
1796 self
1797 }
1798
1799 pub fn rdbcompression(mut self, enable: bool) -> Self {
1801 self.config.rdbcompression = Some(enable);
1802 self
1803 }
1804
1805 pub fn rdbchecksum(mut self, enable: bool) -> Self {
1807 self.config.rdbchecksum = Some(enable);
1808 self
1809 }
1810
1811 pub fn rdb_save_incremental_fsync(mut self, enable: bool) -> Self {
1813 self.config.rdb_save_incremental_fsync = Some(enable);
1814 self
1815 }
1816
1817 pub fn rdb_del_sync_files(mut self, enable: bool) -> Self {
1819 self.config.rdb_del_sync_files = Some(enable);
1820 self
1821 }
1822
1823 pub fn stop_writes_on_bgsave_error(mut self, enable: bool) -> Self {
1825 self.config.stop_writes_on_bgsave_error = Some(enable);
1826 self
1827 }
1828
1829 pub fn shutdown_on_sigint(mut self, behavior: impl Into<String>) -> Self {
1833 self.config.shutdown_on_sigint = Some(behavior.into());
1834 self
1835 }
1836
1837 pub fn shutdown_on_sigterm(mut self, behavior: impl Into<String>) -> Self {
1839 self.config.shutdown_on_sigterm = Some(behavior.into());
1840 self
1841 }
1842
1843 pub fn shutdown_timeout(mut self, seconds: u32) -> Self {
1845 self.config.shutdown_timeout = Some(seconds);
1846 self
1847 }
1848
1849 pub fn activerehashing(mut self, enable: bool) -> Self {
1853 self.config.activerehashing = Some(enable);
1854 self
1855 }
1856
1857 pub fn crash_log_enabled(mut self, enable: bool) -> Self {
1859 self.config.crash_log_enabled = Some(enable);
1860 self
1861 }
1862
1863 pub fn crash_memcheck_enabled(mut self, enable: bool) -> Self {
1865 self.config.crash_memcheck_enabled = Some(enable);
1866 self
1867 }
1868
1869 pub fn disable_thp(mut self, enable: bool) -> Self {
1871 self.config.disable_thp = Some(enable);
1872 self
1873 }
1874
1875 pub fn dynamic_hz(mut self, enable: bool) -> Self {
1877 self.config.dynamic_hz = Some(enable);
1878 self
1879 }
1880
1881 pub fn ignore_warnings(mut self, warning: impl Into<String>) -> Self {
1883 self.config.ignore_warnings = Some(warning.into());
1884 self
1885 }
1886
1887 pub fn include(mut self, path: impl Into<PathBuf>) -> Self {
1889 self.config.include.push(path.into());
1890 self
1891 }
1892
1893 pub fn jemalloc_bg_thread(mut self, enable: bool) -> Self {
1895 self.config.jemalloc_bg_thread = Some(enable);
1896 self
1897 }
1898
1899 pub fn locale_collate(mut self, locale: impl Into<String>) -> Self {
1901 self.config.locale_collate = Some(locale.into());
1902 self
1903 }
1904
1905 pub fn lua_time_limit(mut self, ms: u64) -> Self {
1907 self.config.lua_time_limit = Some(ms);
1908 self
1909 }
1910
1911 pub fn oom_score_adj(mut self, mode: impl Into<String>) -> Self {
1913 self.config.oom_score_adj = Some(mode.into());
1914 self
1915 }
1916
1917 pub fn oom_score_adj_values(mut self, values: impl Into<String>) -> Self {
1919 self.config.oom_score_adj_values = Some(values.into());
1920 self
1921 }
1922
1923 pub fn propagation_error_behavior(mut self, behavior: impl Into<String>) -> Self {
1925 self.config.propagation_error_behavior = Some(behavior.into());
1926 self
1927 }
1928
1929 pub fn tracking_table_max_keys(mut self, n: u64) -> Self {
1931 self.config.tracking_table_max_keys = Some(n);
1932 self
1933 }
1934
1935 pub fn redis_server_bin(mut self, bin: impl Into<String>) -> Self {
1939 self.config.redis_server_bin = bin.into();
1940 self
1941 }
1942
1943 pub fn redis_cli_bin(mut self, bin: impl Into<String>) -> Self {
1945 self.config.redis_cli_bin = bin.into();
1946 self
1947 }
1948
1949 pub fn extra(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1951 self.config.extra.insert(key.into(), value.into());
1952 self
1953 }
1954
1955 pub async fn start(self) -> Result<RedisServerHandle> {
1960 if which::which(&self.config.redis_server_bin).is_err() {
1961 return Err(Error::BinaryNotFound {
1962 binary: self.config.redis_server_bin.clone(),
1963 });
1964 }
1965 if which::which(&self.config.redis_cli_bin).is_err() {
1966 return Err(Error::BinaryNotFound {
1967 binary: self.config.redis_cli_bin.clone(),
1968 });
1969 }
1970
1971 let node_dir = self.config.dir.join(format!("node-{}", self.config.port));
1972 fs::create_dir_all(&node_dir)?;
1973
1974 let conf_path = node_dir.join("redis.conf");
1975 let conf_content = self.generate_config(&node_dir);
1976 fs::write(&conf_path, conf_content)?;
1977
1978 let status = Command::new(&self.config.redis_server_bin)
1979 .arg(&conf_path)
1980 .stdout(std::process::Stdio::null())
1981 .stderr(std::process::Stdio::null())
1982 .status()
1983 .await?;
1984
1985 if !status.success() {
1986 return Err(Error::ServerStart {
1987 port: self.config.port,
1988 });
1989 }
1990
1991 let mut cli = RedisCli::new()
1992 .bin(&self.config.redis_cli_bin)
1993 .host(&self.config.bind)
1994 .port(self.config.port);
1995 if let Some(ref pw) = self.config.password {
1996 cli = cli.password(pw);
1997 }
1998
1999 cli.wait_for_ready(Duration::from_secs(10)).await?;
2000
2001 let pid_path = node_dir.join("redis.pid");
2002 let pid: u32 = fs::read_to_string(&pid_path)
2003 .map_err(Error::Io)?
2004 .trim()
2005 .parse()
2006 .map_err(|_| Error::ServerStart {
2007 port: self.config.port,
2008 })?;
2009
2010 Ok(RedisServerHandle {
2011 config: self.config,
2012 cli,
2013 pid,
2014 detached: false,
2015 })
2016 }
2017
2018 fn generate_config(&self, node_dir: &std::path::Path) -> String {
2019 let yn = |b: bool| if b { "yes" } else { "no" };
2020
2021 let mut conf = format!(
2022 "port {port}\n\
2023 bind {bind}\n\
2024 daemonize {daemonize}\n\
2025 pidfile {dir}/redis.pid\n\
2026 dir {dir}\n\
2027 loglevel {level}\n\
2028 protected-mode {protected}\n",
2029 port = self.config.port,
2030 bind = self.config.bind,
2031 daemonize = yn(self.config.daemonize),
2032 dir = node_dir.display(),
2033 level = self.config.loglevel,
2034 protected = yn(self.config.protected_mode),
2035 );
2036
2037 let logfile = self
2038 .config
2039 .logfile
2040 .as_deref()
2041 .map(str::to_owned)
2042 .unwrap_or_else(|| format!("{}/redis.log", node_dir.display()));
2043 conf.push_str(&format!("logfile {logfile}\n"));
2044
2045 if let Some(backlog) = self.config.tcp_backlog {
2047 conf.push_str(&format!("tcp-backlog {backlog}\n"));
2048 }
2049 if let Some(ref path) = self.config.unixsocket {
2050 conf.push_str(&format!("unixsocket {}\n", path.display()));
2051 }
2052 if let Some(perm) = self.config.unixsocketperm {
2053 conf.push_str(&format!("unixsocketperm {perm}\n"));
2054 }
2055 if let Some(t) = self.config.timeout {
2056 conf.push_str(&format!("timeout {t}\n"));
2057 }
2058 if let Some(ka) = self.config.tcp_keepalive {
2059 conf.push_str(&format!("tcp-keepalive {ka}\n"));
2060 }
2061
2062 if let Some(port) = self.config.tls_port {
2064 conf.push_str(&format!("tls-port {port}\n"));
2065 }
2066 if let Some(ref path) = self.config.tls_cert_file {
2067 conf.push_str(&format!("tls-cert-file {}\n", path.display()));
2068 }
2069 if let Some(ref path) = self.config.tls_key_file {
2070 conf.push_str(&format!("tls-key-file {}\n", path.display()));
2071 }
2072 if let Some(ref pass) = self.config.tls_key_file_pass {
2073 conf.push_str(&format!("tls-key-file-pass {pass}\n"));
2074 }
2075 if let Some(ref path) = self.config.tls_ca_cert_file {
2076 conf.push_str(&format!("tls-ca-cert-file {}\n", path.display()));
2077 }
2078 if let Some(ref path) = self.config.tls_ca_cert_dir {
2079 conf.push_str(&format!("tls-ca-cert-dir {}\n", path.display()));
2080 }
2081 if let Some(auth) = self.config.tls_auth_clients {
2082 conf.push_str(&format!("tls-auth-clients {}\n", yn(auth)));
2083 }
2084 if let Some(ref path) = self.config.tls_client_cert_file {
2085 conf.push_str(&format!("tls-client-cert-file {}\n", path.display()));
2086 }
2087 if let Some(ref path) = self.config.tls_client_key_file {
2088 conf.push_str(&format!("tls-client-key-file {}\n", path.display()));
2089 }
2090 if let Some(ref pass) = self.config.tls_client_key_file_pass {
2091 conf.push_str(&format!("tls-client-key-file-pass {pass}\n"));
2092 }
2093 if let Some(ref path) = self.config.tls_dh_params_file {
2094 conf.push_str(&format!("tls-dh-params-file {}\n", path.display()));
2095 }
2096 if let Some(ref ciphers) = self.config.tls_ciphers {
2097 conf.push_str(&format!("tls-ciphers {ciphers}\n"));
2098 }
2099 if let Some(ref suites) = self.config.tls_ciphersuites {
2100 conf.push_str(&format!("tls-ciphersuites {suites}\n"));
2101 }
2102 if let Some(ref protocols) = self.config.tls_protocols {
2103 conf.push_str(&format!("tls-protocols {protocols}\n"));
2104 }
2105 if let Some(v) = self.config.tls_prefer_server_ciphers {
2106 conf.push_str(&format!("tls-prefer-server-ciphers {}\n", yn(v)));
2107 }
2108 if let Some(v) = self.config.tls_session_caching {
2109 conf.push_str(&format!("tls-session-caching {}\n", yn(v)));
2110 }
2111 if let Some(size) = self.config.tls_session_cache_size {
2112 conf.push_str(&format!("tls-session-cache-size {size}\n"));
2113 }
2114 if let Some(timeout) = self.config.tls_session_cache_timeout {
2115 conf.push_str(&format!("tls-session-cache-timeout {timeout}\n"));
2116 }
2117 if let Some(v) = self.config.tls_replication {
2118 conf.push_str(&format!("tls-replication {}\n", yn(v)));
2119 }
2120 if let Some(v) = self.config.tls_cluster {
2121 conf.push_str(&format!("tls-cluster {}\n", yn(v)));
2122 }
2123
2124 if let Some(n) = self.config.databases {
2126 conf.push_str(&format!("databases {n}\n"));
2127 }
2128
2129 if let Some(ref limit) = self.config.maxmemory {
2131 conf.push_str(&format!("maxmemory {limit}\n"));
2132 }
2133 if let Some(ref policy) = self.config.maxmemory_policy {
2134 conf.push_str(&format!("maxmemory-policy {policy}\n"));
2135 }
2136 if let Some(n) = self.config.maxmemory_samples {
2137 conf.push_str(&format!("maxmemory-samples {n}\n"));
2138 }
2139 if let Some(ref limit) = self.config.maxmemory_clients {
2140 conf.push_str(&format!("maxmemory-clients {limit}\n"));
2141 }
2142 if let Some(n) = self.config.maxmemory_eviction_tenacity {
2143 conf.push_str(&format!("maxmemory-eviction-tenacity {n}\n"));
2144 }
2145 if let Some(n) = self.config.maxclients {
2146 conf.push_str(&format!("maxclients {n}\n"));
2147 }
2148 if let Some(n) = self.config.lfu_log_factor {
2149 conf.push_str(&format!("lfu-log-factor {n}\n"));
2150 }
2151 if let Some(n) = self.config.lfu_decay_time {
2152 conf.push_str(&format!("lfu-decay-time {n}\n"));
2153 }
2154 if let Some(n) = self.config.active_expire_effort {
2155 conf.push_str(&format!("active-expire-effort {n}\n"));
2156 }
2157
2158 if let Some(v) = self.config.lazyfree_lazy_eviction {
2160 conf.push_str(&format!("lazyfree-lazy-eviction {}\n", yn(v)));
2161 }
2162 if let Some(v) = self.config.lazyfree_lazy_expire {
2163 conf.push_str(&format!("lazyfree-lazy-expire {}\n", yn(v)));
2164 }
2165 if let Some(v) = self.config.lazyfree_lazy_server_del {
2166 conf.push_str(&format!("lazyfree-lazy-server-del {}\n", yn(v)));
2167 }
2168 if let Some(v) = self.config.lazyfree_lazy_user_del {
2169 conf.push_str(&format!("lazyfree-lazy-user-del {}\n", yn(v)));
2170 }
2171 if let Some(v) = self.config.lazyfree_lazy_user_flush {
2172 conf.push_str(&format!("lazyfree-lazy-user-flush {}\n", yn(v)));
2173 }
2174
2175 match &self.config.save {
2177 SavePolicy::Disabled => conf.push_str("save \"\"\n"),
2178 SavePolicy::Default => {}
2179 SavePolicy::Custom(pairs) => {
2180 for (secs, changes) in pairs {
2181 conf.push_str(&format!("save {secs} {changes}\n"));
2182 }
2183 }
2184 }
2185 if self.config.appendonly {
2186 conf.push_str("appendonly yes\n");
2187 }
2188 if let Some(ref policy) = self.config.appendfsync {
2189 conf.push_str(&format!("appendfsync {policy}\n"));
2190 }
2191 if let Some(ref name) = self.config.appendfilename {
2192 conf.push_str(&format!("appendfilename \"{name}\"\n"));
2193 }
2194 if let Some(ref name) = self.config.appenddirname {
2195 conf.push_str(&format!("appenddirname \"{}\"\n", name.display()));
2196 }
2197 if let Some(v) = self.config.aof_use_rdb_preamble {
2198 conf.push_str(&format!("aof-use-rdb-preamble {}\n", yn(v)));
2199 }
2200 if let Some(v) = self.config.aof_load_truncated {
2201 conf.push_str(&format!("aof-load-truncated {}\n", yn(v)));
2202 }
2203 if let Some(ref size) = self.config.aof_load_corrupt_tail_max_size {
2204 conf.push_str(&format!("aof-load-corrupt-tail-max-size {size}\n"));
2205 }
2206 if let Some(v) = self.config.aof_rewrite_incremental_fsync {
2207 conf.push_str(&format!("aof-rewrite-incremental-fsync {}\n", yn(v)));
2208 }
2209 if let Some(v) = self.config.aof_timestamp_enabled {
2210 conf.push_str(&format!("aof-timestamp-enabled {}\n", yn(v)));
2211 }
2212 if let Some(pct) = self.config.auto_aof_rewrite_percentage {
2213 conf.push_str(&format!("auto-aof-rewrite-percentage {pct}\n"));
2214 }
2215 if let Some(ref size) = self.config.auto_aof_rewrite_min_size {
2216 conf.push_str(&format!("auto-aof-rewrite-min-size {size}\n"));
2217 }
2218 if let Some(v) = self.config.no_appendfsync_on_rewrite {
2219 conf.push_str(&format!("no-appendfsync-on-rewrite {}\n", yn(v)));
2220 }
2221
2222 if let Some((ref host, port)) = self.config.replicaof {
2224 conf.push_str(&format!("replicaof {host} {port}\n"));
2225 }
2226 if let Some(ref pw) = self.config.masterauth {
2227 conf.push_str(&format!("masterauth {pw}\n"));
2228 }
2229 if let Some(ref user) = self.config.masteruser {
2230 conf.push_str(&format!("masteruser {user}\n"));
2231 }
2232 if let Some(ref size) = self.config.repl_backlog_size {
2233 conf.push_str(&format!("repl-backlog-size {size}\n"));
2234 }
2235 if let Some(ttl) = self.config.repl_backlog_ttl {
2236 conf.push_str(&format!("repl-backlog-ttl {ttl}\n"));
2237 }
2238 if let Some(v) = self.config.repl_disable_tcp_nodelay {
2239 conf.push_str(&format!("repl-disable-tcp-nodelay {}\n", yn(v)));
2240 }
2241 if let Some(ref policy) = self.config.repl_diskless_load {
2242 conf.push_str(&format!("repl-diskless-load {policy}\n"));
2243 }
2244 if let Some(v) = self.config.repl_diskless_sync {
2245 conf.push_str(&format!("repl-diskless-sync {}\n", yn(v)));
2246 }
2247 if let Some(delay) = self.config.repl_diskless_sync_delay {
2248 conf.push_str(&format!("repl-diskless-sync-delay {delay}\n"));
2249 }
2250 if let Some(n) = self.config.repl_diskless_sync_max_replicas {
2251 conf.push_str(&format!("repl-diskless-sync-max-replicas {n}\n"));
2252 }
2253 if let Some(period) = self.config.repl_ping_replica_period {
2254 conf.push_str(&format!("repl-ping-replica-period {period}\n"));
2255 }
2256 if let Some(t) = self.config.repl_timeout {
2257 conf.push_str(&format!("repl-timeout {t}\n"));
2258 }
2259 if let Some(ref ip) = self.config.replica_announce_ip {
2260 conf.push_str(&format!("replica-announce-ip {ip}\n"));
2261 }
2262 if let Some(port) = self.config.replica_announce_port {
2263 conf.push_str(&format!("replica-announce-port {port}\n"));
2264 }
2265 if let Some(v) = self.config.replica_announced {
2266 conf.push_str(&format!("replica-announced {}\n", yn(v)));
2267 }
2268 if let Some(ref size) = self.config.replica_full_sync_buffer_limit {
2269 conf.push_str(&format!("replica-full-sync-buffer-limit {size}\n"));
2270 }
2271 if let Some(v) = self.config.replica_ignore_disk_write_errors {
2272 conf.push_str(&format!("replica-ignore-disk-write-errors {}\n", yn(v)));
2273 }
2274 if let Some(v) = self.config.replica_ignore_maxmemory {
2275 conf.push_str(&format!("replica-ignore-maxmemory {}\n", yn(v)));
2276 }
2277 if let Some(v) = self.config.replica_lazy_flush {
2278 conf.push_str(&format!("replica-lazy-flush {}\n", yn(v)));
2279 }
2280 if let Some(priority) = self.config.replica_priority {
2281 conf.push_str(&format!("replica-priority {priority}\n"));
2282 }
2283 if let Some(v) = self.config.replica_read_only {
2284 conf.push_str(&format!("replica-read-only {}\n", yn(v)));
2285 }
2286 if let Some(v) = self.config.replica_serve_stale_data {
2287 conf.push_str(&format!("replica-serve-stale-data {}\n", yn(v)));
2288 }
2289 if let Some(n) = self.config.min_replicas_to_write {
2290 conf.push_str(&format!("min-replicas-to-write {n}\n"));
2291 }
2292 if let Some(lag) = self.config.min_replicas_max_lag {
2293 conf.push_str(&format!("min-replicas-max-lag {lag}\n"));
2294 }
2295
2296 if let Some(ref pw) = self.config.password {
2298 conf.push_str(&format!("requirepass {pw}\n"));
2299 }
2300 if let Some(ref path) = self.config.acl_file {
2301 conf.push_str(&format!("aclfile {}\n", path.display()));
2302 }
2303
2304 if self.config.cluster_enabled {
2306 conf.push_str("cluster-enabled yes\n");
2307 if let Some(ref path) = self.config.cluster_config_file {
2308 conf.push_str(&format!("cluster-config-file {}\n", path.display()));
2309 } else {
2310 conf.push_str(&format!(
2311 "cluster-config-file {}/nodes.conf\n",
2312 node_dir.display()
2313 ));
2314 }
2315 if let Some(timeout) = self.config.cluster_node_timeout {
2316 conf.push_str(&format!("cluster-node-timeout {timeout}\n"));
2317 }
2318 if let Some(v) = self.config.cluster_require_full_coverage {
2319 conf.push_str(&format!("cluster-require-full-coverage {}\n", yn(v)));
2320 }
2321 if let Some(v) = self.config.cluster_allow_reads_when_down {
2322 conf.push_str(&format!("cluster-allow-reads-when-down {}\n", yn(v)));
2323 }
2324 if let Some(v) = self.config.cluster_allow_pubsubshard_when_down {
2325 conf.push_str(&format!("cluster-allow-pubsubshard-when-down {}\n", yn(v)));
2326 }
2327 if let Some(v) = self.config.cluster_allow_replica_migration {
2328 conf.push_str(&format!("cluster-allow-replica-migration {}\n", yn(v)));
2329 }
2330 if let Some(barrier) = self.config.cluster_migration_barrier {
2331 conf.push_str(&format!("cluster-migration-barrier {barrier}\n"));
2332 }
2333 if let Some(v) = self.config.cluster_replica_no_failover {
2334 conf.push_str(&format!("cluster-replica-no-failover {}\n", yn(v)));
2335 }
2336 if let Some(factor) = self.config.cluster_replica_validity_factor {
2337 conf.push_str(&format!("cluster-replica-validity-factor {factor}\n"));
2338 }
2339 if let Some(ref ip) = self.config.cluster_announce_ip {
2340 conf.push_str(&format!("cluster-announce-ip {ip}\n"));
2341 }
2342 if let Some(port) = self.config.cluster_announce_port {
2343 conf.push_str(&format!("cluster-announce-port {port}\n"));
2344 }
2345 if let Some(port) = self.config.cluster_announce_bus_port {
2346 conf.push_str(&format!("cluster-announce-bus-port {port}\n"));
2347 }
2348 if let Some(port) = self.config.cluster_announce_tls_port {
2349 conf.push_str(&format!("cluster-announce-tls-port {port}\n"));
2350 }
2351 if let Some(ref hostname) = self.config.cluster_announce_hostname {
2352 conf.push_str(&format!("cluster-announce-hostname {hostname}\n"));
2353 }
2354 if let Some(ref name) = self.config.cluster_announce_human_nodename {
2355 conf.push_str(&format!("cluster-announce-human-nodename {name}\n"));
2356 }
2357 if let Some(port) = self.config.cluster_port {
2358 conf.push_str(&format!("cluster-port {port}\n"));
2359 }
2360 if let Some(ref endpoint_type) = self.config.cluster_preferred_endpoint_type {
2361 conf.push_str(&format!(
2362 "cluster-preferred-endpoint-type {endpoint_type}\n"
2363 ));
2364 }
2365 if let Some(limit) = self.config.cluster_link_sendbuf_limit {
2366 conf.push_str(&format!("cluster-link-sendbuf-limit {limit}\n"));
2367 }
2368 if let Some(ratio) = self.config.cluster_compatibility_sample_ratio {
2369 conf.push_str(&format!("cluster-compatibility-sample-ratio {ratio}\n"));
2370 }
2371 if let Some(bytes) = self.config.cluster_slot_migration_handoff_max_lag_bytes {
2372 conf.push_str(&format!(
2373 "cluster-slot-migration-handoff-max-lag-bytes {bytes}\n"
2374 ));
2375 }
2376 if let Some(ms) = self.config.cluster_slot_migration_write_pause_timeout {
2377 conf.push_str(&format!(
2378 "cluster-slot-migration-write-pause-timeout {ms}\n"
2379 ));
2380 }
2381 if let Some(v) = self.config.cluster_slot_stats_enabled {
2382 conf.push_str(&format!("cluster-slot-stats-enabled {}\n", yn(v)));
2383 }
2384 }
2385
2386 if let Some(n) = self.config.hash_max_listpack_entries {
2388 conf.push_str(&format!("hash-max-listpack-entries {n}\n"));
2389 }
2390 if let Some(n) = self.config.hash_max_listpack_value {
2391 conf.push_str(&format!("hash-max-listpack-value {n}\n"));
2392 }
2393 if let Some(n) = self.config.list_max_listpack_size {
2394 conf.push_str(&format!("list-max-listpack-size {n}\n"));
2395 }
2396 if let Some(n) = self.config.list_compress_depth {
2397 conf.push_str(&format!("list-compress-depth {n}\n"));
2398 }
2399 if let Some(n) = self.config.set_max_intset_entries {
2400 conf.push_str(&format!("set-max-intset-entries {n}\n"));
2401 }
2402 if let Some(n) = self.config.set_max_listpack_entries {
2403 conf.push_str(&format!("set-max-listpack-entries {n}\n"));
2404 }
2405 if let Some(n) = self.config.set_max_listpack_value {
2406 conf.push_str(&format!("set-max-listpack-value {n}\n"));
2407 }
2408 if let Some(n) = self.config.zset_max_listpack_entries {
2409 conf.push_str(&format!("zset-max-listpack-entries {n}\n"));
2410 }
2411 if let Some(n) = self.config.zset_max_listpack_value {
2412 conf.push_str(&format!("zset-max-listpack-value {n}\n"));
2413 }
2414 if let Some(n) = self.config.hll_sparse_max_bytes {
2415 conf.push_str(&format!("hll-sparse-max-bytes {n}\n"));
2416 }
2417 if let Some(n) = self.config.stream_node_max_bytes {
2418 conf.push_str(&format!("stream-node-max-bytes {n}\n"));
2419 }
2420 if let Some(n) = self.config.stream_node_max_entries {
2421 conf.push_str(&format!("stream-node-max-entries {n}\n"));
2422 }
2423 if let Some(ms) = self.config.stream_idmp_duration {
2424 conf.push_str(&format!("stream-idmp-duration {ms}\n"));
2425 }
2426 if let Some(n) = self.config.stream_idmp_maxsize {
2427 conf.push_str(&format!("stream-idmp-maxsize {n}\n"));
2428 }
2429
2430 for path in &self.config.loadmodule {
2432 conf.push_str(&format!("loadmodule {}\n", path.display()));
2433 }
2434
2435 if let Some(hz) = self.config.hz {
2437 conf.push_str(&format!("hz {hz}\n"));
2438 }
2439 if let Some(n) = self.config.io_threads {
2440 conf.push_str(&format!("io-threads {n}\n"));
2441 }
2442 if let Some(enable) = self.config.io_threads_do_reads {
2443 conf.push_str(&format!("io-threads-do-reads {}\n", yn(enable)));
2444 }
2445 if let Some(ref events) = self.config.notify_keyspace_events {
2446 conf.push_str(&format!("notify-keyspace-events {events}\n"));
2447 }
2448
2449 if let Some(us) = self.config.slowlog_log_slower_than {
2451 conf.push_str(&format!("slowlog-log-slower-than {us}\n"));
2452 }
2453 if let Some(n) = self.config.slowlog_max_len {
2454 conf.push_str(&format!("slowlog-max-len {n}\n"));
2455 }
2456
2457 if let Some(ms) = self.config.latency_monitor_threshold {
2459 conf.push_str(&format!("latency-monitor-threshold {ms}\n"));
2460 }
2461 if let Some(enable) = self.config.latency_tracking {
2462 conf.push_str(&format!("latency-tracking {}\n", yn(enable)));
2463 }
2464 if let Some(ref pcts) = self.config.latency_tracking_info_percentiles {
2465 conf.push_str(&format!("latency-tracking-info-percentiles \"{pcts}\"\n"));
2466 }
2467
2468 if let Some(enable) = self.config.activedefrag {
2470 conf.push_str(&format!("activedefrag {}\n", yn(enable)));
2471 }
2472 if let Some(ref bytes) = self.config.active_defrag_ignore_bytes {
2473 conf.push_str(&format!("active-defrag-ignore-bytes {bytes}\n"));
2474 }
2475 if let Some(pct) = self.config.active_defrag_threshold_lower {
2476 conf.push_str(&format!("active-defrag-threshold-lower {pct}\n"));
2477 }
2478 if let Some(pct) = self.config.active_defrag_threshold_upper {
2479 conf.push_str(&format!("active-defrag-threshold-upper {pct}\n"));
2480 }
2481 if let Some(pct) = self.config.active_defrag_cycle_min {
2482 conf.push_str(&format!("active-defrag-cycle-min {pct}\n"));
2483 }
2484 if let Some(pct) = self.config.active_defrag_cycle_max {
2485 conf.push_str(&format!("active-defrag-cycle-max {pct}\n"));
2486 }
2487 if let Some(n) = self.config.active_defrag_max_scan_fields {
2488 conf.push_str(&format!("active-defrag-max-scan-fields {n}\n"));
2489 }
2490
2491 if let Some(enable) = self.config.syslog_enabled {
2493 conf.push_str(&format!("syslog-enabled {}\n", yn(enable)));
2494 }
2495 if let Some(ref ident) = self.config.syslog_ident {
2496 conf.push_str(&format!("syslog-ident {ident}\n"));
2497 }
2498 if let Some(ref facility) = self.config.syslog_facility {
2499 conf.push_str(&format!("syslog-facility {facility}\n"));
2500 }
2501 if let Some(ref mode) = self.config.supervised {
2502 conf.push_str(&format!("supervised {mode}\n"));
2503 }
2504 if let Some(enable) = self.config.always_show_logo {
2505 conf.push_str(&format!("always-show-logo {}\n", yn(enable)));
2506 }
2507 if let Some(enable) = self.config.set_proc_title {
2508 conf.push_str(&format!("set-proc-title {}\n", yn(enable)));
2509 }
2510 if let Some(ref template) = self.config.proc_title_template {
2511 conf.push_str(&format!("proc-title-template \"{template}\"\n"));
2512 }
2513
2514 if let Some(ref default) = self.config.acl_pubsub_default {
2516 conf.push_str(&format!("acl-pubsub-default {default}\n"));
2517 }
2518 if let Some(n) = self.config.acllog_max_len {
2519 conf.push_str(&format!("acllog-max-len {n}\n"));
2520 }
2521 if let Some(ref mode) = self.config.enable_debug_command {
2522 conf.push_str(&format!("enable-debug-command {mode}\n"));
2523 }
2524 if let Some(ref mode) = self.config.enable_module_command {
2525 conf.push_str(&format!("enable-module-command {mode}\n"));
2526 }
2527 if let Some(ref mode) = self.config.enable_protected_configs {
2528 conf.push_str(&format!("enable-protected-configs {mode}\n"));
2529 }
2530 for (cmd, new_name) in &self.config.rename_command {
2531 conf.push_str(&format!("rename-command {cmd} \"{new_name}\"\n"));
2532 }
2533 if let Some(ref mode) = self.config.sanitize_dump_payload {
2534 conf.push_str(&format!("sanitize-dump-payload {mode}\n"));
2535 }
2536 if let Some(enable) = self.config.hide_user_data_from_log {
2537 conf.push_str(&format!("hide-user-data-from-log {}\n", yn(enable)));
2538 }
2539
2540 if let Some(ref addr) = self.config.bind_source_addr {
2542 conf.push_str(&format!("bind-source-addr {addr}\n"));
2543 }
2544 if let Some(ms) = self.config.busy_reply_threshold {
2545 conf.push_str(&format!("busy-reply-threshold {ms}\n"));
2546 }
2547 for limit in &self.config.client_output_buffer_limit {
2548 conf.push_str(&format!("client-output-buffer-limit {limit}\n"));
2549 }
2550 if let Some(ref limit) = self.config.client_query_buffer_limit {
2551 conf.push_str(&format!("client-query-buffer-limit {limit}\n"));
2552 }
2553 if let Some(ref len) = self.config.proto_max_bulk_len {
2554 conf.push_str(&format!("proto-max-bulk-len {len}\n"));
2555 }
2556 if let Some(n) = self.config.max_new_connections_per_cycle {
2557 conf.push_str(&format!("max-new-connections-per-cycle {n}\n"));
2558 }
2559 if let Some(n) = self.config.max_new_tls_connections_per_cycle {
2560 conf.push_str(&format!("max-new-tls-connections-per-cycle {n}\n"));
2561 }
2562 if let Some(id) = self.config.socket_mark_id {
2563 conf.push_str(&format!("socket-mark-id {id}\n"));
2564 }
2565
2566 if let Some(ref name) = self.config.dbfilename {
2568 conf.push_str(&format!("dbfilename {name}\n"));
2569 }
2570 if let Some(enable) = self.config.rdbcompression {
2571 conf.push_str(&format!("rdbcompression {}\n", yn(enable)));
2572 }
2573 if let Some(enable) = self.config.rdbchecksum {
2574 conf.push_str(&format!("rdbchecksum {}\n", yn(enable)));
2575 }
2576 if let Some(enable) = self.config.rdb_save_incremental_fsync {
2577 conf.push_str(&format!("rdb-save-incremental-fsync {}\n", yn(enable)));
2578 }
2579 if let Some(enable) = self.config.rdb_del_sync_files {
2580 conf.push_str(&format!("rdb-del-sync-files {}\n", yn(enable)));
2581 }
2582 if let Some(enable) = self.config.stop_writes_on_bgsave_error {
2583 conf.push_str(&format!("stop-writes-on-bgsave-error {}\n", yn(enable)));
2584 }
2585
2586 if let Some(ref behavior) = self.config.shutdown_on_sigint {
2588 conf.push_str(&format!("shutdown-on-sigint {behavior}\n"));
2589 }
2590 if let Some(ref behavior) = self.config.shutdown_on_sigterm {
2591 conf.push_str(&format!("shutdown-on-sigterm {behavior}\n"));
2592 }
2593 if let Some(seconds) = self.config.shutdown_timeout {
2594 conf.push_str(&format!("shutdown-timeout {seconds}\n"));
2595 }
2596
2597 if let Some(enable) = self.config.activerehashing {
2599 conf.push_str(&format!("activerehashing {}\n", yn(enable)));
2600 }
2601 if let Some(enable) = self.config.crash_log_enabled {
2602 conf.push_str(&format!("crash-log-enabled {}\n", yn(enable)));
2603 }
2604 if let Some(enable) = self.config.crash_memcheck_enabled {
2605 conf.push_str(&format!("crash-memcheck-enabled {}\n", yn(enable)));
2606 }
2607 if let Some(enable) = self.config.disable_thp {
2608 conf.push_str(&format!("disable-thp {}\n", yn(enable)));
2609 }
2610 if let Some(enable) = self.config.dynamic_hz {
2611 conf.push_str(&format!("dynamic-hz {}\n", yn(enable)));
2612 }
2613 if let Some(ref warning) = self.config.ignore_warnings {
2614 conf.push_str(&format!("ignore-warnings {warning}\n"));
2615 }
2616 for path in &self.config.include {
2617 conf.push_str(&format!("include {}\n", path.display()));
2618 }
2619 if let Some(enable) = self.config.jemalloc_bg_thread {
2620 conf.push_str(&format!("jemalloc-bg-thread {}\n", yn(enable)));
2621 }
2622 if let Some(ref locale) = self.config.locale_collate {
2623 conf.push_str(&format!("locale-collate {locale}\n"));
2624 }
2625 if let Some(ms) = self.config.lua_time_limit {
2626 conf.push_str(&format!("lua-time-limit {ms}\n"));
2627 }
2628 if let Some(ref mode) = self.config.oom_score_adj {
2629 conf.push_str(&format!("oom-score-adj {mode}\n"));
2630 }
2631 if let Some(ref values) = self.config.oom_score_adj_values {
2632 conf.push_str(&format!("oom-score-adj-values {values}\n"));
2633 }
2634 if let Some(ref behavior) = self.config.propagation_error_behavior {
2635 conf.push_str(&format!("propagation-error-behavior {behavior}\n"));
2636 }
2637 if let Some(n) = self.config.tracking_table_max_keys {
2638 conf.push_str(&format!("tracking-table-max-keys {n}\n"));
2639 }
2640
2641 for (key, value) in &self.config.extra {
2643 conf.push_str(&format!("{key} {value}\n"));
2644 }
2645
2646 conf
2647 }
2648}
2649
2650impl Default for RedisServer {
2651 fn default() -> Self {
2652 Self::new()
2653 }
2654}
2655
2656pub struct RedisServerHandle {
2658 config: RedisServerConfig,
2659 cli: RedisCli,
2660 pid: u32,
2661 detached: bool,
2662}
2663
2664impl RedisServerHandle {
2665 pub fn addr(&self) -> String {
2667 format!("{}:{}", self.config.bind, self.config.port)
2668 }
2669
2670 pub fn port(&self) -> u16 {
2672 self.config.port
2673 }
2674
2675 pub fn host(&self) -> &str {
2677 &self.config.bind
2678 }
2679
2680 pub fn pid(&self) -> u32 {
2682 self.pid
2683 }
2684
2685 pub async fn is_alive(&self) -> bool {
2687 self.cli.ping().await
2688 }
2689
2690 pub fn cli(&self) -> &RedisCli {
2692 &self.cli
2693 }
2694
2695 pub async fn run(&self, args: &[&str]) -> Result<String> {
2697 self.cli.run(args).await
2698 }
2699
2700 pub fn detach(mut self) {
2702 self.detached = true;
2703 }
2704
2705 pub fn stop(&self) {
2707 self.cli.shutdown();
2708 }
2709
2710 pub async fn wait_for_ready(&self, timeout: Duration) -> Result<()> {
2712 self.cli.wait_for_ready(timeout).await
2713 }
2714}
2715
2716impl Drop for RedisServerHandle {
2717 fn drop(&mut self) {
2718 if !self.detached {
2719 self.stop();
2720 }
2721 }
2722}
2723
2724#[cfg(test)]
2725mod tests {
2726 use super::*;
2727
2728 #[test]
2729 fn default_config() {
2730 let s = RedisServer::new();
2731 assert_eq!(s.config.port, 6379);
2732 assert_eq!(s.config.bind, "127.0.0.1");
2733 assert!(matches!(s.config.save, SavePolicy::Disabled));
2734 }
2735
2736 #[test]
2737 fn builder_chain() {
2738 let s = RedisServer::new()
2739 .port(6400)
2740 .bind("0.0.0.0")
2741 .save(true)
2742 .appendonly(true)
2743 .password("secret")
2744 .logfile("/tmp/redis.log")
2745 .loglevel(LogLevel::Warning)
2746 .extra("maxmemory", "100mb");
2747
2748 assert_eq!(s.config.port, 6400);
2749 assert_eq!(s.config.bind, "0.0.0.0");
2750 assert!(matches!(s.config.save, SavePolicy::Default));
2751 assert!(s.config.appendonly);
2752 assert_eq!(s.config.password.as_deref(), Some("secret"));
2753 assert_eq!(s.config.logfile.as_deref(), Some("/tmp/redis.log"));
2754 assert_eq!(s.config.extra.get("maxmemory").unwrap(), "100mb");
2755 }
2756
2757 #[test]
2758 fn save_schedule() {
2759 let s = RedisServer::new().save_schedule(vec![(900, 1), (300, 10)]);
2760 match &s.config.save {
2761 SavePolicy::Custom(pairs) => {
2762 assert_eq!(pairs, &[(900, 1), (300, 10)]);
2763 }
2764 _ => panic!("expected SavePolicy::Custom"),
2765 }
2766 }
2767
2768 #[test]
2769 fn aof_tuning() {
2770 let s = RedisServer::new()
2771 .appendonly(true)
2772 .appendfsync(AppendFsync::Always)
2773 .appendfilename("my.aof")
2774 .aof_use_rdb_preamble(true)
2775 .auto_aof_rewrite_percentage(100)
2776 .auto_aof_rewrite_min_size("64mb")
2777 .no_appendfsync_on_rewrite(true);
2778
2779 assert!(s.config.appendonly);
2780 assert!(matches!(s.config.appendfsync, Some(AppendFsync::Always)));
2781 assert_eq!(s.config.appendfilename.as_deref(), Some("my.aof"));
2782 assert_eq!(s.config.aof_use_rdb_preamble, Some(true));
2783 assert_eq!(s.config.auto_aof_rewrite_percentage, Some(100));
2784 assert_eq!(s.config.auto_aof_rewrite_min_size.as_deref(), Some("64mb"));
2785 assert_eq!(s.config.no_appendfsync_on_rewrite, Some(true));
2786 }
2787
2788 #[test]
2789 fn memory_eviction_and_lazyfree() {
2790 let s = RedisServer::new()
2791 .maxmemory("256mb")
2792 .maxmemory_policy("allkeys-lfu")
2793 .maxmemory_samples(10)
2794 .maxmemory_clients("0")
2795 .maxmemory_eviction_tenacity(50)
2796 .lfu_log_factor(10)
2797 .lfu_decay_time(1)
2798 .active_expire_effort(25)
2799 .lazyfree_lazy_eviction(true)
2800 .lazyfree_lazy_expire(true)
2801 .lazyfree_lazy_server_del(true)
2802 .lazyfree_lazy_user_del(false)
2803 .lazyfree_lazy_user_flush(true);
2804
2805 assert_eq!(s.config.maxmemory.as_deref(), Some("256mb"));
2806 assert_eq!(s.config.maxmemory_policy.as_deref(), Some("allkeys-lfu"));
2807 assert_eq!(s.config.maxmemory_samples, Some(10));
2808 assert_eq!(s.config.maxmemory_clients.as_deref(), Some("0"));
2809 assert_eq!(s.config.maxmemory_eviction_tenacity, Some(50));
2810 assert_eq!(s.config.lfu_log_factor, Some(10));
2811 assert_eq!(s.config.lfu_decay_time, Some(1));
2812 assert_eq!(s.config.active_expire_effort, Some(25));
2813 assert_eq!(s.config.lazyfree_lazy_eviction, Some(true));
2814 assert_eq!(s.config.lazyfree_lazy_expire, Some(true));
2815 assert_eq!(s.config.lazyfree_lazy_server_del, Some(true));
2816 assert_eq!(s.config.lazyfree_lazy_user_del, Some(false));
2817 assert_eq!(s.config.lazyfree_lazy_user_flush, Some(true));
2818 }
2819
2820 #[test]
2821 fn replication_tuning() {
2822 let s = RedisServer::new()
2823 .replicaof("127.0.0.1", 6379)
2824 .masterauth("secret")
2825 .masteruser("repl-user")
2826 .repl_backlog_size("1mb")
2827 .repl_backlog_ttl(3600)
2828 .repl_disable_tcp_nodelay(true)
2829 .repl_diskless_load(ReplDisklessLoad::Swapdb)
2830 .repl_diskless_sync(true)
2831 .repl_diskless_sync_delay(5)
2832 .repl_diskless_sync_max_replicas(3)
2833 .repl_ping_replica_period(10)
2834 .repl_timeout(60)
2835 .replica_announce_ip("10.0.0.1")
2836 .replica_announce_port(6380)
2837 .replica_announced(true)
2838 .replica_full_sync_buffer_limit("256mb")
2839 .replica_ignore_disk_write_errors(false)
2840 .replica_ignore_maxmemory(true)
2841 .replica_lazy_flush(true)
2842 .replica_priority(100)
2843 .replica_read_only(true)
2844 .replica_serve_stale_data(false)
2845 .min_replicas_to_write(2)
2846 .min_replicas_max_lag(10);
2847
2848 assert_eq!(s.config.replicaof, Some(("127.0.0.1".into(), 6379)));
2849 assert_eq!(s.config.masterauth.as_deref(), Some("secret"));
2850 assert_eq!(s.config.masteruser.as_deref(), Some("repl-user"));
2851 assert_eq!(s.config.repl_backlog_size.as_deref(), Some("1mb"));
2852 assert_eq!(s.config.repl_backlog_ttl, Some(3600));
2853 assert_eq!(s.config.repl_disable_tcp_nodelay, Some(true));
2854 assert!(matches!(
2855 s.config.repl_diskless_load,
2856 Some(ReplDisklessLoad::Swapdb)
2857 ));
2858 assert_eq!(s.config.repl_diskless_sync, Some(true));
2859 assert_eq!(s.config.repl_diskless_sync_delay, Some(5));
2860 assert_eq!(s.config.repl_diskless_sync_max_replicas, Some(3));
2861 assert_eq!(s.config.repl_ping_replica_period, Some(10));
2862 assert_eq!(s.config.repl_timeout, Some(60));
2863 assert_eq!(s.config.replica_announce_ip.as_deref(), Some("10.0.0.1"));
2864 assert_eq!(s.config.replica_announce_port, Some(6380));
2865 assert_eq!(s.config.replica_announced, Some(true));
2866 assert_eq!(
2867 s.config.replica_full_sync_buffer_limit.as_deref(),
2868 Some("256mb")
2869 );
2870 assert_eq!(s.config.replica_ignore_disk_write_errors, Some(false));
2871 assert_eq!(s.config.replica_ignore_maxmemory, Some(true));
2872 assert_eq!(s.config.replica_lazy_flush, Some(true));
2873 assert_eq!(s.config.replica_priority, Some(100));
2874 assert_eq!(s.config.replica_read_only, Some(true));
2875 assert_eq!(s.config.replica_serve_stale_data, Some(false));
2876 assert_eq!(s.config.min_replicas_to_write, Some(2));
2877 assert_eq!(s.config.min_replicas_max_lag, Some(10));
2878 }
2879
2880 #[test]
2881 fn cluster_config() {
2882 let s = RedisServer::new()
2883 .port(7000)
2884 .cluster_enabled(true)
2885 .cluster_node_timeout(5000)
2886 .cluster_config_file("/tmp/nodes.conf")
2887 .cluster_require_full_coverage(false)
2888 .cluster_allow_reads_when_down(true)
2889 .cluster_allow_pubsubshard_when_down(true)
2890 .cluster_allow_replica_migration(true)
2891 .cluster_migration_barrier(1)
2892 .cluster_replica_no_failover(false)
2893 .cluster_replica_validity_factor(10)
2894 .cluster_announce_ip("10.0.0.1")
2895 .cluster_announce_port(7000)
2896 .cluster_announce_bus_port(17000)
2897 .cluster_announce_tls_port(7100)
2898 .cluster_announce_hostname("node1.example.com")
2899 .cluster_announce_human_nodename("node-1")
2900 .cluster_port(17000)
2901 .cluster_preferred_endpoint_type("ip")
2902 .cluster_link_sendbuf_limit(67108864)
2903 .cluster_compatibility_sample_ratio(50)
2904 .cluster_slot_migration_handoff_max_lag_bytes(1048576)
2905 .cluster_slot_migration_write_pause_timeout(5000)
2906 .cluster_slot_stats_enabled(true);
2907
2908 assert!(s.config.cluster_enabled);
2909 assert_eq!(s.config.cluster_node_timeout, Some(5000));
2910 assert_eq!(
2911 s.config.cluster_config_file,
2912 Some(PathBuf::from("/tmp/nodes.conf"))
2913 );
2914 assert_eq!(s.config.cluster_require_full_coverage, Some(false));
2915 assert_eq!(s.config.cluster_allow_reads_when_down, Some(true));
2916 assert_eq!(s.config.cluster_allow_pubsubshard_when_down, Some(true));
2917 assert_eq!(s.config.cluster_allow_replica_migration, Some(true));
2918 assert_eq!(s.config.cluster_migration_barrier, Some(1));
2919 assert_eq!(s.config.cluster_replica_no_failover, Some(false));
2920 assert_eq!(s.config.cluster_replica_validity_factor, Some(10));
2921 assert_eq!(s.config.cluster_announce_ip.as_deref(), Some("10.0.0.1"));
2922 assert_eq!(s.config.cluster_announce_port, Some(7000));
2923 assert_eq!(s.config.cluster_announce_bus_port, Some(17000));
2924 assert_eq!(s.config.cluster_announce_tls_port, Some(7100));
2925 assert_eq!(
2926 s.config.cluster_announce_hostname.as_deref(),
2927 Some("node1.example.com")
2928 );
2929 assert_eq!(
2930 s.config.cluster_announce_human_nodename.as_deref(),
2931 Some("node-1")
2932 );
2933 assert_eq!(s.config.cluster_port, Some(17000));
2934 assert_eq!(
2935 s.config.cluster_preferred_endpoint_type.as_deref(),
2936 Some("ip")
2937 );
2938 assert_eq!(s.config.cluster_link_sendbuf_limit, Some(67108864));
2939 assert_eq!(s.config.cluster_compatibility_sample_ratio, Some(50));
2940 assert_eq!(
2941 s.config.cluster_slot_migration_handoff_max_lag_bytes,
2942 Some(1048576)
2943 );
2944 assert_eq!(
2945 s.config.cluster_slot_migration_write_pause_timeout,
2946 Some(5000)
2947 );
2948 assert_eq!(s.config.cluster_slot_stats_enabled, Some(true));
2949 }
2950
2951 #[test]
2952 fn data_structure_tuning() {
2953 let s = RedisServer::new()
2954 .hash_max_listpack_entries(128)
2955 .hash_max_listpack_value(64)
2956 .list_max_listpack_size(-2)
2957 .list_compress_depth(1)
2958 .set_max_intset_entries(512)
2959 .set_max_listpack_entries(128)
2960 .set_max_listpack_value(64)
2961 .zset_max_listpack_entries(128)
2962 .zset_max_listpack_value(64)
2963 .hll_sparse_max_bytes(3000)
2964 .stream_node_max_bytes(4096)
2965 .stream_node_max_entries(100)
2966 .stream_idmp_duration(5000)
2967 .stream_idmp_maxsize(1000);
2968
2969 assert_eq!(s.config.hash_max_listpack_entries, Some(128));
2970 assert_eq!(s.config.hash_max_listpack_value, Some(64));
2971 assert_eq!(s.config.list_max_listpack_size, Some(-2));
2972 assert_eq!(s.config.list_compress_depth, Some(1));
2973 assert_eq!(s.config.set_max_intset_entries, Some(512));
2974 assert_eq!(s.config.set_max_listpack_entries, Some(128));
2975 assert_eq!(s.config.set_max_listpack_value, Some(64));
2976 assert_eq!(s.config.zset_max_listpack_entries, Some(128));
2977 assert_eq!(s.config.zset_max_listpack_value, Some(64));
2978 assert_eq!(s.config.hll_sparse_max_bytes, Some(3000));
2979 assert_eq!(s.config.stream_node_max_bytes, Some(4096));
2980 assert_eq!(s.config.stream_node_max_entries, Some(100));
2981 assert_eq!(s.config.stream_idmp_duration, Some(5000));
2982 assert_eq!(s.config.stream_idmp_maxsize, Some(1000));
2983 }
2984
2985 #[test]
2986 fn tls_config() {
2987 let s = RedisServer::new()
2988 .port(6400)
2989 .tls_port(6401)
2990 .tls_cert_file("/etc/tls/redis.crt")
2991 .tls_key_file("/etc/tls/redis.key")
2992 .tls_key_file_pass("keypass")
2993 .tls_ca_cert_file("/etc/tls/ca.crt")
2994 .tls_ca_cert_dir("/etc/tls/certs")
2995 .tls_auth_clients(true)
2996 .tls_client_cert_file("/etc/tls/client.crt")
2997 .tls_client_key_file("/etc/tls/client.key")
2998 .tls_client_key_file_pass("clientpass")
2999 .tls_dh_params_file("/etc/tls/dhparams.pem")
3000 .tls_ciphers("ECDHE-RSA-AES256-GCM-SHA384")
3001 .tls_ciphersuites("TLS_AES_256_GCM_SHA384")
3002 .tls_protocols("TLSv1.2 TLSv1.3")
3003 .tls_prefer_server_ciphers(true)
3004 .tls_session_caching(true)
3005 .tls_session_cache_size(20480)
3006 .tls_session_cache_timeout(300)
3007 .tls_replication(true)
3008 .tls_cluster(true);
3009
3010 assert_eq!(s.config.tls_port, Some(6401));
3011 assert_eq!(
3012 s.config.tls_cert_file.as_deref(),
3013 Some(std::path::Path::new("/etc/tls/redis.crt"))
3014 );
3015 assert_eq!(
3016 s.config.tls_key_file.as_deref(),
3017 Some(std::path::Path::new("/etc/tls/redis.key"))
3018 );
3019 assert_eq!(s.config.tls_key_file_pass.as_deref(), Some("keypass"));
3020 assert_eq!(
3021 s.config.tls_ca_cert_file.as_deref(),
3022 Some(std::path::Path::new("/etc/tls/ca.crt"))
3023 );
3024 assert_eq!(
3025 s.config.tls_ca_cert_dir.as_deref(),
3026 Some(std::path::Path::new("/etc/tls/certs"))
3027 );
3028 assert_eq!(s.config.tls_auth_clients, Some(true));
3029 assert_eq!(
3030 s.config.tls_client_cert_file.as_deref(),
3031 Some(std::path::Path::new("/etc/tls/client.crt"))
3032 );
3033 assert_eq!(
3034 s.config.tls_client_key_file.as_deref(),
3035 Some(std::path::Path::new("/etc/tls/client.key"))
3036 );
3037 assert_eq!(
3038 s.config.tls_client_key_file_pass.as_deref(),
3039 Some("clientpass")
3040 );
3041 assert_eq!(
3042 s.config.tls_dh_params_file.as_deref(),
3043 Some(std::path::Path::new("/etc/tls/dhparams.pem"))
3044 );
3045 assert_eq!(
3046 s.config.tls_ciphers.as_deref(),
3047 Some("ECDHE-RSA-AES256-GCM-SHA384")
3048 );
3049 assert_eq!(
3050 s.config.tls_ciphersuites.as_deref(),
3051 Some("TLS_AES_256_GCM_SHA384")
3052 );
3053 assert_eq!(s.config.tls_protocols.as_deref(), Some("TLSv1.2 TLSv1.3"));
3054 assert_eq!(s.config.tls_prefer_server_ciphers, Some(true));
3055 assert_eq!(s.config.tls_session_caching, Some(true));
3056 assert_eq!(s.config.tls_session_cache_size, Some(20480));
3057 assert_eq!(s.config.tls_session_cache_timeout, Some(300));
3058 assert_eq!(s.config.tls_replication, Some(true));
3059 assert_eq!(s.config.tls_cluster, Some(true));
3060 }
3061}