Beta — introduced in Geneva 0.11.0
Geneva’s standard UDFs operate row-at-a-time — one input row produces exactly one output value. Batch User-Defined Table Functions (UDTFs) lift this restriction, enabling N:M transformations where the output can have a completely different schema and row count than the input.
Defining a Batch UDTF
Use the @udtf decorator on a class or function. The UDTF receives a query builder over the source data and yields pa.RecordBatch objects with an arbitrary output schema.
Class-based
Function-based
Decorator Parameters
Creating and Refreshing a UDTF View
Batch UDTFs are always attached to a persistent view via create_udtf_view(). Call refresh() to populate or update the view.
UDTF views use the same cluster infrastructure as other Geneva jobs:
Version-aware refresh
On each refresh, Geneva checks the source table’s version against the version stored in the view metadata. If the source has not changed, the refresh is skipped entirely — an O(1) check.
Execution Modes
Single-worker (no partitioning)
When neither partition_by nor partition_by_indexed_column is set, the UDTF runs as a single Ray task with access to the entire source dataset. Use this for global operations that need cross-row visibility.
Partition-parallel (partition_by)
The framework groups source data by the partition column and dispatches each partition as an independent Ray task. Use this when the computation is naturally parallelizable by some grouping key.
Index-based partitioning (partition_by_indexed_column)
Instead of partitioning by a materialized column, the framework reads partition assignments directly from an existing IVF vector index (IVF_FLAT, IVF_PQ, IVF_HNSW_FLAT, IVF_HNSW_SQ, etc.). This avoids materializing a partition_id column and keeps partitions synchronized with the index.
partition_by and partition_by_indexed_column are mutually exclusive. Setting both raises ValueError.
Yielding Batches
The UDTF yields one or more pa.RecordBatch or pa.Table objects. Each batch must conform to output_schema. The framework validates each batch, writes it, and optionally checkpoints it.
Use the streaming pattern for memory-efficient processing. Use the bulk pattern when the computation inherently requires all data in memory (e.g., clustering, global deduplication).
Error Handling
Error handling operates at partition granularity — the unit of work is the entire __call__() execution for a partition (or the full table in single-worker mode).
Unlike standard UDF error handling, there is no row-level skip — UDTFs yield whole batches, so the smallest error unit is the partition.
Checkpointing
Each yielded batch is checkpointed before reporting completion. On resume after a failure, completed batches are skipped and entire partitions with a __done__ marker are skipped.
Checkpoint keys include the source table version, so stale checkpoints from a previous source version are automatically ignored when the source changes.
Checkpointed UDTFs must be deterministic — the same input must yield the same batch sequence for resume to work correctly.
Examples
K-Means Clustering
Aggregation
Reference:
create_udtf_view API
- UDTF — full
@udtf / @batch_udtf decorator reference including output_schema, partition_by, num_gpus, and on_error