Is your feature request related to a problem or challenge?
I'd like to build a custom DataSource that reuses an existing FileSource (e.g. ParquetSource) instead of reimplementing all its internals. Today this is not possible without dragging along a FileScanConfig that my custom DataSource doesn't otherwise need.
The architecture diagram in datafusion/datasource/src/source.rs shows a clean top-down layering:
DataSourceExec ──► DataSource (trait) ──► FileScanConfig ──► FileSource (trait) ──► ParquetSource / ...
Roughly speaking, theDataSource handles execution and partitioning and the FileSource handles file-type specifics. But FileSource is currently coupled to FileScanConfig (the concrete DataSource). Its open-time methods take a &FileScanConfig and use it as a "bag of args" (e.g. ParquetSource pulls expr_adapter_factory, limit, preserve_order, ...).
fn create_file_opener(&self, object_store: Arc<dyn ObjectStore>, base_config: &FileScanConfig, partition: usize) -> ...;
fn create_morselizer(&self, object_store: Arc<dyn ObjectStore>, base_config: &FileScanConfig, partition: usize) -> ...;
Decoupling the two would let a custom DataSource reuse any FileSource directly.
Describe the solution you'd like
Pass those inputs explicitly via a small, freely-constructible args struct, replacing retrieval direct from the &FileScanConfig:
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct FileSourceArgs {
pub object_store: Arc<dyn ObjectStore>,
pub batch_size: usize,
pub limit: Option<usize>,
pub preserve_order: bool,
pub file_compression_type: FileCompressionType,
pub expr_adapter_factory: Option<Arc<dyn PhysicalExprAdapterFactory>>,
}
fn create_file_opener(&self, args: &FileSourceArgs, partition: usize) -> ...;
fn create_morselizer(&self, args: &FileSourceArgs, partition: usize) -> ...;
// with_batch_size removed — batch size now flows through FileSourceArgs
FileScanConfig builds FileSourceArgs inline (behavior unchanged). However, now a custom DataSource can build the same struct and reuse any FileSource too.
This is a breaking change to the public FileSource trait, but the migration path is simple.
I have a draft PR ready for this issue.
Describe alternatives you've considered
- Do nothing. I can construct a dummy
FileScanConfig within my custom DataSource - simply to satisfy the interface - but the construction is quite messy.
- Extend the
DataSource trait with all getter fns that a FileSource may - might(?) avoid a breaking change, but adds coupling that may not make sense for other custom DataSources.
- Fork ParquetSource, add a custom
fn create_morselizer() that doesn't depend on FileSourceArgs - I'd prefer not to diverge from upstream
Additional context
The goal of my custom DataSource is to increase I/O parallelism on an I/O bound workload with plenty of CPU to spare. To achieve this, I am diverging from the FileStream currently tightly coupled in a FileScanConfig.
P.S. this is my first datafusion issue. Thanks!
Is your feature request related to a problem or challenge?
I'd like to build a custom
DataSourcethat reuses an existingFileSource(e.g.ParquetSource) instead of reimplementing all its internals. Today this is not possible without dragging along aFileScanConfigthat my customDataSourcedoesn't otherwise need.The architecture diagram in
datafusion/datasource/src/source.rsshows a clean top-down layering:Roughly speaking, the
DataSourcehandles execution and partitioning and theFileSourcehandles file-type specifics. ButFileSourceis currently coupled toFileScanConfig(the concreteDataSource). Its open-time methods take a&FileScanConfigand use it as a "bag of args" (e.g.ParquetSourcepullsexpr_adapter_factory,limit,preserve_order, ...).Decoupling the two would let a custom
DataSourcereuse anyFileSourcedirectly.Describe the solution you'd like
Pass those inputs explicitly via a small, freely-constructible args struct, replacing retrieval direct from the
&FileScanConfig:FileScanConfigbuildsFileSourceArgsinline (behavior unchanged). However, now a customDataSourcecan build the same struct and reuse anyFileSourcetoo.This is a breaking change to the public
FileSourcetrait, but the migration path is simple.I have a draft PR ready for this issue.
Describe alternatives you've considered
FileScanConfigwithin my customDataSource- simply to satisfy the interface - but the construction is quite messy.DataSourcetrait with all getter fns that aFileSourcemay - might(?) avoid a breaking change, but adds coupling that may not make sense for other custom DataSources.fn create_morselizer()that doesn't depend onFileSourceArgs- I'd prefer not to diverge from upstreamAdditional context
The goal of my custom DataSource is to increase I/O parallelism on an I/O bound workload with plenty of CPU to spare. To achieve this, I am diverging from the
FileStreamcurrently tightly coupled in aFileScanConfig.P.S. this is my first datafusion issue. Thanks!