1use std::sync::atomic::{AtomicU64, Ordering};
16use std::sync::{Arc, LazyLock};
17
18use anyhow::Context;
19use await_tree::InstrumentAwait;
20use prometheus::{Registry, exponential_buckets};
21use risingwave_common::array::arrow::arrow_schema_udf::{Fields, Schema, SchemaRef};
22use risingwave_common::array::arrow::{UdfArrowConvert, UdfFromArrow, UdfToArrow};
23use risingwave_common::array::{Array, ArrayRef, DataChunk};
24use risingwave_common::metrics::*;
25use risingwave_common::monitor::GLOBAL_METRICS_REGISTRY;
26use risingwave_common::row::OwnedRow;
27use risingwave_common::types::{DataType, Datum};
28use risingwave_expr::expr_context::FRAGMENT_ID;
29use risingwave_pb::expr::ExprNode;
30
31use super::{AsyncExpressionBoxExt, BoxedExpression, BuildBoxed};
32use crate::expr::{AsyncExpression, ExpressionInfo};
33use crate::sig::{BuildOptions, UdfImpl, UdfKind};
34use crate::{ExprError, Result, bail};
35
36#[derive(Debug)]
37pub struct UserDefinedFunction {
38 children: Vec<BoxedExpression>,
39 arg_types: Vec<DataType>,
40 return_type: DataType,
41 arg_schema: SchemaRef,
42 runtime: Box<dyn UdfImpl>,
43 arrow_convert: UdfArrowConvert,
44 span: await_tree::Span,
45 metrics: Metrics,
46}
47
48impl ExpressionInfo for UserDefinedFunction {
49 fn return_type(&self) -> DataType {
50 self.return_type.clone()
51 }
52}
53
54impl AsyncExpression for UserDefinedFunction {
55 async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
56 if input.cardinality() == 0 {
57 let mut builder = self.return_type.create_array_builder(input.capacity());
59 builder.append_n_null(input.capacity());
60 return Ok(builder.finish().into_ref());
61 }
62 let mut columns = Vec::with_capacity(self.children.len());
63 for child in &self.children {
64 let array = child.eval(input).await?;
65 columns.push(array);
66 }
67 let chunk = DataChunk::new(columns, input.visibility().clone());
68 self.eval_inner(&chunk).await
69 }
70
71 async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
72 let mut columns = Vec::with_capacity(self.children.len());
73 for child in &self.children {
74 let datum = child.eval_row(input).await?;
75 columns.push(datum);
76 }
77 let arg_row = OwnedRow::new(columns);
78 let chunk = DataChunk::from_rows(std::slice::from_ref(&arg_row), &self.arg_types);
79 let output_array = self.eval_inner(&chunk).await?;
80 Ok(output_array.to_datum())
81 }
82}
83
84impl UserDefinedFunction {
85 async fn eval_inner(&self, input: &DataChunk) -> Result<ArrayRef> {
86 let arrow_input = self
88 .arrow_convert
89 .to_record_batch(self.arg_schema.clone(), input)?;
90
91 self.metrics
93 .input_chunk_rows
94 .observe(arrow_input.num_rows() as f64);
95 self.metrics
96 .input_rows
97 .inc_by(arrow_input.num_rows() as u64);
98 self.metrics
99 .input_bytes
100 .inc_by(arrow_input.get_array_memory_size() as u64);
101 let timer = self.metrics.latency.start_timer();
102
103 let arrow_output_result = self
104 .runtime
105 .call(&arrow_input)
106 .instrument_await(self.span.clone())
107 .await;
108
109 timer.stop_and_record();
110 if arrow_output_result.is_ok() {
111 &self.metrics.success_count
112 } else {
113 &self.metrics.failure_count
114 }
115 .inc();
116 self.metrics
118 .memory_usage_bytes
119 .set(self.runtime.memory_usage() as i64);
120
121 let arrow_output = arrow_output_result?;
122
123 if arrow_output.num_rows() != input.cardinality() {
124 bail!(
125 "UDF returned {} rows, but expected {}",
126 arrow_output.num_rows(),
127 input.cardinality(),
128 );
129 }
130
131 let output = self.arrow_convert.from_record_batch(&arrow_output)?;
132 let output = output.expand_vis(input.visibility().clone());
133
134 let Some(array) = output.columns().first() else {
135 bail!("UDF returned no columns");
136 };
137 if !array.data_type().equals_datatype(&self.return_type) {
138 bail!(
139 "UDF returned {:?}, but expected {:?}",
140 array.data_type(),
141 self.return_type,
142 );
143 }
144
145 if let Some(errors) = output.columns().get(1) {
147 if errors.data_type() != DataType::Varchar {
148 bail!(
149 "UDF returned errors column with invalid type: {:?}",
150 errors.data_type()
151 );
152 }
153 let errors = errors
154 .as_utf8()
155 .iter()
156 .filter_map(|msg| msg.map(|s| ExprError::Custom(s.into())))
157 .collect();
158 return Err(crate::ExprError::Multiple(array.clone(), errors));
159 }
160
161 Ok(array.clone())
162 }
163}
164
165impl UserDefinedFunction {
166 fn build_inner(
167 prost: &ExprNode,
168 build_child: impl Fn(&ExprNode) -> Result<BoxedExpression>,
169 ) -> Result<Self> {
170 let return_type = DataType::from(prost.get_return_type().unwrap());
171 let udf = prost.get_rex_node().unwrap().as_udf().unwrap();
172 let name = udf.get_name();
173 let arg_types = udf.arg_types.iter().map(|t| t.into()).collect::<Vec<_>>();
174
175 let language = udf.language.as_str();
176 let runtime = udf.runtime.as_deref();
177 let link = udf.link.as_deref();
178
179 let name_in_runtime = udf
180 .name_in_runtime()
181 .expect("SQL UDF won't get here, other UDFs must have `name_in_runtime`");
182
183 let build_fn = crate::sig::find_udf_impl(language, runtime, link)?.build_fn;
185 let runtime = build_fn(BuildOptions {
186 kind: UdfKind::Scalar,
187 body: udf.body.as_deref(),
188 compressed_binary: udf.compressed_binary.as_deref(),
189 link: udf.link.as_deref(),
190 name_in_runtime,
191 arg_names: &udf.arg_names,
192 arg_types: &arg_types,
193 return_type: &return_type,
194 always_retry_on_network_error: udf.always_retry_on_network_error,
195 language,
196 is_async: udf.is_async,
197 is_batched: udf.is_batched,
198 })
199 .context("failed to build UDF runtime")?;
200
201 let arrow_convert = UdfArrowConvert {
202 legacy: runtime.is_legacy(),
203 };
204
205 let arg_schema = Arc::new(Schema::new(
206 udf.arg_types
207 .iter()
208 .map(|t| arrow_convert.to_arrow_field("", &DataType::from(t)))
209 .try_collect::<Fields>()?,
210 ));
211
212 let metrics = GLOBAL_METRICS.with_label_values(
213 link.unwrap_or(""),
214 language,
215 name,
216 &FRAGMENT_ID::try_with(ToOwned::to_owned)
218 .unwrap_or(0.into())
219 .to_string(),
220 );
221
222 let children: Vec<BoxedExpression> = udf.children.iter().map(build_child).try_collect()?;
223
224 Ok(Self {
225 children,
226 arg_types,
227 return_type,
228 arg_schema,
229 runtime,
230 arrow_convert,
231 span: await_tree::span!("udf_call({})", name),
232 metrics,
233 })
234 }
235}
236
237impl BuildBoxed for UserDefinedFunction {
238 fn build_boxed(
239 prost: &ExprNode,
240 build_child: impl Fn(&ExprNode) -> Result<BoxedExpression>,
241 ) -> Result<BoxedExpression> {
242 Self::build_inner(prost, build_child).map(AsyncExpressionBoxExt::boxed)
243 }
244}
245
246#[derive(Debug, Clone)]
248struct MetricsVec {
249 success_count: LabelGuardedIntCounterVec,
251 failure_count: LabelGuardedIntCounterVec,
253 retry_count: LabelGuardedIntCounterVec,
255 input_chunk_rows: LabelGuardedHistogramVec,
257 latency: LabelGuardedHistogramVec,
259 input_rows: LabelGuardedIntCounterVec,
261 input_bytes: LabelGuardedIntCounterVec,
263 memory_usage_bytes: LabelGuardedIntGaugeVec,
265}
266
267#[derive(Debug, Clone)]
269struct Metrics {
270 success_count: LabelGuardedIntCounter,
272 failure_count: LabelGuardedIntCounter,
274 #[expect(dead_code)]
276 retry_count: LabelGuardedIntCounter,
277 input_chunk_rows: LabelGuardedHistogram,
279 latency: LabelGuardedHistogram,
281 input_rows: LabelGuardedIntCounter,
283 input_bytes: LabelGuardedIntCounter,
285 memory_usage_bytes: LabelGuardedIntGauge,
287}
288
289static GLOBAL_METRICS: LazyLock<MetricsVec> =
291 LazyLock::new(|| MetricsVec::new(&GLOBAL_METRICS_REGISTRY));
292
293impl MetricsVec {
294 fn new(registry: &Registry) -> Self {
295 let labels = &["link", "language", "name", "fragment_id"];
296 let labels5 = &["link", "language", "name", "fragment_id", "instance_id"];
297 let success_count = register_guarded_int_counter_vec_with_registry!(
298 "udf_success_count",
299 "Total number of successful UDF calls",
300 labels,
301 registry
302 )
303 .unwrap();
304 let failure_count = register_guarded_int_counter_vec_with_registry!(
305 "udf_failure_count",
306 "Total number of failed UDF calls",
307 labels,
308 registry
309 )
310 .unwrap();
311 let retry_count = register_guarded_int_counter_vec_with_registry!(
312 "udf_retry_count",
313 "Total number of retried UDF calls",
314 labels,
315 registry
316 )
317 .unwrap();
318 let input_chunk_rows = register_guarded_histogram_vec_with_registry!(
319 "udf_input_chunk_rows",
320 "Input chunk rows of UDF calls",
321 labels,
322 exponential_buckets(1.0, 2.0, 10).unwrap(), registry
324 )
325 .unwrap();
326 let latency = register_guarded_histogram_vec_with_registry!(
327 "udf_latency",
328 "The latency(s) of UDF calls",
329 labels,
330 exponential_buckets(0.000001, 2.0, 30).unwrap(), registry
332 )
333 .unwrap();
334 let input_rows = register_guarded_int_counter_vec_with_registry!(
335 "udf_input_rows",
336 "Total number of input rows of UDF calls",
337 labels,
338 registry
339 )
340 .unwrap();
341 let input_bytes = register_guarded_int_counter_vec_with_registry!(
342 "udf_input_bytes",
343 "Total number of input bytes of UDF calls",
344 labels,
345 registry
346 )
347 .unwrap();
348 let memory_usage_bytes = register_guarded_int_gauge_vec_with_registry!(
349 "udf_memory_usage",
350 "Total memory usage of UDF runtime in bytes",
351 labels5,
352 registry
353 )
354 .unwrap();
355
356 MetricsVec {
357 success_count,
358 failure_count,
359 retry_count,
360 input_chunk_rows,
361 latency,
362 input_rows,
363 input_bytes,
364 memory_usage_bytes,
365 }
366 }
367
368 fn with_label_values(
369 &self,
370 link: &str,
371 language: &str,
372 name: &str,
373 fragment_id: &str,
374 ) -> Metrics {
375 static NEXT_INSTANCE_ID: AtomicU64 = AtomicU64::new(0);
377 let instance_id = NEXT_INSTANCE_ID.fetch_add(1, Ordering::Relaxed).to_string();
378
379 let labels = &[link, language, name, fragment_id];
380 let labels5 = &[link, language, name, fragment_id, &instance_id];
381
382 Metrics {
383 success_count: self.success_count.with_guarded_label_values(labels),
384 failure_count: self.failure_count.with_guarded_label_values(labels),
385 retry_count: self.retry_count.with_guarded_label_values(labels),
386 input_chunk_rows: self.input_chunk_rows.with_guarded_label_values(labels),
387 latency: self.latency.with_guarded_label_values(labels),
388 input_rows: self.input_rows.with_guarded_label_values(labels),
389 input_bytes: self.input_bytes.with_guarded_label_values(labels),
390 memory_usage_bytes: self.memory_usage_bytes.with_guarded_label_values(labels5),
391 }
392 }
393}