feat(datafusion) Add snapshot_id parameter to pin table reads#2862
feat(datafusion) Add snapshot_id parameter to pin table reads#2862NoahKusaba wants to merge 17 commits into
Conversation
|
@CTTY do you have time to look at this? |
There was a problem hiding this comment.
Hey, thanks for the PR, just wondering what the benefit of this is vs using the StaticTableProvider?
If we decide this is the right path forward, I wonder if we can make it generic such that this works for incremental append scans etc also? Maybe with a ScanRange enum or something? Happy to hear thoughts here!
My idea is to register tables to datafusion using IcebergTableProvider, so that the same registration also allows for inserts/deletes/merges, etc. Whereas StaticTableProvider is strictly scan only. There's also a distributed angle (my motivating use case is a Ballista integration): the flow is Client -> LogicalCodec serializes the provider's recipe -> Scheduler rebuilds the provider and does physical planning -> PhysicalCodec -> Executors. The provider needs to hold everything required to recreate the table connection on remote executors: snapshot_id here, and catalog config (catalog_type, name, storage_properties) in a follow-up PR. I think the ScanRange suggestion is great, and since the current snapshot_id field is private we can replace it with ScanRange in the future without breaking anything. This would also require some plumbing to IcebergTableScan to work, which I think should live in a separate PR to keep this minimal. |
|
Something seems a little off here to me. Read/write skew seems like a footgun. A single registered table that reads snapshot A but writes to HEAD is a confusing object. I also think schema isn't being handled here correctly for pinned snapshots, we should resolve against the snapshot's schema rather than current, the static table provider handles this correctly. |
insert-read problem: The way I have it wired in my Ballista integration is that snapshot_id is resolved by the scheduler when it encodes the physical plan to executors, so a read -> insert -> read would properly show the new data as the snapshot_id would be reset to head by the scheduler as queries are sequentially made. I think it makes sense to add a write guard, if the snapshot_id is pinned (not None), to constrain confusing behavior? I added it in a new commit, let me know how you think this should be handled. |
|
Thanks for iterating with me! What are we gaining here over using the StaticTableProvider now? |
Thanks for helping me with this too (still fixing the schema update with snapshot id problem), and sorry for responding late. To answer your question, anyone not using the iceberg-ballista integration likely won't benefit from these changes, and they should keep using IcebergStaticTableProvider for time-travel reads. After this PR the two resolve schemas through the same helper and both refuse writes when pinned, so read semantics are identical. Ballista requires you instantiate one TableProvider, through which queries are routed (explained above). This creates two requirements:
|
|
Schema Problem: **Also now re-loads the table to reload the schema. While addressing this, I found a second issue: with_snapshot_id cached the Arrow schema at construction time (the current schema). Pinning across a schema evolution would then push down non-existent column names from the old schema. Fix: Schema resolution is now centralized in a shared snapshot_arrow_schema helper that looks up the snapshot’s own schema via snapshot.schema(metadata). Both with_snapshot_id and IcebergStaticTableProvider constructors use this helper, ensuring a single resolution path. with_snapshot_id is now fallible, as the snapshot ID is validated during pinning. |
|
Hey @xanderbailey can you take a look at this again when you have time? |
Which issue does this PR close?
Related to : #2613
What changes are included in this PR?
Optional Snapshot ID parameter to IcebergTableProvider to pin table reads
Are these changes tested?