Skip to content

parquet: Remove explicit object_store integration - #10354

Open
brancz wants to merge 3 commits into
apache:mainfrom
polarsignals:rm-obj-store-parquet
Open

parquet: Remove explicit object_store integration#10354
brancz wants to merge 3 commits into
apache:mainfrom
polarsignals:rm-obj-store-parquet

Conversation

@brancz

@brancz brancz commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Deprecate ParquetObjectReader/ParquetObjectWriter in favor of
implementing AsyncFileReader directly (with an example on the trait
docs) and passing an AsyncWrite such as object_store's BufWriter to
AsyncArrowWriter. To reduce the code needed by implementors I also added
SpawnedReader and ParquetMetaDataReader::with_arrow_reader_options. In
the future we will only need to keep object_store as a dev-dependency to
we can ensure compatibility and use it in benchmarking but remove it
from being a full runtime dependency all together.

Also added a full example in parquet/examples/object_store.rs.

Which issue does this PR close?

If we agree on the approach on this, I basically already have the same thing ready for avro, but wanted to keep the PR smaller so we can align on the direction before doing it all at once.

What changes are included in this PR?

Deprecate the object_store dependency and add some extra helpers to allow users to replace it with very few lines of code (see the example where it only takes ~60 lines to replace what would take hundreds without the helpers).

Are these changes tested?

Yes

Are there any user-facing changes?

Not yet, just deprecations.

Additional notes

Full disclaimer: Since we don't use parquet, I don't know this code super well, so I used Claude Code a bit to help me in creating this patch. That said, I've fully read the AI-generated parts and understand what they do, and they are how I would have written it, and how I interpreted the prior discussion in #10308.

@alamb

@github-actions github-actions Bot added the parquet Changes to the parquet crate label Jul 16, 2026
@alamb alamb changed the title parquet: Remove object_store integration parquet: Remove explicit object_store integration Jul 20, 2026

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @brancz -- I think this is the right approach and I think it will make using the parquet reader with many downstream crates much easier

My only concern with this PR is now how to minimize the downstream impact / make upgrading as simple for people as possible. To that end, I think we need:

  1. A step-by-step upgrade guide (that can be mechanically followed)
  2. A way to get the old behavior (including with_runtime, etc)

What I was thinking is that we can move (literally move) the code from the existing reader/writer into a file in the examples directory, and then update the comments explaining the rationale / how to upgrade.

I have some ideas on how to do this. I'll make a PR shortly for your consideration

.head(&Path::from("uniform_encryption.parquet.encrypted"))
.await
.unwrap();
fn to_parquet_err(e: object_store::Error) -> ParquetError {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty compelling short example showing the integration doesn't take much code (150 lines or so)

@alamb

alamb commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Is the reason this doesn't close #10308 is that it doesn't address the avro reader?

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I thought about upgrade, the more I think if we just remove the object_store stuff in 60 that will be a jarring upgrade

Instead, I think we should do this in two steps (PRS) to minimize the blast radius:

  1. Deprecate the ParquetObjectReader and ParquetObjectWriter and point them at a new example (parquet/examples/object_store.rs) that shows how to inline the structures in downstream crates
  2. Actually remove the ParquetObjectReader and ParquetObjectWriter

@alamb

alamb commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

@alamb

alamb commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Also, according to the https://github.com/apache/arrow-rs#deprecation-guidelines we should wait for 2 major versions before removing so I think we should begin deprecating first

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to be a little more gentle when removing APIs -- per the https://github.com/apache/arrow-rs#deprecation-guidelines we should wait for 2 major versions before removing so I think we should begin deprecating first

@brancz
brancz force-pushed the rm-obj-store-parquet branch 4 times, most recently from 89e20e6 to 424dae5 Compare July 21, 2026 09:17
@brancz

brancz commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Alright, changed it to just deprecate, but kept the added helpers so the replacement code can be only ~60 lines as opposed to ~400 without them (see the example).

If this is generally what we want the avro deprecations to look like as well, I can prepare those changes as well.

Deprecate ParquetObjectReader/ParquetObjectWriter in favor of
implementing AsyncFileReader directly (with an example on the trait
docs) and passing an AsyncWrite such as object_store's BufWriter to
AsyncArrowWriter. To reduce the code needed by implementors I also added
SpawnedReader and ParquetMetaDataReader::with_arrow_reader_options. In
the future we will only need to keep object_store as a dev-dependency to
we can ensure compatibility and use it in benchmarking but remove it
from being a full runtime dependency all together.

Also added a full example in `parquet/examples/object_store.rs`.

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @brancz -- I think this looks really good

I have been thinking about how to remove the dependency sooner than 2 major releases (6 months)

One idea I had was that as a follow on PR, we can introduce a new feature flag (that was not enabled by default) like "no_object_store" that avoids the dependence

That way downstream systems that want to remove the dependence can do so (and lose the API) but would have to "opt in" for a few releases

Comment thread parquet/examples/object_store.rs Outdated
/// 1. Writes a Parquet file to an [`ObjectStore`] by passing an
/// [`object_store::buffered::BufWriter`] directly to [`AsyncArrowWriter`],
/// via the blanket [`AsyncFileWriter`] implementation for types
/// implementing [`AsyncWrite`] (replaces `ParquetObjectWriter`).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not: maybe we could say "equivalent of ParquetObjectWriter" rather than "replaces"

Comment thread parquet/examples/object_store.rs Outdated
/// implementing [`AsyncWrite`] (replaces `ParquetObjectWriter`).
///
/// 2. Reads it back with [`ObjectStoreReader`], a minimal [`AsyncFileReader`]
/// implementation on top of an [`ObjectStore`] (replaces

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly here "equivalent of " ...

Comment thread parquet/examples/object_store.rs Outdated
///
/// 3. Reads it again with the reader wrapped in a [`SpawnedReader`], which
/// performs all I/O on a separate tokio runtime so that the runtime
/// decoding Parquet is not also driving the I/O (replaces

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

/// Note: this method does not make any effort to combine consecutive ranges, nor coalesce
/// ranges that are close together. This is instead delegated to the IO subsystem to optimise,
/// e.g. [`ObjectStore::get_ranges`](object_store::ObjectStore::get_ranges)
/// e.g. `ObjectStore::get_ranges` in the `object_store` crate

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps we can link to https://crates.io/crates/object_store

/// 2. Implementations for remote storage, such as the `object_store` crate,
/// can implement this interface directly, typically by pairing a store
/// handle with an object path and delegating [`Self::get_bytes`] and
/// [`Self::get_byte_ranges`] to ranged reads. [`SpawnedReader`] can wrap

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is great

/// [`AsyncArrowWriter`]: crate::arrow::async_writer::AsyncArrowWriter
#[deprecated(
since = "59.2.0",
note = "Pass an `object_store::buffered::BufWriter` to `AsyncArrowWriter` directly instead; see `parquet/examples/object_store.rs`"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double checked that this is the right suggestion

https://docs.rs/object_store/latest/object_store/buffered/struct.BufWriter.html

@alamb

alamb commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

FYI @brancz and @tustvold

@brancz

brancz commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed the comments and merged main into this (let me know if you prefer a rebase).

Should be ready to go.

@alamb

alamb commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Pushing new commits is preferred (everything is squashed merged to main). I'll plan to merge it tomorrow. I won't cut the release without it

@Xuanwo

Xuanwo commented Jul 28, 2026

Copy link
Copy Markdown
Member

Happy to see this change (wearing an opendal hat 🤩)

I would happy to submit an opendal example as a follow up.

@tustvold

Copy link
Copy Markdown
Contributor

FWIW an alternative way to resolve the actual issue, which appears to be wanting newer versions of object_store in downstreams faster, is to have versioned feature flags, i.e. object_store_0_14, etc... As the API this depends is unlikely to change I don't expect this to be a major maintenance burden.

AFAICT the proposed change here would really just move this upgrade hazard into the next layer in the chain, i.e. DataFusion

@tustvold

Copy link
Copy Markdown
Contributor

FWIW I had Claude vomit out #10454 as a POC

@alamb

alamb commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

AFAICT the proposed change here would really just move this upgrade hazard into the next layer in the chain, i.e. DataFusion

Yes I believe this is an accurate assesment.

@alamb

alamb commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

In my opinion, it is hard to justify why the parquet crate has a built in integration for one I/O system but not others (OpenDAL being an obvious example, but there are others). The argument that earlier on we needed an integration to help inform API design is reasonable, but I think that time has past now

I personally think separating out the I/O concerns from the lower level decoder logic will make the parquet reader easier to use and customize downstream systems (e.g. to do prefetching of I/O for example).

There are also projects that don't use object_store as their I/O layer so cutting the dependency I think is the right long term architectural direction.

@alamb

alamb commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

FWIW I had Claude vomit out #10454 as a POC

This is an interesting approach, and I am not opposed to it, but I still think moving the object store integration out of the parquet crate is the right technical approach

If we are concerned with the effect on downstream migrations, we could potentially moving the integration to a new crate (e.g. parquet-object-store) and move the integration there.

However, given the amount of code it would have (10s of lines of integration), a new crate seemed unecessary. However, if others feel strongly I would support that. We would still hvae to deprecate the export in this crate before removing them, so I think this PR would still be necessary

@tustvold

Copy link
Copy Markdown
Contributor

I personally don't think it is all that hard to justify that we provide a batteries included option using the IO abstraction maintained as part of the arrow project, and have APIs to allow plugging in other IO abstractions should users wish to use them. That being said I was mainly responding to the linked ticket, as if the issue is upgrade hazards, I fail to see that this PR actually is an improvement in that regard - if anything it will make it worse as each downstream project is forced to adopt their own variant of it.

That being said I don't feel overly strongly in this regard, and don't know what other plans are in the works, was just responding as I was tagged 😅

@alamb

alamb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

I personally don't think it is all that hard to justify that we provide a batteries included option using the IO abstraction maintained as part of the arrow project, and have APIs to allow plugging in other IO abstractions should users wish to use them. That being said I was mainly responding to the linked ticket, as if the issue is upgrade hazards, I fail to see that this PR actually is an improvement in that regard - if anything it will make it worse as each downstream project is forced to adopt their own variant of it.

Given this feedback the more I am liking the approach of moving the object_store code to a new crate -- this would mean we still provide a first party integration but it would be an opt-in dependency 🤔

@brancz

brancz commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

I don't really understand what batteries-included really means for an in-memory format like arrow. I get batteries-included (but removable) as a philosophy for datafusion. I see all of this work in the realm of "but removable", not meaning worse for downstream users, but more flexibility for downstream users (eg. opendal for reads, vortex as file format), and given that datafusion has become somewhat of a framework to build query engines, I think that's beneficial to fuel this way the community has picket it up.

All that said, a first-party crate is totally reasonable, and with these changes, that crate would be pretty tiny and an easy follow-up. We should just be careful not to create the same coupling.

@alamb

alamb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

If we are going to go with a first party crate I think we should probably do that as part of deprecation(so we don't tell users we are goign to remove the feature, rather we tell them they need to add a new crate dependency)

I can make a PR to do that , but I am pretty busy the next day or two -- if someone else could do that (basically move the code to a new crate, and update the deprecation message to say "use the version in parquet-object-store" or whatever the crate is known

@alamb

alamb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

We could also publish the deprecation warning and see if any downstream crates complain 🤔 And if there is demand then we can make the crate

@tustvold

tustvold commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Would this crate be versioned in the same way as the arrow crates, or have it's own versioning, or do some feature flag based versioning?

I don't really understand what batteries-included really means for an in-memory format like arrow

I agree for an in-memory format it doesn't make much sense to bundle an IO abstraction, however, here we are talking about file formats - parquet / avro. Providing a way to read these formats from where they are most often stored, is pretty table stakes IMO.

@tustvold

Copy link
Copy Markdown
Contributor

We could also publish the deprecation warning and see if any downstream crates complain 🤔 And if there is demand then we can make the crate

TBC I don't see any issues with this this approach, my point is more that it doesn't really resolve the linked issue, rather just punts it downstream 😅

Maybe downstreams make more frequent breaking releases and therefore this is less painful there, idk, I'm just struggling to understand the vision here

@brancz

brancz commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

I don't think punting it to downstream is a negative thing, nor does it not solve the issue. It means downstream can be flexible about it. That's not to say that downstream is as flexible yet as I think it should be, but that'll be follow-up work I'm intending to work on anyway. Not having the hard dependency allows downstream users to control their destiny, including, but not limited to, the version bump in question.

I think something like a parquet-object-store crate should be separately versioned from arrow, just like object_store, otherwise, we're not solving anything.

@alamb

alamb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

I think something like a parquet-object-store crate should be separately versioned from arrow, just like object_store, otherwise, we're not solving anything.

This is fair -- what a new crate might be doing is easing the migration for people who were happy with the current dependency chain.

@alamb

alamb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

I plan to merge this tomorrow

@Xuanwo

Xuanwo commented Jul 30, 2026

Copy link
Copy Markdown
Member

This is fair -- what a new crate might be doing is easing the migration for people who were happy with the current dependency chain.

For reference, the OpenDAL community maintains a crate called parquet-opendal: https://crates.io/crates/parquet-opendal. It manages its own versions separately from both OpenDAL and Parquet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parquet Changes to the parquet crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extract object store related code

4 participants