pub trait View:
Debug
+ Send
+ Sync {
Show 14 methods
// Required methods
fn get_view_set_name(&self) -> Arc<String> ⓘ;
fn get_view_instance_id(&self) -> Arc<String> ⓘ;
fn make_batch_partition_spec<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
existing_partitions: Arc<PartitionCache>,
insert_range: TimeRange,
) -> Pin<Box<dyn Future<Output = Result<Arc<dyn PartitionSpec>, Error>> + Send + 'async_trait>>
where 'life0: 'async_trait,
Self: 'async_trait;
fn get_file_schema_hash(&self) -> Vec<u8> ⓘ;
fn get_file_schema(&self) -> Arc<Schema> ⓘ;
fn jit_update<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
query_range: Option<TimeRange>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'async_trait>>
where 'life0: 'async_trait,
Self: 'async_trait;
fn make_time_filter(
&self,
_begin: DateTime<Utc>,
_end: DateTime<Utc>,
) -> Result<Vec<Expr>, Error>;
fn get_time_bounds(&self) -> Arc<dyn DataFrameTimeBounds> ⓘ;
fn get_update_group(&self) -> Option<i32>;
// Provided methods
fn register_table<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SessionContext,
table: MaterializedView,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait { ... }
fn merge_partitions<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
partitions_to_merge: Arc<Vec<Partition>>,
partitions_all_views: Arc<PartitionCache>,
insert_range: TimeRange,
) -> Pin<Box<dyn Future<Output = Result<MergeQueryResult, Error>> + Send + 'async_trait>>
where 'life0: 'async_trait,
Self: 'async_trait { ... }
fn get_merged_partition_sort_order(
&self,
_partitions_to_merge: &[Partition],
) -> Option<Vec<String>> { ... }
fn get_max_partition_time_delta(
&self,
_strategy: &PartitionCreationStrategy,
) -> TimeDelta { ... }
fn get_scan_output_ordering(&self) -> ScanOrdering { ... }
}Expand description
A trait for defining a view.
Required Methods§
Sourcefn get_view_set_name(&self) -> Arc<String> ⓘ
fn get_view_set_name(&self) -> Arc<String> ⓘ
name of the table from the user’s perspective
Sourcefn get_view_instance_id(&self) -> Arc<String> ⓘ
fn get_view_instance_id(&self) -> Arc<String> ⓘ
get_view_instance_id can be a process_id, a stream_id or ‘global’.
Sourcefn make_batch_partition_spec<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
existing_partitions: Arc<PartitionCache>,
insert_range: TimeRange,
) -> Pin<Box<dyn Future<Output = Result<Arc<dyn PartitionSpec>, Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
fn make_batch_partition_spec<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
existing_partitions: Arc<PartitionCache>,
insert_range: TimeRange,
) -> Pin<Box<dyn Future<Output = Result<Arc<dyn PartitionSpec>, Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
make_batch_partition_spec determines what should be found in an up to date partition. The resulting PartitionSpec can be used to validate existing partitions are create a new one.
Sourcefn get_file_schema_hash(&self) -> Vec<u8> ⓘ
fn get_file_schema_hash(&self) -> Vec<u8> ⓘ
get_file_schema_hash returns a hash (can be a version number, version string, etc.) that allows to identify out of date partitions.
Sourcefn get_file_schema(&self) -> Arc<Schema> ⓘ
fn get_file_schema(&self) -> Arc<Schema> ⓘ
get_file_schema returns the schema of the partition file in object storage
Sourcefn jit_update<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
query_range: Option<TimeRange>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
fn jit_update<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
query_range: Option<TimeRange>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
jit_update creates or updates process-specific partitions before a query
Sourcefn make_time_filter(
&self,
_begin: DateTime<Utc>,
_end: DateTime<Utc>,
) -> Result<Vec<Expr>, Error>
fn make_time_filter( &self, _begin: DateTime<Utc>, _end: DateTime<Utc>, ) -> Result<Vec<Expr>, Error>
make_time_filter returns a set of expressions that will filter out the rows of the partition outside the time range requested.
fn get_time_bounds(&self) -> Arc<dyn DataFrameTimeBounds> ⓘ
Sourcefn get_update_group(&self) -> Option<i32>
fn get_update_group(&self) -> Option<i32>
tells the daemon which view should be materialized and in what order
Provided Methods§
Sourcefn register_table<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SessionContext,
table: MaterializedView,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn register_table<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 SessionContext,
table: MaterializedView,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
register the table in the SessionContext
fn merge_partitions<'life0, 'async_trait>(
&'life0 self,
lakehouse: Arc<LakehouseContext>,
partitions_to_merge: Arc<Vec<Partition>>,
partitions_all_views: Arc<PartitionCache>,
insert_range: TimeRange,
) -> Pin<Box<dyn Future<Output = Result<MergeQueryResult, Error>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
Sourcefn get_merged_partition_sort_order(
&self,
_partitions_to_merge: &[Partition],
) -> Option<Vec<String>>
fn get_merged_partition_sort_order( &self, _partitions_to_merge: &[Partition], ) -> Option<Vec<String>>
Returns the sort guarantee a merge of partitions_to_merge will actually produce, to be
recorded as the resulting partition’s Partition::sort_order (see
merge::create_merged_partition).
This is a distinct concept from get_scan_output_ordering(): that one is a trusted
scan-ordering declaration consumed during physical planning, while this one is a record of
what a specific merge actually produced, computed purely from the input partitions (before
merge_partitions runs) and independent of whether DataFusion’s elision optimization
happened to succeed for this particular run.
Default: None – no guarantee recorded, ignoring the argument.
Sourcefn get_max_partition_time_delta(
&self,
_strategy: &PartitionCreationStrategy,
) -> TimeDelta
fn get_max_partition_time_delta( &self, _strategy: &PartitionCreationStrategy, ) -> TimeDelta
allow the view to subdivide the requested partition
Sourcefn get_scan_output_ordering(&self) -> ScanOrdering
fn get_scan_output_ordering(&self) -> ScanOrdering
Declares an ordering the view’s partition scan already emits, letting DataFusion
elide redundant Sort nodes for queries that ORDER BY these columns.
A large or memory-heavy merge is not, on its own, a reason to declare an ordering here.
Every QueryMerger merge scans its source partitions with a single sequential reader
regardless of what this method returns (make_merge_session_context,
tasks/1491_merge_scan_memory_plan.md), so a view with no natural sort contract gets a
bounded-memory merge for free, with no ordering guarantee to maintain. Reach for a declared
ordering only when a query-side need justifies it – eliding a Sort for queries that
already ORDER BY these columns, or (via PerFile) a streaming k-way merge that must
preserve per-file order across an aggregation – not to bound a merge’s memory.
Returning a non-Unordered value is a correctness contract the view must guarantee, and
its shape depends on which variant is returned:
-
Concatenated { columns, .. }: rows within each partition file are already sorted bycolumns, the leading column is the view’s min-event-time column, and partition event-time ranges are non-overlapping (so files concatenate in globally-sorted order). ForThreadSpansView, the rows-sorted-within-a-file half is obtained from JIT partitions being grouped underjit_partitions::BlockOrder::EventTime:group_blocks_into_partitionssorts a segment’s blocks by event time and only cuts at insert-safe points (see its docs), so within a segment blocks land in event order rather than relying on an unenforced assumption about registration order, andthread_spans_view::ensure_begin_non_decreasingchecks the resulting batch at write time.The partition-ranges-don’t-overlap half is not established by that grouping alone – a partition’s declared event-time bounds come from its blocks’
begin_ticks/end_ticks, not from the rows – but by design both producers’ consecutive blocks now touch exactly:- Both
micromegas_tracing::dispatch’s four flush paths and the Unreal producer (MicromegasTracing/Private/Dispatch.cpp) take a single shared timestamp per flush and use it for both the outgoing block’s close and the replacement block’sbegin, soblock[k].end_ticks == block[k+1].begin_ticksby construction. This is the design intent the shared boundary stamp exists for: it lets call trees merge seamlessly across the cut (group_contiguous_block_chainstreats touching blocks as chain-connected). - Data from
micromegas_tracingversions predating that fix (or any other producer that stamps two separate timestamps) can still strictly overlap at block boundaries – this is a legacy producer bug, not a supported producer shape, but the server tolerates it: the view recordsmax_sort_key_time(the true max leading-sort-column value, exact per partition), andpartitioned_execution_plan::partition_boundsreads that in preference tomax_event_timefor the non-overlap check, which the swap-window argument intasks/completed/thread_spans_segment_boundary_overlap_plan.mdshows can never trip for cuts at block boundaries of either producer. Partitions written before that column existed (or by a view that never populates it) fall back to the old, loosermax_event_timebound, which is self-healing rather than a permanent gap:ThreadSpansView::SCHEMA_VERSION’s bump makes every pre-existing partition stale by schema hash, so it rebuilds – carryingmax_sort_key_time– automatically on its next query, no adminretire_partitionscall required.
Two residual caveats therefore remain, both backstopped by
sort_and_check_non_overlapping(partitioned_execution_plan.rs) failing the query loudly rather than returning wrong rows: an insert-time inversion straddling a JIT segment boundary (segments are still grouped independently, seegenerate_stream_jit_partitions), which produces a genuine row-level overlap that the check correctly rejects; and TSC-frequency re-estimation drift across materialization epochs fortsc_frequency == 0processes, which can skew bounds written under different converters. - Both
-
PerFile { columns }: rows within each partition file are already sorted, ascending, bycolumns, but partitions may overlap each other arbitrarily on those columns. A false declaration here is not merely mis-ordered rows but, under order-aware aggregation, wrong aggregate results (groups closed early, duplicate group keys) – seeSqlBatchView::with_merge_sort_order. What makes declaring this safe is the recorded-sort_ordercertification gate insidemake_partitioned_execution_plan: every non-empty partition must certifycolumnsbefore the declaration reaches DataFusion at all.
Default: Unordered (no declared ordering — DataFusion sorts as usual).
Implementations§
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".