Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions iceberg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ typetag = "0.2"
datafusion-distributed = { path = "..", features = ["sql"] }
delegate = "0.13"
prost = "0.14.1"
rmp-serde = "1"
tokio = { version = "1.48", features = ["full"] }
tokio-stream = "0.1"

Expand Down
41 changes: 0 additions & 41 deletions iceberg/src/codec.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use crate::work_unit_feed::FileScanTaskMessage;
use bytes::{Buf, BufMut};
use datafusion::common::Result;
use datafusion::execution::TaskContext;
use datafusion::physical_plan::ExecutionPlan;
use datafusion_proto::physical_plan::PhysicalExtensionCodec;
use prost::encoding::{DecodeContext, WireType};
use prost::{DecodeError, Message};
use std::sync::Arc;

#[derive(Debug)]
Expand All @@ -26,40 +22,3 @@ impl PhysicalExtensionCodec for IcebergCodec {
unimplemented!()
}
}

// TODO: Implement serde for FileScanTaskMessage
// This message is an individual WorkUnit, but it cannot be serialized yet. During distributed
// execution, this will be streamed over the wire from coordinator to workers, but for that to
// happen, it will need to be represented as a prost::Message.
//
// WARNING: for the ones who end up implementing this. I have no idea if implementing Message here
// is really the best option for serialization, it might not be.
impl Message for FileScanTaskMessage {
fn encode_raw(&self, _buf: &mut impl BufMut)
where
Self: Sized,
{
unimplemented!()
}

fn merge_field(
&mut self,
_tag: u32,
_wire_type: WireType,
_buf: &mut impl Buf,
_ctx: DecodeContext,
) -> std::result::Result<(), DecodeError>
where
Self: Sized,
{
unimplemented!()
}

fn encoded_len(&self) -> usize {
unimplemented!()
}

fn clear(&mut self) {
unimplemented!()
}
}
11 changes: 5 additions & 6 deletions iceberg/src/data_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use datafusion::arrow::datatypes::SchemaRef;
use datafusion::common::stats::Precision;
use datafusion::common::{ColumnStatistics, Statistics, exec_datafusion_err};
use datafusion::common::{ColumnStatistics, Statistics};
use datafusion::config::ConfigOptions;
use datafusion::datasource::source::DataSource;
use datafusion::error::Result;
Expand All @@ -22,6 +22,7 @@ use iceberg::arrow::ArrowReaderBuilder;
use iceberg::spec::SnapshotRef;

use crate::common::{convert_filters_to_predicate, df_err, iceberg_err};
use crate::work_unit_wire::FileScanTaskDecoder;
use crate::{IcebergConfig, IcebergWorkUnitFeed};

/// Snapshot summary keys defined by the Iceberg table spec:
Expand Down Expand Up @@ -209,14 +210,12 @@ impl DataSource for IcebergDataSource {
.with_row_selection_enabled(config.row_selection_enabled)
.build();

let mut decoder = FileScanTaskDecoder::default();
let feed = self
.feed
.feed(partition, context)?
.map(|msg_or_err| match msg_or_err {
Ok(msg) => match msg.inner {
Some(msg) => Ok(msg),
None => Err(iceberg_err(exec_datafusion_err!("Missing inner"))),
},
.map(move |msg_or_err| match msg_or_err {
Ok(msg) => decoder.decode(msg).map_err(iceberg_err),
Err(err) => Err(iceberg_err(err)),
})
.boxed();
Expand Down
1 change: 1 addition & 0 deletions iceberg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod distributed_desired_task_count_handler;
mod iceberg_ext;
mod table_provider;
mod work_unit_feed;
mod work_unit_wire;

mod codec;
#[doc(hidden)]
Expand Down
5 changes: 5 additions & 0 deletions iceberg/src/test_utils/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ impl IcebergTestHarness {
pub async fn physical_plan(&self, sql: &str) -> Result<Arc<dyn ExecutionPlan>> {
self.ctx.sql(sql).await?.create_physical_plan().await
}

#[cfg(test)]
pub(crate) fn task_context(&self) -> Arc<datafusion::execution::TaskContext> {
self.ctx.task_ctx()
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
26 changes: 11 additions & 15 deletions iceberg/src/work_unit_feed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use datafusion::error::DataFusionError;
use datafusion::execution::TaskContext;
use datafusion::physical_expr::Partitioning;
use datafusion_distributed::{DistributedWorkUnitFeedContext, WorkUnitFeedProvider};
use futures::StreamExt;
use futures::stream::BoxStream;
use futures::{StreamExt, TryStreamExt};
use iceberg::expr::Predicate;
use iceberg::scan::FileScanTask;
use tokio::sync::mpsc::UnboundedReceiver;
use tokio_stream::wrappers::UnboundedReceiverStream;

use crate::common::df_err;
use crate::work_unit_wire::{FileScanTaskEncoder, FileScanTaskMessage};

/// Work unit feed implementation that yields [FileScanTask] messages at execution time.
///
Expand Down Expand Up @@ -116,17 +116,6 @@ pub(crate) struct SyncManager {
feeds: TakeableVec<UnboundedReceiver<Result<FileScanTaskMessage>>>,
}

#[derive(Debug, Clone, Default)]
pub struct FileScanTaskMessage {
pub(crate) inner: Option<FileScanTask>,
}

impl FileScanTaskMessage {
fn new(inner: FileScanTask) -> Self {
Self { inner: Some(inner) }
}
}

impl WorkUnitFeedProvider for IcebergWorkUnitFeed {
type WorkUnit = FileScanTaskMessage;

Expand Down Expand Up @@ -173,7 +162,7 @@ impl WorkUnitFeedProvider for IcebergWorkUnitFeed {
// the return streams of the `feed()` method.
let task = SpawnedTask::spawn(async move {
let mut stream = match table_scan.plan_files().await {
Ok(stream) => stream.map_ok(FileScanTaskMessage::new),
Ok(stream) => stream,
Err(err) => {
let _ = txs[0].send(Err(df_err(err)));
return;
Expand All @@ -183,9 +172,16 @@ impl WorkUnitFeedProvider for IcebergWorkUnitFeed {
// Round robing across output partitions.
// TODO: this is fine for Partitioning::UnknownPartitioning, but any other
// partitioning will require smarter routing across output channels.
let mut encoders = (0..txs.len())
.map(|_| FileScanTaskEncoder::default())
.collect::<Vec<_>>();
let mut i = 0;
while let Some(scan_task_or_err) = stream.next().await {
let _ = txs[i % txs.len()].send(scan_task_or_err.map_err(df_err));
let partition = i % txs.len();
let message = scan_task_or_err
.map_err(df_err)
.and_then(|task| encoders[partition].encode(task));
let _ = txs[partition].send(message);
i += 1;
}
});
Expand Down
Loading
Loading