1mod iceberg_query_storage_mode;
16mod non_zero64;
17mod opt;
18pub mod parallelism;
19mod query_mode;
20mod search_path;
21pub mod sink_decouple;
22mod statement_timeout;
23mod transaction_isolation_level;
24mod visibility_mode;
25
26use chrono_tz::Tz;
27pub use iceberg_query_storage_mode::IcebergQueryStorageMode;
28use itertools::Itertools;
29pub use opt::OptionConfig;
30pub use query_mode::QueryMode;
31use risingwave_common_proc_macro::{ConfigDoc, SessionConfig};
32pub use search_path::{SearchPath, USER_NAME_WILD_CARD};
33use serde::{Deserialize, Serialize};
34pub use statement_timeout::StatementTimeout;
35use thiserror::Error;
36
37use self::non_zero64::ConfigNonZeroU64;
38use crate::config::mutate::TomlTableMutateExt;
39use crate::config::streaming::{CacheRefillPolicy, JoinEncodingType, OverWindowCachePolicy};
40use crate::config::{ConfigMergeError, StreamingConfig, merge_streaming_config_section};
41use crate::hash::VirtualNode;
42use crate::session_config::parallelism::{ConfigBackfillParallelism, ConfigParallelism};
43use crate::session_config::sink_decouple::SinkDecouple;
44use crate::session_config::transaction_isolation_level::IsolationLevel;
45pub use crate::session_config::visibility_mode::VisibilityMode;
46use crate::{PG_VERSION, SERVER_ENCODING, SERVER_VERSION_NUM, STANDARD_CONFORMING_STRINGS};
47
48pub const SESSION_CONFIG_LIST_SEP: &str = ", ";
49
50#[derive(Error, Debug)]
51pub enum SessionConfigError {
52 #[error("Invalid value `{value}` for `{entry}`")]
53 InvalidValue {
54 entry: &'static str,
55 value: String,
56 source: anyhow::Error,
57 },
58
59 #[error("Unrecognized config entry `{0}`")]
60 UnrecognizedEntry(String),
61}
62
63type SessionConfigResult<T> = std::result::Result<T, SessionConfigError>;
64
65const DISABLE_BACKFILL_RATE_LIMIT: i32 = -1;
68const DISABLE_SOURCE_RATE_LIMIT: i32 = -1;
69const DISABLE_DML_RATE_LIMIT: i32 = -1;
70const DISABLE_SINK_RATE_LIMIT: i32 = -1;
71
72const BYPASS_CLUSTER_LIMITS: bool = cfg!(debug_assertions);
74
75#[serde_with::apply(_ => #[serde_as(as = "serde_with::DisplayFromStr")] )]
87#[serde_with::serde_as]
88#[derive(Clone, Debug, Deserialize, Serialize, SessionConfig, ConfigDoc, PartialEq)]
89pub struct SessionConfig {
90 #[parameter(default = false, alias = "rw_implicit_flush")]
94 implicit_flush: bool,
95
96 #[parameter(default = false)]
99 dml_wait_persistence: bool,
100
101 #[parameter(default = false)]
104 create_compaction_group_for_mv: bool,
105
106 #[parameter(default = QueryMode::default())]
110 query_mode: QueryMode,
111
112 #[parameter(default = IcebergQueryStorageMode::default())]
115 iceberg_query_storage_mode: IcebergQueryStorageMode,
116
117 #[parameter(default = 1)]
120 extra_float_digits: i32,
121
122 #[parameter(default = "", flags = "REPORT")]
125 application_name: String,
126
127 #[parameter(default = "", rename = "datestyle")]
130 date_style: String,
131
132 #[parameter(default = true, alias = "rw_batch_enable_lookup_join")]
134 batch_enable_lookup_join: bool,
135
136 #[parameter(default = true, alias = "rw_batch_enable_sort_agg")]
139 batch_enable_sort_agg: bool,
140
141 #[parameter(default = false, rename = "batch_enable_distributed_dml")]
144 batch_enable_distributed_dml: bool,
145
146 #[parameter(default = true)]
150 batch_expr_strict_mode: bool,
151
152 #[parameter(default = 8)]
154 max_split_range_gap: i32,
155
156 #[parameter(default = SearchPath::default())]
160 search_path: SearchPath,
161
162 #[parameter(default = VisibilityMode::default())]
164 visibility_mode: VisibilityMode,
165
166 #[parameter(default = IsolationLevel::default())]
168 transaction_isolation: IsolationLevel,
169
170 #[parameter(default = ConfigNonZeroU64::default())]
173 query_epoch: ConfigNonZeroU64,
174
175 #[parameter(default = "UTC", check_hook = check_timezone)]
177 timezone: String,
178
179 #[parameter(default = ConfigParallelism::Default, flags = "SESSION_INIT")]
183 streaming_parallelism: ConfigParallelism,
184
185 #[parameter(
188 default = ConfigBackfillParallelism::Default,
189 check_hook = check_streaming_parallelism_for_backfill,
190 flags = "SESSION_INIT"
191 )]
192 streaming_parallelism_for_backfill: ConfigBackfillParallelism,
193
194 #[parameter(default = ConfigParallelism::Default, flags = "SESSION_INIT")]
198 streaming_parallelism_for_table: ConfigParallelism,
199
200 #[parameter(default = ConfigParallelism::Default, flags = "SESSION_INIT")]
202 streaming_parallelism_for_sink: ConfigParallelism,
203
204 #[parameter(default = ConfigParallelism::Default, flags = "SESSION_INIT")]
206 streaming_parallelism_for_index: ConfigParallelism,
207
208 #[parameter(default = ConfigParallelism::Default, flags = "SESSION_INIT")]
212 streaming_parallelism_for_source: ConfigParallelism,
213
214 #[parameter(default = ConfigParallelism::Default, flags = "SESSION_INIT")]
216 streaming_parallelism_for_materialized_view: ConfigParallelism,
217
218 #[parameter(default = false, alias = "rw_streaming_enable_delta_join")]
220 streaming_enable_delta_join: bool,
221
222 #[parameter(default = true, alias = "rw_streaming_enable_bushy_join")]
224 streaming_enable_bushy_join: bool,
225
226 #[parameter(default = false, alias = "rw_streaming_force_filter_inside_join")]
229 streaming_force_filter_inside_join: bool,
230
231 #[parameter(
234 default = true,
235 deprecated = "The session variable STREAMING_USE_ARRANGEMENT_BACKFILL has been deprecated and is ignored. Arrangement backfill is always used as the fallback backfill type for new streaming jobs."
236 )]
237 streaming_use_arrangement_backfill: bool,
238
239 #[parameter(default = true)]
240 streaming_use_snapshot_backfill: bool,
241
242 #[parameter(default = false)]
244 enable_serverless_backfill: bool,
245
246 #[parameter(default = false, alias = "rw_streaming_allow_jsonb_in_stream_key")]
248 streaming_allow_jsonb_in_stream_key: bool,
249
250 #[parameter(default = false)]
254 streaming_unsafe_allow_unmaterialized_impure_expr: bool,
255
256 #[parameter(default = false)]
262 streaming_unsafe_allow_upsert_sink_pk_mismatch: bool,
263
264 #[parameter(default = false)]
266 streaming_separate_consecutive_join: bool,
267
268 #[parameter(default = false)]
270 streaming_separate_sink: bool,
271
272 #[parameter(default = None)]
277 streaming_join_encoding: OptionConfig<JoinEncodingType>,
278
279 #[parameter(default = true, alias = "rw_enable_join_ordering")]
281 enable_join_ordering: bool,
282
283 #[parameter(default = true, flags = "SETTER", alias = "rw_enable_two_phase_agg")]
286 enable_two_phase_agg: bool,
287
288 #[parameter(default = false, flags = "SETTER", alias = "rw_force_two_phase_agg")]
292 force_two_phase_agg: bool,
293
294 #[parameter(default = true, alias = "rw_enable_share_plan")]
297 enable_share_plan: bool,
299
300 #[parameter(default = false, alias = "rw_force_split_distinct_agg")]
302 force_split_distinct_agg: bool,
303
304 #[parameter(default = "", rename = "intervalstyle")]
306 interval_style: String,
307
308 #[parameter(default = ConfigNonZeroU64::default())]
310 batch_parallelism: ConfigNonZeroU64,
311
312 #[parameter(default = PG_VERSION)]
314 server_version: String,
315
316 #[parameter(default = SERVER_VERSION_NUM)]
318 server_version_num: i32,
319
320 #[parameter(default = "notice")]
322 client_min_messages: String,
323
324 #[parameter(default = SERVER_ENCODING, check_hook = check_client_encoding)]
326 client_encoding: String,
327
328 #[parameter(default = SinkDecouple::default())]
330 sink_decouple: SinkDecouple,
331
332 #[parameter(default = false)]
335 synchronize_seqscans: bool,
336
337 #[parameter(default = StatementTimeout::default())]
342 statement_timeout: StatementTimeout,
343
344 #[parameter(default = 60000u32)]
346 idle_in_transaction_session_timeout: u32,
347
348 #[parameter(default = 0)]
351 lock_timeout: i32,
352
353 #[parameter(default = 60)]
355 cdc_source_wait_streaming_start_timeout: i32,
356
357 #[parameter(default = true)]
360 row_security: bool,
361
362 #[parameter(default = STANDARD_CONFORMING_STRINGS)]
364 standard_conforming_strings: String,
365
366 #[parameter(default = DISABLE_BACKFILL_RATE_LIMIT)]
370 backfill_rate_limit: i32,
371
372 #[parameter(default = DISABLE_SOURCE_RATE_LIMIT)]
376 source_rate_limit: i32,
377
378 #[parameter(default = DISABLE_DML_RATE_LIMIT)]
382 dml_rate_limit: i32,
383
384 #[parameter(default = DISABLE_SINK_RATE_LIMIT)]
388 sink_rate_limit: i32,
389
390 #[parameter(default = None, alias = "rw_streaming_over_window_cache_policy")]
396 streaming_over_window_cache_policy: OptionConfig<OverWindowCachePolicy>,
397
398 #[parameter(default = None)]
404 streaming_cache_refill_policy: OptionConfig<CacheRefillPolicy>,
405
406 #[parameter(default = false)]
408 background_ddl: bool,
409
410 #[parameter(default = true)]
415 streaming_use_shared_source: bool,
416
417 #[parameter(default = true)]
424 streaming_asof_join_use_cache: bool,
425
426 #[parameter(default = SERVER_ENCODING)]
428 server_encoding: String,
429
430 #[parameter(default = "hex", check_hook = check_bytea_output)]
431 bytea_output: String,
432
433 #[parameter(default = BYPASS_CLUSTER_LIMITS)]
437 bypass_cluster_limits: bool,
438
439 #[parameter(default = VirtualNode::COUNT_FOR_COMPAT, check_hook = check_streaming_max_parallelism)]
449 streaming_max_parallelism: usize,
450
451 #[parameter(default = "", check_hook = check_iceberg_engine_connection)]
454 iceberg_engine_connection: String,
455
456 #[parameter(default = false)]
458 streaming_enable_unaligned_join: bool,
459
460 #[parameter(default = None)]
467 streaming_sync_log_store_pause_duration_ms: OptionConfig<usize>,
468
469 #[parameter(default = None)]
474 streaming_sync_log_store_buffer_size: OptionConfig<usize>,
475
476 #[parameter(default = false, flags = "NO_ALTER_SYS")]
480 disable_purify_definition: bool,
481
482 #[parameter(default = 40_usize)] batch_hnsw_ef_search: usize,
485
486 #[parameter(default = true)]
488 enable_index_selection: bool,
489
490 #[parameter(default = false)]
492 enable_mv_selection: bool,
493
494 #[parameter(default = false)]
496 enable_locality_backfill: bool,
497
498 #[parameter(default = 30u32)]
501 slow_ddl_notification_secs: u32,
502
503 #[parameter(default = false)]
507 unsafe_enable_storage_retention_for_non_append_only_tables: bool,
508
509 #[parameter(default = true)]
512 enable_datafusion_engine: bool,
513
514 #[parameter(default = true)]
518 datafusion_prefer_hash_join: bool,
519
520 #[parameter(default = false)]
527 upsert_dml: bool,
528}
529
530fn check_iceberg_engine_connection(val: &str) -> Result<(), String> {
531 if val.is_empty() {
532 return Ok(());
533 }
534
535 let parts: Vec<&str> = val.split('.').collect();
536 if parts.len() != 2 {
537 return Err("Invalid iceberg engine connection format, Should be set to this format: schema_name.connection_name.".to_owned());
538 }
539
540 Ok(())
541}
542
543fn check_timezone(val: &str) -> Result<(), String> {
544 Tz::from_str_insensitive(val).map_err(|_e| "Not a valid timezone")?;
546 Ok(())
547}
548
549fn check_client_encoding(val: &str) -> Result<(), String> {
550 let clean = val.replace(|c: char| !c.is_ascii_alphanumeric(), "");
552 if !clean.eq_ignore_ascii_case("UTF8") {
553 Err("Only support 'UTF8' for CLIENT_ENCODING".to_owned())
554 } else {
555 Ok(())
556 }
557}
558
559fn check_bytea_output(val: &str) -> Result<(), String> {
560 if val == "hex" {
561 Ok(())
562 } else {
563 Err("Only support 'hex' for BYTEA_OUTPUT".to_owned())
564 }
565}
566
567fn check_streaming_max_parallelism(val: &usize) -> Result<(), String> {
569 match val {
570 0 | 1 => Err("STREAMING_MAX_PARALLELISM must be greater than 1".to_owned()),
573 2..=VirtualNode::MAX_COUNT => Ok(()),
574 _ => Err(format!(
575 "STREAMING_MAX_PARALLELISM must be less than or equal to {}",
576 VirtualNode::MAX_COUNT
577 )),
578 }
579}
580
581fn check_streaming_parallelism_for_backfill(val: &ConfigBackfillParallelism) -> Result<(), String> {
582 match val {
583 ConfigBackfillParallelism::Default | ConfigBackfillParallelism::Fixed(_) => Ok(()),
584 ConfigBackfillParallelism::Adaptive
585 | ConfigBackfillParallelism::Bounded(_)
586 | ConfigBackfillParallelism::Ratio(_) => Err(
587 "Only `default` or fixed backfill parallelism is supported here; adaptive backfill strategy is deferred to a later change.".to_owned(),
588 ),
589 }
590}
591
592impl SessionConfig {
593 pub fn set_force_two_phase_agg(
594 &mut self,
595 val: bool,
596 reporter: &mut impl ConfigReporter,
597 ) -> SessionConfigResult<bool> {
598 let set_val = self.set_force_two_phase_agg_inner(val, reporter)?;
599 if self.force_two_phase_agg {
600 self.set_enable_two_phase_agg(true, reporter)
601 } else {
602 Ok(set_val)
603 }
604 }
605
606 pub fn set_enable_two_phase_agg(
607 &mut self,
608 val: bool,
609 reporter: &mut impl ConfigReporter,
610 ) -> SessionConfigResult<bool> {
611 let set_val = self.set_enable_two_phase_agg_inner(val, reporter)?;
612 if !self.force_two_phase_agg {
613 self.set_force_two_phase_agg(false, reporter)
614 } else {
615 Ok(set_val)
616 }
617 }
618}
619
620pub struct VariableInfo {
621 pub name: String,
622 pub setting: String,
623 pub description: String,
624}
625
626pub trait ConfigReporter {
628 fn report_status(&mut self, key: &str, new_val: String);
629}
630
631impl ConfigReporter for () {
633 fn report_status(&mut self, _key: &str, _new_val: String) {}
634}
635
636def_anyhow_newtype! {
637 pub SessionConfigToOverrideError,
638 toml::ser::Error => "failed to serialize session config",
639 ConfigMergeError => transparent,
640}
641
642impl SessionConfig {
643 pub fn to_initial_streaming_config_override(
645 &self,
646 ) -> Result<String, SessionConfigToOverrideError> {
647 let mut table = toml::Table::new();
648
649 if let Some(v) = self.streaming_join_encoding.as_ref() {
652 table
653 .upsert("streaming.developer.join_encoding_type", v)
654 .unwrap();
655 }
656 if let Some(v) = self.streaming_sync_log_store_pause_duration_ms.as_ref() {
657 table
658 .upsert("streaming.developer.sync_log_store_pause_duration_ms", v)
659 .unwrap();
660 }
661 if let Some(v) = self.streaming_sync_log_store_buffer_size.as_ref() {
662 table
663 .upsert("streaming.developer.sync_log_store_buffer_size", v)
664 .unwrap();
665 }
666 if let Some(v) = self.streaming_over_window_cache_policy.as_ref() {
667 table
668 .upsert("streaming.developer.over_window_cache_policy", v)
669 .unwrap();
670 }
671 if let Some(v) = self.streaming_cache_refill_policy.as_ref() {
672 table
673 .upsert("streaming.developer.cache_refill_policy", v)
674 .unwrap();
675 }
676
677 let res = toml::to_string(&table)?;
678
679 if !res.is_empty() {
681 let merged =
682 merge_streaming_config_section(&StreamingConfig::default(), res.as_str())?.unwrap();
683
684 let unrecognized_keys = merged.unrecognized_keys().collect_vec();
685 if !unrecognized_keys.is_empty() {
686 bail!("unrecognized configs: {:?}", unrecognized_keys);
687 }
688 }
689
690 Ok(res)
691 }
692}
693
694#[cfg(test)]
695mod test {
696 use expect_test::expect;
697
698 use super::*;
699
700 #[derive(SessionConfig)]
701 struct TestConfig {
702 #[parameter(default = 1, flags = "NO_ALTER_SYS", alias = "test_param_alias" | "alias_param_test")]
703 test_param: i32,
704 #[parameter(default = false, deprecated = "deprecated test notice")]
705 deprecated_test_param: bool,
706 }
707
708 #[test]
709 fn test_session_config_alias() {
710 let mut config = TestConfig::default();
711 config.set("test_param", "2".to_owned(), &mut ()).unwrap();
712 assert_eq!(config.get("test_param_alias").unwrap(), "2");
713 config
714 .set("alias_param_test", "3".to_owned(), &mut ())
715 .unwrap();
716 assert_eq!(config.get("test_param_alias").unwrap(), "3");
717 assert!(TestConfig::check_no_alter_sys("test_param").unwrap());
718 assert_eq!(
719 TestConfig::deprecated_notice("deprecated_test_param").unwrap(),
720 Some("deprecated test notice")
721 );
722 assert_eq!(TestConfig::deprecated_notice("test_param").unwrap(), None);
723 }
724
725 #[test]
726 fn test_initial_streaming_config_override() {
727 let mut config = SessionConfig::default();
728 config
729 .set_streaming_join_encoding(Some(JoinEncodingType::Cpu).into(), &mut ())
730 .unwrap();
731 config
732 .set_streaming_over_window_cache_policy(
733 Some(OverWindowCachePolicy::RecentFirstN).into(),
734 &mut (),
735 )
736 .unwrap();
737 config
738 .set_streaming_cache_refill_policy(Some(CacheRefillPolicy::Both).into(), &mut ())
739 .unwrap();
740
741 let override_str = config.to_initial_streaming_config_override().unwrap();
743 expect![[r#"
744 [streaming.developer]
745 cache_refill_policy = "both"
746 join_encoding_type = "cpu_optimized"
747 over_window_cache_policy = "recent_first_n"
748 "#]]
749 .assert_eq(&override_str);
750
751 let merged = merge_streaming_config_section(&StreamingConfig::default(), &override_str)
753 .unwrap()
754 .unwrap();
755 assert_eq!(merged.developer.join_encoding_type, JoinEncodingType::Cpu);
756 assert_eq!(
757 merged.developer.over_window_cache_policy,
758 OverWindowCachePolicy::RecentFirstN
759 );
760 assert_eq!(
761 merged.developer.cache_refill_policy,
762 CacheRefillPolicy::Both
763 );
764 }
765
766 #[test]
767 fn test_streaming_parallelism_defaults() {
768 let config = SessionConfig::default();
769
770 assert_eq!(config.streaming_parallelism(), ConfigParallelism::Default);
771 assert_eq!(
772 config.streaming_parallelism_for_table(),
773 ConfigParallelism::Default
774 );
775 assert_eq!(
776 config.streaming_parallelism_for_source(),
777 ConfigParallelism::Default
778 );
779 assert_eq!(
780 config.streaming_parallelism_for_sink(),
781 ConfigParallelism::Default
782 );
783 assert_eq!(
784 config.streaming_parallelism_for_index(),
785 ConfigParallelism::Default
786 );
787 assert_eq!(
788 config.streaming_parallelism_for_materialized_view(),
789 ConfigParallelism::Default
790 );
791 assert!(!config.streaming_unsafe_allow_upsert_sink_pk_mismatch());
792 }
793
794 #[test]
795 fn test_streaming_parallelism_default_round_trip() {
796 let mut config = SessionConfig::default();
797
798 assert_eq!(config.get("streaming_parallelism").unwrap(), "default");
799 assert_eq!(
800 config.get("streaming_parallelism_for_table").unwrap(),
801 "default"
802 );
803 assert_eq!(
804 config.get("streaming_parallelism_for_source").unwrap(),
805 "default"
806 );
807
808 config
809 .set("streaming_parallelism", "default".to_owned(), &mut ())
810 .unwrap();
811 assert_eq!(config.get("streaming_parallelism").unwrap(), "default");
812
813 config
814 .set("streaming_parallelism", "bounded(16)".to_owned(), &mut ())
815 .unwrap();
816 config
817 .set(
818 "streaming_parallelism_for_table",
819 "bounded(8)".to_owned(),
820 &mut (),
821 )
822 .unwrap();
823 config
824 .set(
825 "streaming_parallelism_for_source",
826 "bounded(8)".to_owned(),
827 &mut (),
828 )
829 .unwrap();
830
831 assert_eq!(
832 config.reset("streaming_parallelism", &mut ()).unwrap(),
833 "default"
834 );
835 assert_eq!(
836 config
837 .reset("streaming_parallelism_for_table", &mut ())
838 .unwrap(),
839 "default"
840 );
841 assert_eq!(
842 config
843 .reset("streaming_parallelism_for_source", &mut ())
844 .unwrap(),
845 "default"
846 );
847 }
848 #[test]
849 fn test_streaming_parallelism_for_backfill_accepts_default_and_fixed() {
850 let mut config = SessionConfig::default();
851
852 config
853 .set(
854 "streaming_parallelism_for_backfill",
855 "default".to_owned(),
856 &mut (),
857 )
858 .unwrap();
859 assert_eq!(
860 config.get("streaming_parallelism_for_backfill").unwrap(),
861 "default"
862 );
863
864 config
865 .set(
866 "streaming_parallelism_for_backfill",
867 "2".to_owned(),
868 &mut (),
869 )
870 .unwrap();
871 assert_eq!(config.streaming_parallelism_for_backfill().to_string(), "2");
872 }
873
874 #[test]
875 fn test_streaming_parallelism_for_backfill_rejects_adaptive_modes() {
876 let mut config = SessionConfig::default();
877 let expected = "Only `default` or fixed backfill parallelism is supported here; adaptive backfill strategy is deferred to a later change.";
878
879 for value in ["adaptive", "bounded(2)", "ratio(0.5)"] {
880 let err = config
881 .set(
882 "streaming_parallelism_for_backfill",
883 value.to_owned(),
884 &mut (),
885 )
886 .unwrap_err();
887
888 match err {
889 SessionConfigError::InvalidValue {
890 entry,
891 value: actual_value,
892 source,
893 } => {
894 assert_eq!(entry, "streaming_parallelism_for_backfill");
895 assert_eq!(actual_value, value);
896 assert_eq!(source.to_string(), expected);
897 }
898 other => panic!("unexpected error: {other:?}"),
899 }
900 }
901 }
902}