refactor(io): make FileIO resolution registry-driven - #889
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors FileIO resolution so ResolvingFileIO routes locations via FileIORegistry factories (registry-driven scheme ownership), improves delegate caching across credential refreshes, narrows S3 routing to s3/s3a/s3n, and adds/updates targeted coverage plus end-user documentation.
Changes:
- Introduces
FileIORegistry::Factory{create, accepts}and scheme-basedFileIORegistry::Resolve()with “latest registration wins” semantics. - Updates
ResolvingFileIOto resolve schemes via the registry (Java-style first-colon parsing) and to cache delegates usingshared_ptrwith refreshed-credential rebuild behavior. - Updates tests, build files, and docs to reflect the new registry-driven routing model and supported S3 schemes.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/iceberg/util/location_util.h | Adds LocationUtil::ParseScheme declaration used for scheme routing. |
| src/iceberg/util/location_util.cc | Implements ParseScheme for Java-style first-colon scheme parsing. |
| src/iceberg/file_io_registry.h | Refactors registry API to struct-based factories and adds Resolve(scheme). |
| src/iceberg/file_io_registry.cc | Implements ordered registrations, explicit load, and scheme resolution. |
| src/iceberg/resolving_file_io.h | Switches caching to shared_ptr and updates locking to shared_mutex. |
| src/iceberg/resolving_file_io.cc | Routes per-location via FileIORegistry::Resolve, caches delegates, rebuilds on credential refresh, and groups bulk deletes by delegate. |
| src/iceberg/catalog/rest/rest_file_io.cc | Defaults REST catalog FileIO to directly constructed ResolvingFileIO when io-impl is absent. |
| src/iceberg/arrow/arrow_register.cc | Registers built-in local/S3 FileIOs with create + accepts callbacks. |
| src/iceberg/arrow/s3/s3_properties.h | Centralizes S3 scheme list and helpers for scheme acceptance. |
| src/iceberg/arrow/s3/arrow_s3_file_io.cc | Removes OSS alias handling from S3 credential prefix logic and scheme canonicalization. |
| src/iceberg/util/location_util.cc | (Also) relocates scheme parsing logic into a shared util. |
| src/iceberg/test/rest_file_io_test.cc | Adjusts default REST FileIO expectation; adds a registry-delegation test; updates registry registration callsites. |
| src/iceberg/test/rest_catalog_integration_test.cc | Updates registry registration to new factory struct form. |
| src/iceberg/test/resolving_file_io_test.cc | Updates routing assumptions (no OSS alias), adds batch delete grouping tests, and validates no fallback after selected factory failure. |
| src/iceberg/test/location_util_test.cc | Adds unit test coverage for ParseScheme. |
| src/iceberg/test/arrow_s3_file_io_test.cc | Removes OSS from S3-compatible credential prefixes; updates endpoint scheme test data away from OSS-specific values. |
| src/iceberg/test/arrow_io_test.cc | Adds a registration smoke test ensuring built-in registry routing works via ResolvingFileIO. |
| src/iceberg/test/rest_arrow_file_io_test.cc | Removes an integration test that depended on the prior OSS/S3 routing behavior and bundle linkage. |
| src/iceberg/test/CMakeLists.txt | Removes bundle-only REST Arrow FileIO test wiring and USE_BUNDLE option. |
| src/iceberg/CMakeLists.txt | Adds util/location_util.cc to the CMake build. |
| src/iceberg/meson.build | Adds util/location_util.cc to the Meson build. |
| mkdocs/mkdocs.yml | Adds FileIO docs page to the documentation nav. |
| mkdocs/docs/file-io.md | Documents built-in/custom FileIO registration and selection, plus credential forwarding behavior. |
Suppressed comments (1)
src/iceberg/util/location_util.cc:28
ParseSchemecurrently treats any text before the first ':' as a scheme, even for local paths that may contain ':' (e.g. WindowsC:\\...or a POSIX path segment with ':'). That makesResolvingFileIOattempt registry resolution for what should be a local path and can yield kNotSupported.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Add scheme-aware FileIO factories with deterministic registration precedence, route ResolvingFileIO by location scheme, and forward vended storage credentials through registered delegates.
0e9424e to
cc580e9
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/iceberg/util/location_util.cc:29
ParseSchemetreats any first-colon prefix as a scheme, which will interpret Windows drive-letter paths likeC:\tmp\file.parquet/C:/tmp/file.parquetas schemecand break local FileIO resolution (local accepts only empty orfile). Consider special-casing drive-letter paths on Windows so they are treated as "no scheme".
src/iceberg/test/location_util_test.cc:73ParseSchemenow follows first-colon parsing; add a Windows drive-letter case to the unit test to prevent regressions for local paths likeC:\\tmp\\file.parquet/C:/tmp/file.parquet.
TEST(LocationUtilTest, ParseScheme) {
auto s3 = LocationUtil::ParseScheme("S3://bucket/path");
EXPECT_EQ(s3, "S3");
auto no_scheme = LocationUtil::ParseScheme("/tmp/file.parquet");
EXPECT_TRUE(no_scheme.empty());
auto empty_scheme = LocationUtil::ParseScheme("://bucket/path");
EXPECT_TRUE(empty_scheme.empty());
|
I've made some refactoring to the FileIO resolution. Let me know what you think. @plusplusjiajia |
@wgtmac Thanks for looping me in — registry-driven resolution is right, and credential prefixes — agree. routing — I'd keep |
| bool IsS3FileIOCredentialPrefix(std::string_view prefix) { | ||
| return prefix == "s3" || prefix.starts_with("s3://") || prefix.starts_with("s3a://") || | ||
| prefix.starts_with("s3n://") || prefix.starts_with("oss://"); | ||
| prefix.starts_with("s3n://"); |
There was a problem hiding this comment.
We’d be better off using a macro for this?
| Result<std::shared_ptr<FileIO>> ResolvingFileIO::FileIOForPath( | ||
| std::string_view location) { | ||
| const auto scheme = StringUtils::ToLower(LocationUtil::ParseScheme(location)); | ||
| ICEBERG_ASSIGN_OR_RAISE(const auto name, FileIORegistry::Resolve(scheme)); |
There was a problem hiding this comment.
ResolvingFileIO does not have a bound stable registry snapshot therefore, the parsing results returned by FileIO may be affected by the process-wide mutable registry ?
example:
Step 1: A(s3) Registration
Step 2: Resolver resolves s3 -> A
Time 3: A is replaced by a record with the same name.
Result: Resolver still hit an old cache entry named "A".
I just feel maybe there is this kind of problem.
Problem
The existing FileIO design embeds scheme ownership in
ResolvingFileIOandregisters the resolver itself as a special implementation. This duplicates
backend-specific scheme knowledge, prevents custom FileIOs from declaring the
schemes they support, and couples automatic routing to a special registry
entry.
Cached delegates are also returned as raw pointers while credential refresh
can invalidate the cache. In addition, treating
oss://as an S3 alias is notsafe without provider-specific endpoint and compatibility validation.
Changes
FileIORegistry::Factorycontain a requiredcreatecallback and anoptional
acceptscallback.registrations overriding earlier ones.
ResolvingFileIOdirectly while preserving explicitio-implprecedence.mapping from the resolver.
shared_ptracross credential refreshes.delegates after refresh.
s3,s3a, ands3n; defer OSS/COS support to a separate change.