1use std::str::FromStr;
13use std::time::Duration;
14
15use anyhow::anyhow;
16use mz_dyncfg::{Config, ConfigSet, ConfigUpdates, ParameterScope};
17use mz_tracing::params::TracingParameters;
18use mz_tracing::{CloneableEnvFilter, SerializableDirective};
19use tracing_subscriber::filter::Directive;
20
21pub const SIGTERM_CONNECTION_WAIT: Config<Duration> = Config::new(
27 "balancerd_sigterm_connection_wait",
28 Duration::from_secs(60 * 9),
29 "Duration to wait after listeners closed via SIGTERM for outstanding connections to complete.",
30 ParameterScope::Environment,
31);
32
33pub const SIGTERM_LISTEN_WAIT: Config<Duration> = Config::new(
35 "balancerd_sigterm_listen_wait",
36 Duration::from_secs(60),
37 "Duration to wait after SIGTERM to begin shutdown of servers.",
38 ParameterScope::Environment,
39);
40
41pub const INJECT_PROXY_PROTOCOL_HEADER_HTTP: Config<bool> = Config::new(
43 "balancerd_inject_proxy_protocol_header_http",
44 false,
45 "Whether to inject tcp proxy protocol headers to downstream http servers.",
46 ParameterScope::Environment,
47);
48
49pub const MAX_CONNECTIONS: Config<u32> = Config::new(
51 "balancerd_max_connections",
52 5000,
53 "Maximum number of client connections to proxy at once, across the pgwire and HTTPS \
54 listeners. Connections beyond this are rejected. Zero disables the limit.",
55 ParameterScope::Environment,
56);
57
58pub const CONNECTION_HIGH_WATERMARK: Config<usize> = Config::new(
60 "balancerd_connection_high_watermark",
61 0,
62 "Number of open connections at which balancerd reports not ready on /api/readyz, so \
63 load balancers stop sending it new connections while existing connections continue. \
64 Takes effect only when balancerd_connection_low_watermark is also non-zero, the low \
65 watermark is below the high watermark, and the high watermark is below a non-zero \
66 balancerd_max_connections. Zero disables the watermarks.",
67 ParameterScope::Environment,
68);
69
70pub const CONNECTION_LOW_WATERMARK: Config<usize> = Config::new(
72 "balancerd_connection_low_watermark",
73 0,
74 "Number of open connections below which balancerd reports ready again after reaching \
75 balancerd_connection_high_watermark. Must be below the high watermark. The gap \
76 between the two keeps a balancer near the threshold from repeatedly entering and \
77 leaving load balancer rotation. Zero disables the watermarks.",
78 ParameterScope::Environment,
79);
80
81pub const PRE_RESOLVED_TIMEOUT: Config<Duration> = Config::new(
86 "balancerd_pre_resolved_timeout",
87 Duration::from_secs(60),
88 "How long a client has to complete the TLS handshake, the startup sequence and any credential \
89 exchange before the connection is closed. Zero disables the deadline.",
90 ParameterScope::Environment,
91);
92
93pub const LOGGING_FILTER: Config<&str> = Config::new(
95 "balancerd_log_filter",
96 "info",
97 "Sets the filter to apply to stderr logging.",
98 ParameterScope::Environment,
99);
100
101pub const OPENTELEMETRY_FILTER: Config<&str> = Config::new(
103 "balancerd_opentelemetry_filter",
104 "info",
105 "Sets the filter to apply to OpenTelemetry-backed distributed tracing.",
106 ParameterScope::Environment,
107);
108
109pub const LOGGING_FILTER_DEFAULTS: Config<fn() -> String> = Config::new(
113 "balancerd_log_filter_defaults",
114 || mz_ore::tracing::LOGGING_DEFAULTS_STR.join(","),
115 "Sets additional default directives to apply to stderr logging. \
116 These apply to all variations of `log_filter`. Directives other than \
117 `module=off` are likely incorrect. Comma separated list.",
118 ParameterScope::Environment,
119);
120
121pub const OPENTELEMETRY_FILTER_DEFAULTS: Config<fn() -> String> = Config::new(
126 "balancerd_opentelemetry_filter_defaults",
127 || mz_ore::tracing::OPENTELEMETRY_DEFAULTS_STR.join(","),
128 "Sets additional default directives to apply to OpenTelemetry-backed \
129 distributed tracing. \
130 These apply to all variations of `opentelemetry_filter`. Directives other than \
131 `module=off` are likely incorrect. Comma separated list.",
132 ParameterScope::Environment,
133);
134
135pub const SENTRY_FILTERS: Config<fn() -> String> = Config::new(
139 "balancerd_sentry_filters",
140 || mz_ore::tracing::SENTRY_DEFAULTS_STR.join(","),
141 "Sets additional default directives to apply to sentry logging. \
142 These apply on top of a default `info` directive. Directives other than \
143 `module=off` are likely incorrect. Comma separated list.",
144 ParameterScope::Environment,
145);
146
147pub fn all_dyncfgs(configs: ConfigSet) -> ConfigSet {
149 configs
150 .add(&SIGTERM_CONNECTION_WAIT)
151 .add(&SIGTERM_LISTEN_WAIT)
152 .add(&INJECT_PROXY_PROTOCOL_HEADER_HTTP)
153 .add(&MAX_CONNECTIONS)
154 .add(&CONNECTION_HIGH_WATERMARK)
155 .add(&CONNECTION_LOW_WATERMARK)
156 .add(&PRE_RESOLVED_TIMEOUT)
157 .add(&LOGGING_FILTER)
158 .add(&OPENTELEMETRY_FILTER)
159 .add(&LOGGING_FILTER_DEFAULTS)
160 .add(&OPENTELEMETRY_FILTER_DEFAULTS)
161 .add(&SENTRY_FILTERS)
162}
163
164pub(crate) fn set_defaults(
173 config_set: &ConfigSet,
174 default_config: Vec<(String, String)>,
175) -> Result<(), anyhow::Error> {
176 let mut config_updates = ConfigUpdates::default();
177 for (k, v) in default_config.iter() {
178 if k.as_str() == INJECT_PROXY_PROTOCOL_HEADER_HTTP.name() {
179 config_updates.add_dynamic(
180 INJECT_PROXY_PROTOCOL_HEADER_HTTP.name(),
181 mz_dyncfg::ConfigVal::Bool(bool::from_str(v)?),
182 )
183 } else if k.as_str() == MAX_CONNECTIONS.name() {
184 config_updates.add_dynamic(
185 MAX_CONNECTIONS.name(),
186 mz_dyncfg::ConfigVal::U32(u32::from_str(v)?),
187 )
188 } else if k.as_str() == CONNECTION_HIGH_WATERMARK.name() {
189 config_updates.add_dynamic(
190 CONNECTION_HIGH_WATERMARK.name(),
191 mz_dyncfg::ConfigVal::Usize(usize::from_str(v)?),
192 )
193 } else if k.as_str() == CONNECTION_LOW_WATERMARK.name() {
194 config_updates.add_dynamic(
195 CONNECTION_LOW_WATERMARK.name(),
196 mz_dyncfg::ConfigVal::Usize(usize::from_str(v)?),
197 )
198 } else if k.as_str() == PRE_RESOLVED_TIMEOUT.name() {
199 config_updates.add_dynamic(
200 PRE_RESOLVED_TIMEOUT.name(),
201 mz_dyncfg::ConfigVal::Duration(humantime::parse_duration(v)?),
202 )
203 } else {
204 return Err(anyhow!("Invalid default config value {k}"));
205 }
206 }
207 config_updates.apply(config_set);
208 Ok(())
209}
210
211pub fn tracing_config(configs: &ConfigSet) -> Result<TracingParameters, String> {
213 fn to_serializable_directives(
214 config: &Config<fn() -> String>,
215 configs: &ConfigSet,
216 ) -> Result<Vec<SerializableDirective>, String> {
217 let directives = config.get(configs);
218 let directives: Vec<_> = directives
219 .split(',')
220 .map(Directive::from_str)
221 .collect::<Result<_, _>>()
222 .map_err(|e| e.to_string())?;
223 Ok(directives.into_iter().map(|d| d.into()).collect())
224 }
225
226 let log_filter = LOGGING_FILTER.get(configs);
227 let log_filter = CloneableEnvFilter::from_str(&log_filter).map_err(|e| e.to_string())?;
228
229 let opentelemetry_filter = OPENTELEMETRY_FILTER.get(configs);
230 let opentelemetry_filter =
231 CloneableEnvFilter::from_str(&opentelemetry_filter).map_err(|e| e.to_string())?;
232
233 let log_filter_defaults = to_serializable_directives(&LOGGING_FILTER_DEFAULTS, configs)?;
234
235 let opentelemetry_filter_defaults =
236 to_serializable_directives(&OPENTELEMETRY_FILTER_DEFAULTS, configs)?;
237
238 let sentry_filters = to_serializable_directives(&SENTRY_FILTERS, configs)?;
239
240 Ok(TracingParameters {
241 log_filter: Some(log_filter),
242 opentelemetry_filter: Some(opentelemetry_filter),
243 log_filter_defaults,
244 opentelemetry_filter_defaults,
245 sentry_filters,
246 })
247}
248
249pub fn has_tracing_config_update(updates: &ConfigUpdates) -> bool {
251 [
252 LOGGING_FILTER.name(),
253 OPENTELEMETRY_FILTER.name(),
254 LOGGING_FILTER_DEFAULTS.name(),
255 OPENTELEMETRY_FILTER_DEFAULTS.name(),
256 SENTRY_FILTERS.name(),
257 ]
258 .into_iter()
259 .any(|name| updates.updates.contains_key(name))
260}