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 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());
1539 self
1540 }
1541
1542 pub fn hz(mut self, hz: u32) -> Self {
1546 self.config.hz = Some(hz);
1547 self
1548 }
1549
1550 pub fn io_threads(mut self, n: u32) -> Self {
1552 self.config.io_threads = Some(n);
1553 self
1554 }
1555
1556 pub fn io_threads_do_reads(mut self, enable: bool) -> Self {
1558 self.config.io_threads_do_reads = Some(enable);
1559 self
1560 }
1561
1562 pub fn notify_keyspace_events(mut self, events: impl Into<String>) -> Self {
1564 self.config.notify_keyspace_events = Some(events.into());
1565 self
1566 }
1567
1568 pub fn slowlog_log_slower_than(mut self, us: i64) -> Self {
1572 self.config.slowlog_log_slower_than = Some(us);
1573 self
1574 }
1575
1576 pub fn slowlog_max_len(mut self, n: u32) -> Self {
1578 self.config.slowlog_max_len = Some(n);
1579 self
1580 }
1581
1582 pub fn latency_monitor_threshold(mut self, ms: u64) -> Self {
1586 self.config.latency_monitor_threshold = Some(ms);
1587 self
1588 }
1589
1590 pub fn latency_tracking(mut self, enable: bool) -> Self {
1592 self.config.latency_tracking = Some(enable);
1593 self
1594 }
1595
1596 pub fn latency_tracking_info_percentiles(mut self, percentiles: impl Into<String>) -> Self {
1598 self.config.latency_tracking_info_percentiles = Some(percentiles.into());
1599 self
1600 }
1601
1602 pub fn activedefrag(mut self, enable: bool) -> Self {
1606 self.config.activedefrag = Some(enable);
1607 self
1608 }
1609
1610 pub fn active_defrag_ignore_bytes(mut self, bytes: impl Into<String>) -> Self {
1612 self.config.active_defrag_ignore_bytes = Some(bytes.into());
1613 self
1614 }
1615
1616 pub fn active_defrag_threshold_lower(mut self, pct: u32) -> Self {
1618 self.config.active_defrag_threshold_lower = Some(pct);
1619 self
1620 }
1621
1622 pub fn active_defrag_threshold_upper(mut self, pct: u32) -> Self {
1624 self.config.active_defrag_threshold_upper = Some(pct);
1625 self
1626 }
1627
1628 pub fn active_defrag_cycle_min(mut self, pct: u32) -> Self {
1630 self.config.active_defrag_cycle_min = Some(pct);
1631 self
1632 }
1633
1634 pub fn active_defrag_cycle_max(mut self, pct: u32) -> Self {
1636 self.config.active_defrag_cycle_max = Some(pct);
1637 self
1638 }
1639
1640 pub fn active_defrag_max_scan_fields(mut self, n: u32) -> Self {
1642 self.config.active_defrag_max_scan_fields = Some(n);
1643 self
1644 }
1645
1646 pub fn syslog_enabled(mut self, enable: bool) -> Self {
1650 self.config.syslog_enabled = Some(enable);
1651 self
1652 }
1653
1654 pub fn syslog_ident(mut self, ident: impl Into<String>) -> Self {
1656 self.config.syslog_ident = Some(ident.into());
1657 self
1658 }
1659
1660 pub fn syslog_facility(mut self, facility: impl Into<String>) -> Self {
1662 self.config.syslog_facility = Some(facility.into());
1663 self
1664 }
1665
1666 pub fn supervised(mut self, mode: impl Into<String>) -> Self {
1668 self.config.supervised = Some(mode.into());
1669 self
1670 }
1671
1672 pub fn always_show_logo(mut self, enable: bool) -> Self {
1674 self.config.always_show_logo = Some(enable);
1675 self
1676 }
1677
1678 pub fn set_proc_title(mut self, enable: bool) -> Self {
1680 self.config.set_proc_title = Some(enable);
1681 self
1682 }
1683
1684 pub fn proc_title_template(mut self, template: impl Into<String>) -> Self {
1686 self.config.proc_title_template = Some(template.into());
1687 self
1688 }
1689
1690 pub fn acl_pubsub_default(mut self, default: impl Into<String>) -> Self {
1694 self.config.acl_pubsub_default = Some(default.into());
1695 self
1696 }
1697
1698 pub fn acllog_max_len(mut self, n: u32) -> Self {
1700 self.config.acllog_max_len = Some(n);
1701 self
1702 }
1703
1704 pub fn enable_debug_command(mut self, mode: impl Into<String>) -> Self {
1706 self.config.enable_debug_command = Some(mode.into());
1707 self
1708 }
1709
1710 pub fn enable_module_command(mut self, mode: impl Into<String>) -> Self {
1712 self.config.enable_module_command = Some(mode.into());
1713 self
1714 }
1715
1716 pub fn enable_protected_configs(mut self, mode: impl Into<String>) -> Self {
1718 self.config.enable_protected_configs = Some(mode.into());
1719 self
1720 }
1721
1722 pub fn rename_command(
1724 mut self,
1725 command: impl Into<String>,
1726 new_name: impl Into<String>,
1727 ) -> Self {
1728 self.config
1729 .rename_command
1730 .push((command.into(), new_name.into()));
1731 self
1732 }
1733
1734 pub fn sanitize_dump_payload(mut self, mode: impl Into<String>) -> Self {
1736 self.config.sanitize_dump_payload = Some(mode.into());
1737 self
1738 }
1739
1740 pub fn hide_user_data_from_log(mut self, enable: bool) -> Self {
1742 self.config.hide_user_data_from_log = Some(enable);
1743 self
1744 }
1745
1746 pub fn bind_source_addr(mut self, addr: impl Into<String>) -> Self {
1750 self.config.bind_source_addr = Some(addr.into());
1751 self
1752 }
1753
1754 pub fn busy_reply_threshold(mut self, ms: u64) -> Self {
1756 self.config.busy_reply_threshold = Some(ms);
1757 self
1758 }
1759
1760 pub fn client_output_buffer_limit(mut self, limit: impl Into<String>) -> Self {
1762 self.config.client_output_buffer_limit.push(limit.into());
1763 self
1764 }
1765
1766 pub fn client_query_buffer_limit(mut self, limit: impl Into<String>) -> Self {
1768 self.config.client_query_buffer_limit = Some(limit.into());
1769 self
1770 }
1771
1772 pub fn proto_max_bulk_len(mut self, len: impl Into<String>) -> Self {
1774 self.config.proto_max_bulk_len = Some(len.into());
1775 self
1776 }
1777
1778 pub fn max_new_connections_per_cycle(mut self, n: u32) -> Self {
1780 self.config.max_new_connections_per_cycle = Some(n);
1781 self
1782 }
1783
1784 pub fn max_new_tls_connections_per_cycle(mut self, n: u32) -> Self {
1786 self.config.max_new_tls_connections_per_cycle = Some(n);
1787 self
1788 }
1789
1790 pub fn socket_mark_id(mut self, id: u32) -> Self {
1792 self.config.socket_mark_id = Some(id);
1793 self
1794 }
1795
1796 pub fn dbfilename(mut self, name: impl Into<String>) -> Self {
1800 self.config.dbfilename = Some(name.into());
1801 self
1802 }
1803
1804 pub fn rdbcompression(mut self, enable: bool) -> Self {
1806 self.config.rdbcompression = Some(enable);
1807 self
1808 }
1809
1810 pub fn rdbchecksum(mut self, enable: bool) -> Self {
1812 self.config.rdbchecksum = Some(enable);
1813 self
1814 }
1815
1816 pub fn rdb_save_incremental_fsync(mut self, enable: bool) -> Self {
1818 self.config.rdb_save_incremental_fsync = Some(enable);
1819 self
1820 }
1821
1822 pub fn rdb_del_sync_files(mut self, enable: bool) -> Self {
1824 self.config.rdb_del_sync_files = Some(enable);
1825 self
1826 }
1827
1828 pub fn stop_writes_on_bgsave_error(mut self, enable: bool) -> Self {
1830 self.config.stop_writes_on_bgsave_error = Some(enable);
1831 self
1832 }
1833
1834 pub fn shutdown_on_sigint(mut self, behavior: impl Into<String>) -> Self {
1838 self.config.shutdown_on_sigint = Some(behavior.into());
1839 self
1840 }
1841
1842 pub fn shutdown_on_sigterm(mut self, behavior: impl Into<String>) -> Self {
1844 self.config.shutdown_on_sigterm = Some(behavior.into());
1845 self
1846 }
1847
1848 pub fn shutdown_timeout(mut self, seconds: u32) -> Self {
1850 self.config.shutdown_timeout = Some(seconds);
1851 self
1852 }
1853
1854 pub fn activerehashing(mut self, enable: bool) -> Self {
1858 self.config.activerehashing = Some(enable);
1859 self
1860 }
1861
1862 pub fn crash_log_enabled(mut self, enable: bool) -> Self {
1864 self.config.crash_log_enabled = Some(enable);
1865 self
1866 }
1867
1868 pub fn crash_memcheck_enabled(mut self, enable: bool) -> Self {
1870 self.config.crash_memcheck_enabled = Some(enable);
1871 self
1872 }
1873
1874 pub fn disable_thp(mut self, enable: bool) -> Self {
1876 self.config.disable_thp = Some(enable);
1877 self
1878 }
1879
1880 pub fn dynamic_hz(mut self, enable: bool) -> Self {
1882 self.config.dynamic_hz = Some(enable);
1883 self
1884 }
1885
1886 pub fn ignore_warnings(mut self, warning: impl Into<String>) -> Self {
1888 self.config.ignore_warnings = Some(warning.into());
1889 self
1890 }
1891
1892 pub fn include(mut self, path: impl Into<PathBuf>) -> Self {
1894 self.config.include.push(path.into());
1895 self
1896 }
1897
1898 pub fn jemalloc_bg_thread(mut self, enable: bool) -> Self {
1900 self.config.jemalloc_bg_thread = Some(enable);
1901 self
1902 }
1903
1904 pub fn locale_collate(mut self, locale: impl Into<String>) -> Self {
1906 self.config.locale_collate = Some(locale.into());
1907 self
1908 }
1909
1910 pub fn lua_time_limit(mut self, ms: u64) -> Self {
1912 self.config.lua_time_limit = Some(ms);
1913 self
1914 }
1915
1916 pub fn oom_score_adj(mut self, mode: impl Into<String>) -> Self {
1918 self.config.oom_score_adj = Some(mode.into());
1919 self
1920 }
1921
1922 pub fn oom_score_adj_values(mut self, values: impl Into<String>) -> Self {
1924 self.config.oom_score_adj_values = Some(values.into());
1925 self
1926 }
1927
1928 pub fn propagation_error_behavior(mut self, behavior: impl Into<String>) -> Self {
1930 self.config.propagation_error_behavior = Some(behavior.into());
1931 self
1932 }
1933
1934 pub fn tracking_table_max_keys(mut self, n: u64) -> Self {
1936 self.config.tracking_table_max_keys = Some(n);
1937 self
1938 }
1939
1940 pub fn redis_server_bin(mut self, bin: impl Into<String>) -> Self {
1944 self.config.redis_server_bin = bin.into();
1945 self
1946 }
1947
1948 pub fn redis_cli_bin(mut self, bin: impl Into<String>) -> Self {
1950 self.config.redis_cli_bin = bin.into();
1951 self
1952 }
1953
1954 pub fn no_stack_modules(mut self) -> Self {
1960 self.config.no_stack_modules = true;
1961 self
1962 }
1963
1964 pub fn extra(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1966 self.config.extra.insert(key.into(), value.into());
1967 self
1968 }
1969
1970 pub async fn start(self) -> Result<RedisServerHandle> {
1975 if which::which(&self.config.redis_server_bin).is_err() {
1976 return Err(Error::BinaryNotFound {
1977 binary: self.config.redis_server_bin.clone(),
1978 });
1979 }
1980 if which::which(&self.config.redis_cli_bin).is_err() {
1981 return Err(Error::BinaryNotFound {
1982 binary: self.config.redis_cli_bin.clone(),
1983 });
1984 }
1985
1986 let node_dir = self.config.dir.join(format!("node-{}", self.config.port));
1987
1988 let stale_pidfile = node_dir.join("redis.pid");
1992 if let Some(stale_pid) = crate::process::read_pidfile(&stale_pidfile)
1993 && crate::process::pid_alive(stale_pid)
1994 {
1995 crate::process::force_kill(stale_pid);
1996 }
1997
1998 fs::create_dir_all(&node_dir)?;
1999
2000 let conf_path = node_dir.join("redis.conf");
2001 let conf_content = self.generate_config(&node_dir);
2002 fs::write(&conf_path, conf_content)?;
2003
2004 let module_args = if self.config.no_stack_modules {
2005 Vec::new()
2006 } else {
2007 crate::stack::detect_stack_modules(&self.config.redis_server_bin)
2008 };
2009 let status = Command::new(&self.config.redis_server_bin)
2010 .arg(&conf_path)
2011 .args(&module_args)
2012 .stdout(std::process::Stdio::null())
2013 .stderr(std::process::Stdio::null())
2014 .status()
2015 .await?;
2016
2017 if !status.success() {
2018 return Err(Error::ServerStart {
2019 port: self.config.port,
2020 });
2021 }
2022
2023 let mut cli = RedisCli::new()
2024 .bin(&self.config.redis_cli_bin)
2025 .host(&self.config.bind)
2026 .port(self.config.port);
2027 if let Some(ref pw) = self.config.password {
2028 cli = cli.password(pw);
2029 }
2030 if self.config.tls_cert_file.is_some() && self.config.tls_key_file.is_some() {
2032 cli = cli.tls(true);
2033 if let Some(ref ca) = self.config.tls_ca_cert_file {
2034 cli = cli.cacert(ca);
2035 } else {
2036 cli = cli.insecure(true);
2037 }
2038 if let Some(ref cert) = self.config.tls_cert_file {
2039 cli = cli.cert(cert);
2040 }
2041 if let Some(ref key) = self.config.tls_key_file {
2042 cli = cli.key(key);
2043 }
2044 }
2045
2046 cli.wait_for_ready(Duration::from_secs(10)).await?;
2047
2048 let pid_path = node_dir.join("redis.pid");
2049 let pid: u32 = fs::read_to_string(&pid_path)
2050 .map_err(Error::Io)?
2051 .trim()
2052 .parse()
2053 .map_err(|_| Error::ServerStart {
2054 port: self.config.port,
2055 })?;
2056
2057 Ok(RedisServerHandle {
2058 config: self.config,
2059 cli,
2060 pid,
2061 detached: false,
2062 })
2063 }
2064
2065 fn generate_config(&self, node_dir: &std::path::Path) -> String {
2066 let yn = |b: bool| if b { "yes" } else { "no" };
2067
2068 let mut conf = format!(
2069 "port {port}\n\
2070 bind {bind}\n\
2071 daemonize {daemonize}\n\
2072 pidfile {dir}/redis.pid\n\
2073 dir {dir}\n\
2074 loglevel {level}\n\
2075 protected-mode {protected}\n",
2076 port = self.config.port,
2077 bind = self.config.bind,
2078 daemonize = yn(self.config.daemonize),
2079 dir = node_dir.display(),
2080 level = self.config.loglevel,
2081 protected = yn(self.config.protected_mode),
2082 );
2083
2084 let logfile = self
2085 .config
2086 .logfile
2087 .as_deref()
2088 .map(str::to_owned)
2089 .unwrap_or_else(|| format!("{}/redis.log", node_dir.display()));
2090 conf.push_str(&format!("logfile {logfile}\n"));
2091
2092 if let Some(backlog) = self.config.tcp_backlog {
2094 conf.push_str(&format!("tcp-backlog {backlog}\n"));
2095 }
2096 if let Some(ref path) = self.config.unixsocket {
2097 conf.push_str(&format!("unixsocket {}\n", path.display()));
2098 }
2099 if let Some(perm) = self.config.unixsocketperm {
2100 conf.push_str(&format!("unixsocketperm {perm}\n"));
2101 }
2102 if let Some(t) = self.config.timeout {
2103 conf.push_str(&format!("timeout {t}\n"));
2104 }
2105 if let Some(ka) = self.config.tcp_keepalive {
2106 conf.push_str(&format!("tcp-keepalive {ka}\n"));
2107 }
2108
2109 if let Some(port) = self.config.tls_port {
2111 conf.push_str(&format!("tls-port {port}\n"));
2112 }
2113 if let Some(ref path) = self.config.tls_cert_file {
2114 conf.push_str(&format!("tls-cert-file {}\n", path.display()));
2115 }
2116 if let Some(ref path) = self.config.tls_key_file {
2117 conf.push_str(&format!("tls-key-file {}\n", path.display()));
2118 }
2119 if let Some(ref pass) = self.config.tls_key_file_pass {
2120 conf.push_str(&format!("tls-key-file-pass {pass}\n"));
2121 }
2122 if let Some(ref path) = self.config.tls_ca_cert_file {
2123 conf.push_str(&format!("tls-ca-cert-file {}\n", path.display()));
2124 }
2125 if let Some(ref path) = self.config.tls_ca_cert_dir {
2126 conf.push_str(&format!("tls-ca-cert-dir {}\n", path.display()));
2127 }
2128 if let Some(auth) = self.config.tls_auth_clients {
2129 conf.push_str(&format!("tls-auth-clients {}\n", yn(auth)));
2130 }
2131 if let Some(ref path) = self.config.tls_client_cert_file {
2132 conf.push_str(&format!("tls-client-cert-file {}\n", path.display()));
2133 }
2134 if let Some(ref path) = self.config.tls_client_key_file {
2135 conf.push_str(&format!("tls-client-key-file {}\n", path.display()));
2136 }
2137 if let Some(ref pass) = self.config.tls_client_key_file_pass {
2138 conf.push_str(&format!("tls-client-key-file-pass {pass}\n"));
2139 }
2140 if let Some(ref path) = self.config.tls_dh_params_file {
2141 conf.push_str(&format!("tls-dh-params-file {}\n", path.display()));
2142 }
2143 if let Some(ref ciphers) = self.config.tls_ciphers {
2144 conf.push_str(&format!("tls-ciphers {ciphers}\n"));
2145 }
2146 if let Some(ref suites) = self.config.tls_ciphersuites {
2147 conf.push_str(&format!("tls-ciphersuites {suites}\n"));
2148 }
2149 if let Some(ref protocols) = self.config.tls_protocols {
2150 conf.push_str(&format!("tls-protocols {protocols}\n"));
2151 }
2152 if let Some(v) = self.config.tls_prefer_server_ciphers {
2153 conf.push_str(&format!("tls-prefer-server-ciphers {}\n", yn(v)));
2154 }
2155 if let Some(v) = self.config.tls_session_caching {
2156 conf.push_str(&format!("tls-session-caching {}\n", yn(v)));
2157 }
2158 if let Some(size) = self.config.tls_session_cache_size {
2159 conf.push_str(&format!("tls-session-cache-size {size}\n"));
2160 }
2161 if let Some(timeout) = self.config.tls_session_cache_timeout {
2162 conf.push_str(&format!("tls-session-cache-timeout {timeout}\n"));
2163 }
2164 if let Some(v) = self.config.tls_replication {
2165 conf.push_str(&format!("tls-replication {}\n", yn(v)));
2166 }
2167 if let Some(v) = self.config.tls_cluster {
2168 conf.push_str(&format!("tls-cluster {}\n", yn(v)));
2169 }
2170
2171 if let Some(n) = self.config.databases {
2173 conf.push_str(&format!("databases {n}\n"));
2174 }
2175
2176 if let Some(ref limit) = self.config.maxmemory {
2178 conf.push_str(&format!("maxmemory {limit}\n"));
2179 }
2180 if let Some(ref policy) = self.config.maxmemory_policy {
2181 conf.push_str(&format!("maxmemory-policy {policy}\n"));
2182 }
2183 if let Some(n) = self.config.maxmemory_samples {
2184 conf.push_str(&format!("maxmemory-samples {n}\n"));
2185 }
2186 if let Some(ref limit) = self.config.maxmemory_clients {
2187 conf.push_str(&format!("maxmemory-clients {limit}\n"));
2188 }
2189 if let Some(n) = self.config.maxmemory_eviction_tenacity {
2190 conf.push_str(&format!("maxmemory-eviction-tenacity {n}\n"));
2191 }
2192 if let Some(n) = self.config.maxclients {
2193 conf.push_str(&format!("maxclients {n}\n"));
2194 }
2195 if let Some(n) = self.config.lfu_log_factor {
2196 conf.push_str(&format!("lfu-log-factor {n}\n"));
2197 }
2198 if let Some(n) = self.config.lfu_decay_time {
2199 conf.push_str(&format!("lfu-decay-time {n}\n"));
2200 }
2201 if let Some(n) = self.config.active_expire_effort {
2202 conf.push_str(&format!("active-expire-effort {n}\n"));
2203 }
2204
2205 if let Some(v) = self.config.lazyfree_lazy_eviction {
2207 conf.push_str(&format!("lazyfree-lazy-eviction {}\n", yn(v)));
2208 }
2209 if let Some(v) = self.config.lazyfree_lazy_expire {
2210 conf.push_str(&format!("lazyfree-lazy-expire {}\n", yn(v)));
2211 }
2212 if let Some(v) = self.config.lazyfree_lazy_server_del {
2213 conf.push_str(&format!("lazyfree-lazy-server-del {}\n", yn(v)));
2214 }
2215 if let Some(v) = self.config.lazyfree_lazy_user_del {
2216 conf.push_str(&format!("lazyfree-lazy-user-del {}\n", yn(v)));
2217 }
2218 if let Some(v) = self.config.lazyfree_lazy_user_flush {
2219 conf.push_str(&format!("lazyfree-lazy-user-flush {}\n", yn(v)));
2220 }
2221
2222 match &self.config.save {
2224 SavePolicy::Disabled => conf.push_str("save \"\"\n"),
2225 SavePolicy::Default => {}
2226 SavePolicy::Custom(pairs) => {
2227 for (secs, changes) in pairs {
2228 conf.push_str(&format!("save {secs} {changes}\n"));
2229 }
2230 }
2231 }
2232 if self.config.appendonly {
2233 conf.push_str("appendonly yes\n");
2234 }
2235 if let Some(ref policy) = self.config.appendfsync {
2236 conf.push_str(&format!("appendfsync {policy}\n"));
2237 }
2238 if let Some(ref name) = self.config.appendfilename {
2239 conf.push_str(&format!("appendfilename \"{name}\"\n"));
2240 }
2241 if let Some(ref name) = self.config.appenddirname {
2242 conf.push_str(&format!("appenddirname \"{}\"\n", name.display()));
2243 }
2244 if let Some(v) = self.config.aof_use_rdb_preamble {
2245 conf.push_str(&format!("aof-use-rdb-preamble {}\n", yn(v)));
2246 }
2247 if let Some(v) = self.config.aof_load_truncated {
2248 conf.push_str(&format!("aof-load-truncated {}\n", yn(v)));
2249 }
2250 if let Some(ref size) = self.config.aof_load_corrupt_tail_max_size {
2251 conf.push_str(&format!("aof-load-corrupt-tail-max-size {size}\n"));
2252 }
2253 if let Some(v) = self.config.aof_rewrite_incremental_fsync {
2254 conf.push_str(&format!("aof-rewrite-incremental-fsync {}\n", yn(v)));
2255 }
2256 if let Some(v) = self.config.aof_timestamp_enabled {
2257 conf.push_str(&format!("aof-timestamp-enabled {}\n", yn(v)));
2258 }
2259 if let Some(pct) = self.config.auto_aof_rewrite_percentage {
2260 conf.push_str(&format!("auto-aof-rewrite-percentage {pct}\n"));
2261 }
2262 if let Some(ref size) = self.config.auto_aof_rewrite_min_size {
2263 conf.push_str(&format!("auto-aof-rewrite-min-size {size}\n"));
2264 }
2265 if let Some(v) = self.config.no_appendfsync_on_rewrite {
2266 conf.push_str(&format!("no-appendfsync-on-rewrite {}\n", yn(v)));
2267 }
2268
2269 if let Some((ref host, port)) = self.config.replicaof {
2271 conf.push_str(&format!("replicaof {host} {port}\n"));
2272 }
2273 if let Some(ref pw) = self.config.masterauth {
2274 conf.push_str(&format!("masterauth {pw}\n"));
2275 }
2276 if let Some(ref user) = self.config.masteruser {
2277 conf.push_str(&format!("masteruser {user}\n"));
2278 }
2279 if let Some(ref size) = self.config.repl_backlog_size {
2280 conf.push_str(&format!("repl-backlog-size {size}\n"));
2281 }
2282 if let Some(ttl) = self.config.repl_backlog_ttl {
2283 conf.push_str(&format!("repl-backlog-ttl {ttl}\n"));
2284 }
2285 if let Some(v) = self.config.repl_disable_tcp_nodelay {
2286 conf.push_str(&format!("repl-disable-tcp-nodelay {}\n", yn(v)));
2287 }
2288 if let Some(ref policy) = self.config.repl_diskless_load {
2289 conf.push_str(&format!("repl-diskless-load {policy}\n"));
2290 }
2291 if let Some(v) = self.config.repl_diskless_sync {
2292 conf.push_str(&format!("repl-diskless-sync {}\n", yn(v)));
2293 }
2294 if let Some(delay) = self.config.repl_diskless_sync_delay {
2295 conf.push_str(&format!("repl-diskless-sync-delay {delay}\n"));
2296 }
2297 if let Some(n) = self.config.repl_diskless_sync_max_replicas {
2298 conf.push_str(&format!("repl-diskless-sync-max-replicas {n}\n"));
2299 }
2300 if let Some(period) = self.config.repl_ping_replica_period {
2301 conf.push_str(&format!("repl-ping-replica-period {period}\n"));
2302 }
2303 if let Some(t) = self.config.repl_timeout {
2304 conf.push_str(&format!("repl-timeout {t}\n"));
2305 }
2306 if let Some(ref ip) = self.config.replica_announce_ip {
2307 conf.push_str(&format!("replica-announce-ip {ip}\n"));
2308 }
2309 if let Some(port) = self.config.replica_announce_port {
2310 conf.push_str(&format!("replica-announce-port {port}\n"));
2311 }
2312 if let Some(v) = self.config.replica_announced {
2313 conf.push_str(&format!("replica-announced {}\n", yn(v)));
2314 }
2315 if let Some(ref size) = self.config.replica_full_sync_buffer_limit {
2316 conf.push_str(&format!("replica-full-sync-buffer-limit {size}\n"));
2317 }
2318 if let Some(v) = self.config.replica_ignore_disk_write_errors {
2319 conf.push_str(&format!("replica-ignore-disk-write-errors {}\n", yn(v)));
2320 }
2321 if let Some(v) = self.config.replica_ignore_maxmemory {
2322 conf.push_str(&format!("replica-ignore-maxmemory {}\n", yn(v)));
2323 }
2324 if let Some(v) = self.config.replica_lazy_flush {
2325 conf.push_str(&format!("replica-lazy-flush {}\n", yn(v)));
2326 }
2327 if let Some(priority) = self.config.replica_priority {
2328 conf.push_str(&format!("replica-priority {priority}\n"));
2329 }
2330 if let Some(v) = self.config.replica_read_only {
2331 conf.push_str(&format!("replica-read-only {}\n", yn(v)));
2332 }
2333 if let Some(v) = self.config.replica_serve_stale_data {
2334 conf.push_str(&format!("replica-serve-stale-data {}\n", yn(v)));
2335 }
2336 if let Some(n) = self.config.min_replicas_to_write {
2337 conf.push_str(&format!("min-replicas-to-write {n}\n"));
2338 }
2339 if let Some(lag) = self.config.min_replicas_max_lag {
2340 conf.push_str(&format!("min-replicas-max-lag {lag}\n"));
2341 }
2342
2343 if let Some(ref pw) = self.config.password {
2345 conf.push_str(&format!("requirepass {pw}\n"));
2346 }
2347 if let Some(ref path) = self.config.acl_file {
2348 conf.push_str(&format!("aclfile {}\n", path.display()));
2349 }
2350
2351 if self.config.cluster_enabled {
2353 conf.push_str("cluster-enabled yes\n");
2354 if let Some(ref path) = self.config.cluster_config_file {
2355 conf.push_str(&format!("cluster-config-file {}\n", path.display()));
2356 } else {
2357 conf.push_str(&format!(
2358 "cluster-config-file {}/nodes.conf\n",
2359 node_dir.display()
2360 ));
2361 }
2362 if let Some(timeout) = self.config.cluster_node_timeout {
2363 conf.push_str(&format!("cluster-node-timeout {timeout}\n"));
2364 }
2365 if let Some(v) = self.config.cluster_require_full_coverage {
2366 conf.push_str(&format!("cluster-require-full-coverage {}\n", yn(v)));
2367 }
2368 if let Some(v) = self.config.cluster_allow_reads_when_down {
2369 conf.push_str(&format!("cluster-allow-reads-when-down {}\n", yn(v)));
2370 }
2371 if let Some(v) = self.config.cluster_allow_pubsubshard_when_down {
2372 conf.push_str(&format!("cluster-allow-pubsubshard-when-down {}\n", yn(v)));
2373 }
2374 if let Some(v) = self.config.cluster_allow_replica_migration {
2375 conf.push_str(&format!("cluster-allow-replica-migration {}\n", yn(v)));
2376 }
2377 if let Some(barrier) = self.config.cluster_migration_barrier {
2378 conf.push_str(&format!("cluster-migration-barrier {barrier}\n"));
2379 }
2380 if let Some(v) = self.config.cluster_replica_no_failover {
2381 conf.push_str(&format!("cluster-replica-no-failover {}\n", yn(v)));
2382 }
2383 if let Some(factor) = self.config.cluster_replica_validity_factor {
2384 conf.push_str(&format!("cluster-replica-validity-factor {factor}\n"));
2385 }
2386 if let Some(ref ip) = self.config.cluster_announce_ip {
2387 conf.push_str(&format!("cluster-announce-ip {ip}\n"));
2388 }
2389 if let Some(port) = self.config.cluster_announce_port {
2390 conf.push_str(&format!("cluster-announce-port {port}\n"));
2391 }
2392 if let Some(port) = self.config.cluster_announce_bus_port {
2393 conf.push_str(&format!("cluster-announce-bus-port {port}\n"));
2394 }
2395 if let Some(port) = self.config.cluster_announce_tls_port {
2396 conf.push_str(&format!("cluster-announce-tls-port {port}\n"));
2397 }
2398 if let Some(ref hostname) = self.config.cluster_announce_hostname {
2399 conf.push_str(&format!("cluster-announce-hostname {hostname}\n"));
2400 }
2401 if let Some(ref name) = self.config.cluster_announce_human_nodename {
2402 conf.push_str(&format!("cluster-announce-human-nodename {name}\n"));
2403 }
2404 if let Some(port) = self.config.cluster_port {
2405 conf.push_str(&format!("cluster-port {port}\n"));
2406 }
2407 if let Some(ref endpoint_type) = self.config.cluster_preferred_endpoint_type {
2408 conf.push_str(&format!(
2409 "cluster-preferred-endpoint-type {endpoint_type}\n"
2410 ));
2411 }
2412 if let Some(limit) = self.config.cluster_link_sendbuf_limit {
2413 conf.push_str(&format!("cluster-link-sendbuf-limit {limit}\n"));
2414 }
2415 if let Some(ratio) = self.config.cluster_compatibility_sample_ratio {
2416 conf.push_str(&format!("cluster-compatibility-sample-ratio {ratio}\n"));
2417 }
2418 if let Some(bytes) = self.config.cluster_slot_migration_handoff_max_lag_bytes {
2419 conf.push_str(&format!(
2420 "cluster-slot-migration-handoff-max-lag-bytes {bytes}\n"
2421 ));
2422 }
2423 if let Some(ms) = self.config.cluster_slot_migration_write_pause_timeout {
2424 conf.push_str(&format!(
2425 "cluster-slot-migration-write-pause-timeout {ms}\n"
2426 ));
2427 }
2428 if let Some(v) = self.config.cluster_slot_stats_enabled {
2429 conf.push_str(&format!("cluster-slot-stats-enabled {}\n", yn(v)));
2430 }
2431 }
2432
2433 if let Some(n) = self.config.hash_max_listpack_entries {
2435 conf.push_str(&format!("hash-max-listpack-entries {n}\n"));
2436 }
2437 if let Some(n) = self.config.hash_max_listpack_value {
2438 conf.push_str(&format!("hash-max-listpack-value {n}\n"));
2439 }
2440 if let Some(n) = self.config.list_max_listpack_size {
2441 conf.push_str(&format!("list-max-listpack-size {n}\n"));
2442 }
2443 if let Some(n) = self.config.list_compress_depth {
2444 conf.push_str(&format!("list-compress-depth {n}\n"));
2445 }
2446 if let Some(n) = self.config.set_max_intset_entries {
2447 conf.push_str(&format!("set-max-intset-entries {n}\n"));
2448 }
2449 if let Some(n) = self.config.set_max_listpack_entries {
2450 conf.push_str(&format!("set-max-listpack-entries {n}\n"));
2451 }
2452 if let Some(n) = self.config.set_max_listpack_value {
2453 conf.push_str(&format!("set-max-listpack-value {n}\n"));
2454 }
2455 if let Some(n) = self.config.zset_max_listpack_entries {
2456 conf.push_str(&format!("zset-max-listpack-entries {n}\n"));
2457 }
2458 if let Some(n) = self.config.zset_max_listpack_value {
2459 conf.push_str(&format!("zset-max-listpack-value {n}\n"));
2460 }
2461 if let Some(n) = self.config.hll_sparse_max_bytes {
2462 conf.push_str(&format!("hll-sparse-max-bytes {n}\n"));
2463 }
2464 if let Some(n) = self.config.stream_node_max_bytes {
2465 conf.push_str(&format!("stream-node-max-bytes {n}\n"));
2466 }
2467 if let Some(n) = self.config.stream_node_max_entries {
2468 conf.push_str(&format!("stream-node-max-entries {n}\n"));
2469 }
2470 if let Some(ms) = self.config.stream_idmp_duration {
2471 conf.push_str(&format!("stream-idmp-duration {ms}\n"));
2472 }
2473 if let Some(n) = self.config.stream_idmp_maxsize {
2474 conf.push_str(&format!("stream-idmp-maxsize {n}\n"));
2475 }
2476
2477 for path in &self.config.loadmodule {
2479 conf.push_str(&format!("loadmodule {}\n", path.display()));
2480 }
2481
2482 if let Some(hz) = self.config.hz {
2484 conf.push_str(&format!("hz {hz}\n"));
2485 }
2486 if let Some(n) = self.config.io_threads {
2487 conf.push_str(&format!("io-threads {n}\n"));
2488 }
2489 if let Some(enable) = self.config.io_threads_do_reads {
2490 conf.push_str(&format!("io-threads-do-reads {}\n", yn(enable)));
2491 }
2492 if let Some(ref events) = self.config.notify_keyspace_events {
2493 conf.push_str(&format!("notify-keyspace-events {events}\n"));
2494 }
2495
2496 if let Some(us) = self.config.slowlog_log_slower_than {
2498 conf.push_str(&format!("slowlog-log-slower-than {us}\n"));
2499 }
2500 if let Some(n) = self.config.slowlog_max_len {
2501 conf.push_str(&format!("slowlog-max-len {n}\n"));
2502 }
2503
2504 if let Some(ms) = self.config.latency_monitor_threshold {
2506 conf.push_str(&format!("latency-monitor-threshold {ms}\n"));
2507 }
2508 if let Some(enable) = self.config.latency_tracking {
2509 conf.push_str(&format!("latency-tracking {}\n", yn(enable)));
2510 }
2511 if let Some(ref pcts) = self.config.latency_tracking_info_percentiles {
2512 conf.push_str(&format!("latency-tracking-info-percentiles \"{pcts}\"\n"));
2513 }
2514
2515 if let Some(enable) = self.config.activedefrag {
2517 conf.push_str(&format!("activedefrag {}\n", yn(enable)));
2518 }
2519 if let Some(ref bytes) = self.config.active_defrag_ignore_bytes {
2520 conf.push_str(&format!("active-defrag-ignore-bytes {bytes}\n"));
2521 }
2522 if let Some(pct) = self.config.active_defrag_threshold_lower {
2523 conf.push_str(&format!("active-defrag-threshold-lower {pct}\n"));
2524 }
2525 if let Some(pct) = self.config.active_defrag_threshold_upper {
2526 conf.push_str(&format!("active-defrag-threshold-upper {pct}\n"));
2527 }
2528 if let Some(pct) = self.config.active_defrag_cycle_min {
2529 conf.push_str(&format!("active-defrag-cycle-min {pct}\n"));
2530 }
2531 if let Some(pct) = self.config.active_defrag_cycle_max {
2532 conf.push_str(&format!("active-defrag-cycle-max {pct}\n"));
2533 }
2534 if let Some(n) = self.config.active_defrag_max_scan_fields {
2535 conf.push_str(&format!("active-defrag-max-scan-fields {n}\n"));
2536 }
2537
2538 if let Some(enable) = self.config.syslog_enabled {
2540 conf.push_str(&format!("syslog-enabled {}\n", yn(enable)));
2541 }
2542 if let Some(ref ident) = self.config.syslog_ident {
2543 conf.push_str(&format!("syslog-ident {ident}\n"));
2544 }
2545 if let Some(ref facility) = self.config.syslog_facility {
2546 conf.push_str(&format!("syslog-facility {facility}\n"));
2547 }
2548 if let Some(ref mode) = self.config.supervised {
2549 conf.push_str(&format!("supervised {mode}\n"));
2550 }
2551 if let Some(enable) = self.config.always_show_logo {
2552 conf.push_str(&format!("always-show-logo {}\n", yn(enable)));
2553 }
2554 if let Some(enable) = self.config.set_proc_title {
2555 conf.push_str(&format!("set-proc-title {}\n", yn(enable)));
2556 }
2557 if let Some(ref template) = self.config.proc_title_template {
2558 conf.push_str(&format!("proc-title-template \"{template}\"\n"));
2559 }
2560
2561 if let Some(ref default) = self.config.acl_pubsub_default {
2563 conf.push_str(&format!("acl-pubsub-default {default}\n"));
2564 }
2565 if let Some(n) = self.config.acllog_max_len {
2566 conf.push_str(&format!("acllog-max-len {n}\n"));
2567 }
2568 if let Some(ref mode) = self.config.enable_debug_command {
2569 conf.push_str(&format!("enable-debug-command {mode}\n"));
2570 }
2571 if let Some(ref mode) = self.config.enable_module_command {
2572 conf.push_str(&format!("enable-module-command {mode}\n"));
2573 }
2574 if let Some(ref mode) = self.config.enable_protected_configs {
2575 conf.push_str(&format!("enable-protected-configs {mode}\n"));
2576 }
2577 for (cmd, new_name) in &self.config.rename_command {
2578 conf.push_str(&format!("rename-command {cmd} \"{new_name}\"\n"));
2579 }
2580 if let Some(ref mode) = self.config.sanitize_dump_payload {
2581 conf.push_str(&format!("sanitize-dump-payload {mode}\n"));
2582 }
2583 if let Some(enable) = self.config.hide_user_data_from_log {
2584 conf.push_str(&format!("hide-user-data-from-log {}\n", yn(enable)));
2585 }
2586
2587 if let Some(ref addr) = self.config.bind_source_addr {
2589 conf.push_str(&format!("bind-source-addr {addr}\n"));
2590 }
2591 if let Some(ms) = self.config.busy_reply_threshold {
2592 conf.push_str(&format!("busy-reply-threshold {ms}\n"));
2593 }
2594 for limit in &self.config.client_output_buffer_limit {
2595 conf.push_str(&format!("client-output-buffer-limit {limit}\n"));
2596 }
2597 if let Some(ref limit) = self.config.client_query_buffer_limit {
2598 conf.push_str(&format!("client-query-buffer-limit {limit}\n"));
2599 }
2600 if let Some(ref len) = self.config.proto_max_bulk_len {
2601 conf.push_str(&format!("proto-max-bulk-len {len}\n"));
2602 }
2603 if let Some(n) = self.config.max_new_connections_per_cycle {
2604 conf.push_str(&format!("max-new-connections-per-cycle {n}\n"));
2605 }
2606 if let Some(n) = self.config.max_new_tls_connections_per_cycle {
2607 conf.push_str(&format!("max-new-tls-connections-per-cycle {n}\n"));
2608 }
2609 if let Some(id) = self.config.socket_mark_id {
2610 conf.push_str(&format!("socket-mark-id {id}\n"));
2611 }
2612
2613 if let Some(ref name) = self.config.dbfilename {
2615 conf.push_str(&format!("dbfilename {name}\n"));
2616 }
2617 if let Some(enable) = self.config.rdbcompression {
2618 conf.push_str(&format!("rdbcompression {}\n", yn(enable)));
2619 }
2620 if let Some(enable) = self.config.rdbchecksum {
2621 conf.push_str(&format!("rdbchecksum {}\n", yn(enable)));
2622 }
2623 if let Some(enable) = self.config.rdb_save_incremental_fsync {
2624 conf.push_str(&format!("rdb-save-incremental-fsync {}\n", yn(enable)));
2625 }
2626 if let Some(enable) = self.config.rdb_del_sync_files {
2627 conf.push_str(&format!("rdb-del-sync-files {}\n", yn(enable)));
2628 }
2629 if let Some(enable) = self.config.stop_writes_on_bgsave_error {
2630 conf.push_str(&format!("stop-writes-on-bgsave-error {}\n", yn(enable)));
2631 }
2632
2633 if let Some(ref behavior) = self.config.shutdown_on_sigint {
2635 conf.push_str(&format!("shutdown-on-sigint {behavior}\n"));
2636 }
2637 if let Some(ref behavior) = self.config.shutdown_on_sigterm {
2638 conf.push_str(&format!("shutdown-on-sigterm {behavior}\n"));
2639 }
2640 if let Some(seconds) = self.config.shutdown_timeout {
2641 conf.push_str(&format!("shutdown-timeout {seconds}\n"));
2642 }
2643
2644 if let Some(enable) = self.config.activerehashing {
2646 conf.push_str(&format!("activerehashing {}\n", yn(enable)));
2647 }
2648 if let Some(enable) = self.config.crash_log_enabled {
2649 conf.push_str(&format!("crash-log-enabled {}\n", yn(enable)));
2650 }
2651 if let Some(enable) = self.config.crash_memcheck_enabled {
2652 conf.push_str(&format!("crash-memcheck-enabled {}\n", yn(enable)));
2653 }
2654 if let Some(enable) = self.config.disable_thp {
2655 conf.push_str(&format!("disable-thp {}\n", yn(enable)));
2656 }
2657 if let Some(enable) = self.config.dynamic_hz {
2658 conf.push_str(&format!("dynamic-hz {}\n", yn(enable)));
2659 }
2660 if let Some(ref warning) = self.config.ignore_warnings {
2661 conf.push_str(&format!("ignore-warnings {warning}\n"));
2662 }
2663 for path in &self.config.include {
2664 conf.push_str(&format!("include {}\n", path.display()));
2665 }
2666 if let Some(enable) = self.config.jemalloc_bg_thread {
2667 conf.push_str(&format!("jemalloc-bg-thread {}\n", yn(enable)));
2668 }
2669 if let Some(ref locale) = self.config.locale_collate {
2670 conf.push_str(&format!("locale-collate {locale}\n"));
2671 }
2672 if let Some(ms) = self.config.lua_time_limit {
2673 conf.push_str(&format!("lua-time-limit {ms}\n"));
2674 }
2675 if let Some(ref mode) = self.config.oom_score_adj {
2676 conf.push_str(&format!("oom-score-adj {mode}\n"));
2677 }
2678 if let Some(ref values) = self.config.oom_score_adj_values {
2679 conf.push_str(&format!("oom-score-adj-values {values}\n"));
2680 }
2681 if let Some(ref behavior) = self.config.propagation_error_behavior {
2682 conf.push_str(&format!("propagation-error-behavior {behavior}\n"));
2683 }
2684 if let Some(n) = self.config.tracking_table_max_keys {
2685 conf.push_str(&format!("tracking-table-max-keys {n}\n"));
2686 }
2687
2688 for (key, value) in &self.config.extra {
2690 conf.push_str(&format!("{key} {value}\n"));
2691 }
2692
2693 conf
2694 }
2695}
2696
2697impl Default for RedisServer {
2698 fn default() -> Self {
2699 Self::new()
2700 }
2701}
2702
2703pub struct RedisServerHandle {
2705 config: RedisServerConfig,
2706 cli: RedisCli,
2707 pid: u32,
2708 detached: bool,
2709}
2710
2711impl RedisServerHandle {
2712 pub fn addr(&self) -> String {
2714 format!("{}:{}", self.config.bind, self.config.port)
2715 }
2716
2717 pub fn port(&self) -> u16 {
2719 self.config.port
2720 }
2721
2722 pub fn host(&self) -> &str {
2724 &self.config.bind
2725 }
2726
2727 pub fn pid(&self) -> u32 {
2729 self.pid
2730 }
2731
2732 pub async fn is_alive(&self) -> bool {
2734 self.cli.ping().await
2735 }
2736
2737 pub fn cli(&self) -> &RedisCli {
2739 &self.cli
2740 }
2741
2742 pub async fn run(&self, args: &[&str]) -> Result<String> {
2744 self.cli.run(args).await
2745 }
2746
2747 pub fn detach(mut self) {
2749 self.detached = true;
2750 }
2751
2752 pub fn stop(&self) {
2759 self.cli.shutdown();
2761 std::thread::sleep(std::time::Duration::from_millis(500));
2763 if crate::process::pid_alive(self.pid) {
2765 crate::process::force_kill(self.pid);
2766 }
2767 crate::process::kill_by_port(self.config.port);
2769 }
2770
2771 pub async fn wait_for_ready(&self, timeout: Duration) -> Result<()> {
2773 self.cli.wait_for_ready(timeout).await
2774 }
2775}
2776
2777impl Drop for RedisServerHandle {
2778 fn drop(&mut self) {
2779 if !self.detached {
2780 self.stop();
2781 }
2782 }
2783}
2784
2785#[cfg(test)]
2786mod tests {
2787 use super::*;
2788
2789 #[test]
2790 fn default_config() {
2791 let s = RedisServer::new();
2792 assert_eq!(s.config.port, 6379);
2793 assert_eq!(s.config.bind, "127.0.0.1");
2794 assert!(matches!(s.config.save, SavePolicy::Disabled));
2795 }
2796
2797 #[test]
2798 fn builder_chain() {
2799 let s = RedisServer::new()
2800 .port(6400)
2801 .bind("0.0.0.0")
2802 .save(true)
2803 .appendonly(true)
2804 .password("secret")
2805 .logfile("/tmp/redis.log")
2806 .loglevel(LogLevel::Warning)
2807 .extra("maxmemory", "100mb");
2808
2809 assert_eq!(s.config.port, 6400);
2810 assert_eq!(s.config.bind, "0.0.0.0");
2811 assert!(matches!(s.config.save, SavePolicy::Default));
2812 assert!(s.config.appendonly);
2813 assert_eq!(s.config.password.as_deref(), Some("secret"));
2814 assert_eq!(s.config.logfile.as_deref(), Some("/tmp/redis.log"));
2815 assert_eq!(s.config.extra.get("maxmemory").unwrap(), "100mb");
2816 }
2817
2818 #[test]
2819 fn save_schedule() {
2820 let s = RedisServer::new().save_schedule(vec![(900, 1), (300, 10)]);
2821 match &s.config.save {
2822 SavePolicy::Custom(pairs) => {
2823 assert_eq!(pairs, &[(900, 1), (300, 10)]);
2824 }
2825 _ => panic!("expected SavePolicy::Custom"),
2826 }
2827 }
2828
2829 #[test]
2830 fn aof_tuning() {
2831 let s = RedisServer::new()
2832 .appendonly(true)
2833 .appendfsync(AppendFsync::Always)
2834 .appendfilename("my.aof")
2835 .aof_use_rdb_preamble(true)
2836 .auto_aof_rewrite_percentage(100)
2837 .auto_aof_rewrite_min_size("64mb")
2838 .no_appendfsync_on_rewrite(true);
2839
2840 assert!(s.config.appendonly);
2841 assert!(matches!(s.config.appendfsync, Some(AppendFsync::Always)));
2842 assert_eq!(s.config.appendfilename.as_deref(), Some("my.aof"));
2843 assert_eq!(s.config.aof_use_rdb_preamble, Some(true));
2844 assert_eq!(s.config.auto_aof_rewrite_percentage, Some(100));
2845 assert_eq!(s.config.auto_aof_rewrite_min_size.as_deref(), Some("64mb"));
2846 assert_eq!(s.config.no_appendfsync_on_rewrite, Some(true));
2847 }
2848
2849 #[test]
2850 fn memory_eviction_and_lazyfree() {
2851 let s = RedisServer::new()
2852 .maxmemory("256mb")
2853 .maxmemory_policy("allkeys-lfu")
2854 .maxmemory_samples(10)
2855 .maxmemory_clients("0")
2856 .maxmemory_eviction_tenacity(50)
2857 .lfu_log_factor(10)
2858 .lfu_decay_time(1)
2859 .active_expire_effort(25)
2860 .lazyfree_lazy_eviction(true)
2861 .lazyfree_lazy_expire(true)
2862 .lazyfree_lazy_server_del(true)
2863 .lazyfree_lazy_user_del(false)
2864 .lazyfree_lazy_user_flush(true);
2865
2866 assert_eq!(s.config.maxmemory.as_deref(), Some("256mb"));
2867 assert_eq!(s.config.maxmemory_policy.as_deref(), Some("allkeys-lfu"));
2868 assert_eq!(s.config.maxmemory_samples, Some(10));
2869 assert_eq!(s.config.maxmemory_clients.as_deref(), Some("0"));
2870 assert_eq!(s.config.maxmemory_eviction_tenacity, Some(50));
2871 assert_eq!(s.config.lfu_log_factor, Some(10));
2872 assert_eq!(s.config.lfu_decay_time, Some(1));
2873 assert_eq!(s.config.active_expire_effort, Some(25));
2874 assert_eq!(s.config.lazyfree_lazy_eviction, Some(true));
2875 assert_eq!(s.config.lazyfree_lazy_expire, Some(true));
2876 assert_eq!(s.config.lazyfree_lazy_server_del, Some(true));
2877 assert_eq!(s.config.lazyfree_lazy_user_del, Some(false));
2878 assert_eq!(s.config.lazyfree_lazy_user_flush, Some(true));
2879 }
2880
2881 #[test]
2882 fn replication_tuning() {
2883 let s = RedisServer::new()
2884 .replicaof("127.0.0.1", 6379)
2885 .masterauth("secret")
2886 .masteruser("repl-user")
2887 .repl_backlog_size("1mb")
2888 .repl_backlog_ttl(3600)
2889 .repl_disable_tcp_nodelay(true)
2890 .repl_diskless_load(ReplDisklessLoad::Swapdb)
2891 .repl_diskless_sync(true)
2892 .repl_diskless_sync_delay(5)
2893 .repl_diskless_sync_max_replicas(3)
2894 .repl_ping_replica_period(10)
2895 .repl_timeout(60)
2896 .replica_announce_ip("10.0.0.1")
2897 .replica_announce_port(6380)
2898 .replica_announced(true)
2899 .replica_full_sync_buffer_limit("256mb")
2900 .replica_ignore_disk_write_errors(false)
2901 .replica_ignore_maxmemory(true)
2902 .replica_lazy_flush(true)
2903 .replica_priority(100)
2904 .replica_read_only(true)
2905 .replica_serve_stale_data(false)
2906 .min_replicas_to_write(2)
2907 .min_replicas_max_lag(10);
2908
2909 assert_eq!(s.config.replicaof, Some(("127.0.0.1".into(), 6379)));
2910 assert_eq!(s.config.masterauth.as_deref(), Some("secret"));
2911 assert_eq!(s.config.masteruser.as_deref(), Some("repl-user"));
2912 assert_eq!(s.config.repl_backlog_size.as_deref(), Some("1mb"));
2913 assert_eq!(s.config.repl_backlog_ttl, Some(3600));
2914 assert_eq!(s.config.repl_disable_tcp_nodelay, Some(true));
2915 assert!(matches!(
2916 s.config.repl_diskless_load,
2917 Some(ReplDisklessLoad::Swapdb)
2918 ));
2919 assert_eq!(s.config.repl_diskless_sync, Some(true));
2920 assert_eq!(s.config.repl_diskless_sync_delay, Some(5));
2921 assert_eq!(s.config.repl_diskless_sync_max_replicas, Some(3));
2922 assert_eq!(s.config.repl_ping_replica_period, Some(10));
2923 assert_eq!(s.config.repl_timeout, Some(60));
2924 assert_eq!(s.config.replica_announce_ip.as_deref(), Some("10.0.0.1"));
2925 assert_eq!(s.config.replica_announce_port, Some(6380));
2926 assert_eq!(s.config.replica_announced, Some(true));
2927 assert_eq!(
2928 s.config.replica_full_sync_buffer_limit.as_deref(),
2929 Some("256mb")
2930 );
2931 assert_eq!(s.config.replica_ignore_disk_write_errors, Some(false));
2932 assert_eq!(s.config.replica_ignore_maxmemory, Some(true));
2933 assert_eq!(s.config.replica_lazy_flush, Some(true));
2934 assert_eq!(s.config.replica_priority, Some(100));
2935 assert_eq!(s.config.replica_read_only, Some(true));
2936 assert_eq!(s.config.replica_serve_stale_data, Some(false));
2937 assert_eq!(s.config.min_replicas_to_write, Some(2));
2938 assert_eq!(s.config.min_replicas_max_lag, Some(10));
2939 }
2940
2941 #[test]
2942 fn cluster_config() {
2943 let s = RedisServer::new()
2944 .port(7000)
2945 .cluster_enabled(true)
2946 .cluster_node_timeout(5000)
2947 .cluster_config_file("/tmp/nodes.conf")
2948 .cluster_require_full_coverage(false)
2949 .cluster_allow_reads_when_down(true)
2950 .cluster_allow_pubsubshard_when_down(true)
2951 .cluster_allow_replica_migration(true)
2952 .cluster_migration_barrier(1)
2953 .cluster_replica_no_failover(false)
2954 .cluster_replica_validity_factor(10)
2955 .cluster_announce_ip("10.0.0.1")
2956 .cluster_announce_port(7000)
2957 .cluster_announce_bus_port(17000)
2958 .cluster_announce_tls_port(7100)
2959 .cluster_announce_hostname("node1.example.com")
2960 .cluster_announce_human_nodename("node-1")
2961 .cluster_port(17000)
2962 .cluster_preferred_endpoint_type("ip")
2963 .cluster_link_sendbuf_limit(67108864)
2964 .cluster_compatibility_sample_ratio(50)
2965 .cluster_slot_migration_handoff_max_lag_bytes(1048576)
2966 .cluster_slot_migration_write_pause_timeout(5000)
2967 .cluster_slot_stats_enabled(true);
2968
2969 assert!(s.config.cluster_enabled);
2970 assert_eq!(s.config.cluster_node_timeout, Some(5000));
2971 assert_eq!(
2972 s.config.cluster_config_file,
2973 Some(PathBuf::from("/tmp/nodes.conf"))
2974 );
2975 assert_eq!(s.config.cluster_require_full_coverage, Some(false));
2976 assert_eq!(s.config.cluster_allow_reads_when_down, Some(true));
2977 assert_eq!(s.config.cluster_allow_pubsubshard_when_down, Some(true));
2978 assert_eq!(s.config.cluster_allow_replica_migration, Some(true));
2979 assert_eq!(s.config.cluster_migration_barrier, Some(1));
2980 assert_eq!(s.config.cluster_replica_no_failover, Some(false));
2981 assert_eq!(s.config.cluster_replica_validity_factor, Some(10));
2982 assert_eq!(s.config.cluster_announce_ip.as_deref(), Some("10.0.0.1"));
2983 assert_eq!(s.config.cluster_announce_port, Some(7000));
2984 assert_eq!(s.config.cluster_announce_bus_port, Some(17000));
2985 assert_eq!(s.config.cluster_announce_tls_port, Some(7100));
2986 assert_eq!(
2987 s.config.cluster_announce_hostname.as_deref(),
2988 Some("node1.example.com")
2989 );
2990 assert_eq!(
2991 s.config.cluster_announce_human_nodename.as_deref(),
2992 Some("node-1")
2993 );
2994 assert_eq!(s.config.cluster_port, Some(17000));
2995 assert_eq!(
2996 s.config.cluster_preferred_endpoint_type.as_deref(),
2997 Some("ip")
2998 );
2999 assert_eq!(s.config.cluster_link_sendbuf_limit, Some(67108864));
3000 assert_eq!(s.config.cluster_compatibility_sample_ratio, Some(50));
3001 assert_eq!(
3002 s.config.cluster_slot_migration_handoff_max_lag_bytes,
3003 Some(1048576)
3004 );
3005 assert_eq!(
3006 s.config.cluster_slot_migration_write_pause_timeout,
3007 Some(5000)
3008 );
3009 assert_eq!(s.config.cluster_slot_stats_enabled, Some(true));
3010 }
3011
3012 #[test]
3013 fn data_structure_tuning() {
3014 let s = RedisServer::new()
3015 .hash_max_listpack_entries(128)
3016 .hash_max_listpack_value(64)
3017 .list_max_listpack_size(-2)
3018 .list_compress_depth(1)
3019 .set_max_intset_entries(512)
3020 .set_max_listpack_entries(128)
3021 .set_max_listpack_value(64)
3022 .zset_max_listpack_entries(128)
3023 .zset_max_listpack_value(64)
3024 .hll_sparse_max_bytes(3000)
3025 .stream_node_max_bytes(4096)
3026 .stream_node_max_entries(100)
3027 .stream_idmp_duration(5000)
3028 .stream_idmp_maxsize(1000);
3029
3030 assert_eq!(s.config.hash_max_listpack_entries, Some(128));
3031 assert_eq!(s.config.hash_max_listpack_value, Some(64));
3032 assert_eq!(s.config.list_max_listpack_size, Some(-2));
3033 assert_eq!(s.config.list_compress_depth, Some(1));
3034 assert_eq!(s.config.set_max_intset_entries, Some(512));
3035 assert_eq!(s.config.set_max_listpack_entries, Some(128));
3036 assert_eq!(s.config.set_max_listpack_value, Some(64));
3037 assert_eq!(s.config.zset_max_listpack_entries, Some(128));
3038 assert_eq!(s.config.zset_max_listpack_value, Some(64));
3039 assert_eq!(s.config.hll_sparse_max_bytes, Some(3000));
3040 assert_eq!(s.config.stream_node_max_bytes, Some(4096));
3041 assert_eq!(s.config.stream_node_max_entries, Some(100));
3042 assert_eq!(s.config.stream_idmp_duration, Some(5000));
3043 assert_eq!(s.config.stream_idmp_maxsize, Some(1000));
3044 }
3045
3046 #[test]
3047 fn tls_config() {
3048 let s = RedisServer::new()
3049 .port(6400)
3050 .tls_port(6401)
3051 .tls_cert_file("/etc/tls/redis.crt")
3052 .tls_key_file("/etc/tls/redis.key")
3053 .tls_key_file_pass("keypass")
3054 .tls_ca_cert_file("/etc/tls/ca.crt")
3055 .tls_ca_cert_dir("/etc/tls/certs")
3056 .tls_auth_clients(true)
3057 .tls_client_cert_file("/etc/tls/client.crt")
3058 .tls_client_key_file("/etc/tls/client.key")
3059 .tls_client_key_file_pass("clientpass")
3060 .tls_dh_params_file("/etc/tls/dhparams.pem")
3061 .tls_ciphers("ECDHE-RSA-AES256-GCM-SHA384")
3062 .tls_ciphersuites("TLS_AES_256_GCM_SHA384")
3063 .tls_protocols("TLSv1.2 TLSv1.3")
3064 .tls_prefer_server_ciphers(true)
3065 .tls_session_caching(true)
3066 .tls_session_cache_size(20480)
3067 .tls_session_cache_timeout(300)
3068 .tls_replication(true)
3069 .tls_cluster(true);
3070
3071 assert_eq!(s.config.tls_port, Some(6401));
3072 assert_eq!(
3073 s.config.tls_cert_file.as_deref(),
3074 Some(std::path::Path::new("/etc/tls/redis.crt"))
3075 );
3076 assert_eq!(
3077 s.config.tls_key_file.as_deref(),
3078 Some(std::path::Path::new("/etc/tls/redis.key"))
3079 );
3080 assert_eq!(s.config.tls_key_file_pass.as_deref(), Some("keypass"));
3081 assert_eq!(
3082 s.config.tls_ca_cert_file.as_deref(),
3083 Some(std::path::Path::new("/etc/tls/ca.crt"))
3084 );
3085 assert_eq!(
3086 s.config.tls_ca_cert_dir.as_deref(),
3087 Some(std::path::Path::new("/etc/tls/certs"))
3088 );
3089 assert_eq!(s.config.tls_auth_clients, Some(true));
3090 assert_eq!(
3091 s.config.tls_client_cert_file.as_deref(),
3092 Some(std::path::Path::new("/etc/tls/client.crt"))
3093 );
3094 assert_eq!(
3095 s.config.tls_client_key_file.as_deref(),
3096 Some(std::path::Path::new("/etc/tls/client.key"))
3097 );
3098 assert_eq!(
3099 s.config.tls_client_key_file_pass.as_deref(),
3100 Some("clientpass")
3101 );
3102 assert_eq!(
3103 s.config.tls_dh_params_file.as_deref(),
3104 Some(std::path::Path::new("/etc/tls/dhparams.pem"))
3105 );
3106 assert_eq!(
3107 s.config.tls_ciphers.as_deref(),
3108 Some("ECDHE-RSA-AES256-GCM-SHA384")
3109 );
3110 assert_eq!(
3111 s.config.tls_ciphersuites.as_deref(),
3112 Some("TLS_AES_256_GCM_SHA384")
3113 );
3114 assert_eq!(s.config.tls_protocols.as_deref(), Some("TLSv1.2 TLSv1.3"));
3115 assert_eq!(s.config.tls_prefer_server_ciphers, Some(true));
3116 assert_eq!(s.config.tls_session_caching, Some(true));
3117 assert_eq!(s.config.tls_session_cache_size, Some(20480));
3118 assert_eq!(s.config.tls_session_cache_timeout, Some(300));
3119 assert_eq!(s.config.tls_replication, Some(true));
3120 assert_eq!(s.config.tls_cluster, Some(true));
3121 }
3122}