Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 4.3k
GH-40343: [C++] Move S3FileSystem to the registry#41559
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -418,6 +418,12 @@ takes precedence over ccache if a storage backend is configured" ON) | ||
| DEPENDS | ||
| ARROW_FILESYSTEM) | ||
| define_option(ARROW_S3_MODULE | ||
raulcd marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| "Build the Arrow S3 filesystem as a dynamic module" | ||
| OFF | ||
| DEPENDS | ||
| ARROW_S3) | ||
| define_option(ARROW_SKYHOOK | ||
| "Build the Skyhook libraries" | ||
| OFF | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -876,6 +876,19 @@ if(ARROW_FILESYSTEM) | ||
| foreach(ARROW_FILESYSTEM_TARGET ${ARROW_FILESYSTEM_TARGETS}) | ||
| target_link_libraries(${ARROW_FILESYSTEM_TARGET} PRIVATE ${AWSSDK_LINK_LIBRARIES}) | ||
| endforeach() | ||
| if(ARROW_S3_MODULE) | ||
| if(NOT ARROW_BUILD_SHARED) | ||
| message(FATAL_ERROR "ARROW_S3_MODULE without shared libarrow (-DARROW_BUILD_SHARED=ON) is not supported" | ||
| ) | ||
| endif() | ||
| add_library(arrow_s3fs MODULE filesystem/s3fs_module.cc filesystem/s3fs.cc) | ||
| target_link_libraries(arrow_s3fs PRIVATE ${AWSSDK_LINK_LIBRARIES} arrow_shared) | ||
| set_source_files_properties(filesystem/s3fs.cc filesystem/s3fs_module.cc | ||
| PROPERTIES SKIP_PRECOMPILE_HEADERS ON | ||
| SKIP_UNITY_BUILD_INCLUSION ON) | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we remove the linking of the main arrow library against MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want to require the main arrow library to load the module. After the module can reproduce the full functionality of the S3FileSystem (S3ProxyOptions, for example) we can deprecate building s3 in the main library. That's not in scope for now, though Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you had any idea on how to make the module expose the missing functionality too? I've been playing around with your PR exposing deftest_filesystem_from_uri_s3(s3_server):
# Load libarrow_s3fs.solibarrow_s3fs_path='/home/raulcd/code/libarrow_s3fs.so'FileSystem.load_file_system(libarrow_s3fs_path)
importctypeslib=ctypes.CDLL(libarrow_s3fs_path, mode=ctypes.RTLD_GLOBAL)
assertlibisnotNonehost, port, access_key, secret_key=s3_server['connection']
uri="s3://{}:{}@mybucket/foo/bar?scheme=http&endpoint_override={}:{}"\
"&allow_bucket_creation=True" \
.format(access_key, secret_key, host, port)
fs, path=FileSystem.from_uri(uri)
assertpath=="mybucket/foo/bar"fs.create_dir(path)
[info] =fs.get_file_info([path])
assertinfo.path==pathassertinfo.type==FileType.DirectoryI am just trying to understand if you had an idea around the next steps. MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The filesystem module supports only construction of filesystems from URI strings (and thereafter we have access only to methods on the FileSystem base interface) so a brief enumeration of functionality missing relative to pyarrow.fs.S3FileSystem is
It is possible to support all of these through URI strings alone, with some caveats:
Once the above changes are made, the full functionality of MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't know why that would be; LoadFileSystemFactories loads symbols locally but the only symbols in libarrow_s3fs would be
... none of which I'd expect to be needed by subsequently loaded shared objects. What symbols are missing? If the AWS libs are "subsequently loaded" then I guess LoadFileSystemFactories will need to load globally as in your python snippet MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course, another way to handle extended features of particular extern"C" {
ARROW_FORCE_EXPORTchar* arrow_filesystem_get_s3_region(constvoid* fs);
}... then in python that function could be retrieved with I recommend avoiding this extra-C-functions approach if possible since it introduces a de-facto ABI that would be a well hidden sharp edge in the library. For resolving s3 region this could be as simple as "the caller is responsible for freeing the returned string if it's not null". However if we consider the more complex example of fully user programmable retry strategy, extra-C-functions would involve
MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After this merges, I will open a draft PR filling out the missing points of S3 support I described above:
I think having a concrete patch to discuss will help focus discussion here and on the ML | ||
| endif() | ||
| endif() | ||
| list(APPEND ARROW_TESTING_SHARED_LINK_LIBS ${ARROW_GTEST_GMOCK}) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -18,6 +18,7 @@ | ||
| #include "arrow/adapters/orc/util.h" | ||
| #include <cmath> | ||
| #include <sstream> | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this? MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is used in | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <vector> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -121,6 +121,24 @@ if(ARROW_S3) | ||
| target_link_libraries(arrow-filesystem-s3fs-benchmark PRIVATE parquet_shared) | ||
| endif() | ||
| endif() | ||
| if(ARROW_S3_MODULE) | ||
| add_arrow_test(s3fs_module_test | ||
| SOURCES | ||
| s3fs_module_test.cc | ||
| s3_test_util.cc | ||
| EXTRA_LABELS | ||
| filesystem | ||
| DEFINITIONS | ||
| ARROW_S3_LIBPATH="$<TARGET_FILE:arrow_s3fs>" | ||
| EXTRA_LINK_LIBS | ||
| Boost::filesystem | ||
| Boost::system) | ||
bkietz marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| target_compile_definitions(arrow-filesystem-test | ||
| PUBLIC ARROW_S3_LIBPATH="$<TARGET_FILE:arrow_s3fs>") | ||
| target_sources(arrow-filesystem-test PUBLIC s3fs_module_test.cc s3_test_util.cc) | ||
| target_link_libraries(arrow-filesystem-test PUBLIC Boost::filesystem Boost::system) | ||
| endif() | ||
| endif() | ||
| if(ARROW_HDFS) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -138,6 +138,7 @@ | ||
| #include "arrow/util/string.h" | ||
| #include "arrow/util/task_group.h" | ||
| #include "arrow/util/thread_pool.h" | ||
| #include "arrow/util/value_parsing.h" | ||
| namespace arrow::fs { | ||
| @@ -169,6 +170,8 @@ static constexpr const char kAwsEndpointUrlEnvVar[] = "AWS_ENDPOINT_URL"; | ||
| static constexpr const char kAwsEndpointUrlS3EnvVar[] = "AWS_ENDPOINT_URL_S3"; | ||
| static constexpr const char kAwsDirectoryContentType[] = "application/x-directory"; | ||
| using namespace std::string_literals; // NOLINT(build/namespaces) | ||
| // ----------------------------------------------------------------------- | ||
| // S3ProxyOptions implementation | ||
| @@ -3082,6 +3085,30 @@ Result<std::string> S3FileSystem::PathFromUri(const std::string& uri_string) con | ||
| internal::AuthorityHandlingBehavior::kPrepend); | ||
| } | ||
| Result<std::string> S3FileSystem::MakeUri(std::string path) const { | ||
| if (path.length() <= 1 || path[0] != '/') { | ||
| return Status::Invalid("MakeUri requires an absolute, non-root path, got ", path); | ||
| } | ||
| ARROW_ASSIGN_OR_RAISE(auto uri, util::UriFromAbsolutePath(path)); | ||
| if (!options().GetAccessKey().empty()) { | ||
| uri = "s3://" + options().GetAccessKey() + ":" + options().GetSecretKey() + "@" + | ||
| uri.substr("file:///"s.size()); | ||
Comment on lines
3094
to
3095
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these be in URIs? Has this always been the case? MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has been in place since S3 URIs were supported, #6403 The URI is intended to be a fully self contained initializer for a filesystem, so if the filesystem requires secrets for initialization then the URI must contain them
| ||
| } else { | ||
| uri = "s3" + uri.substr("file"s.size()); | ||
| } | ||
| uri += "?"; | ||
| uri += "region=" + util::UriEscape(options().region); | ||
| uri += "&"; | ||
| uri += "scheme=" + util::UriEscape(options().scheme); | ||
| uri += "&"; | ||
| uri += "endpoint_override=" + util::UriEscape(options().endpoint_override); | ||
| uri += "&"; | ||
| uri += "allow_bucket_creation="s + (options().allow_bucket_creation ? "1" : "0"); | ||
| uri += "&"; | ||
| uri += "allow_bucket_deletion="s + (options().allow_bucket_deletion ? "1" : "0"); | ||
| return uri; | ||
| } | ||
felipecrv marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| S3Options S3FileSystem::options() const { return impl_->options(); } | ||
| std::string S3FileSystem::region() const { return impl_->region(); } | ||
| @@ -3572,32 +3599,33 @@ bool IsS3Finalized() { return GetAwsInstance()->IsFinalized(); } | ||
| S3GlobalOptions S3GlobalOptions::Defaults() { | ||
| auto log_level = S3LogLevel::Fatal; | ||
| auto result = arrow::internal::GetEnvVar("ARROW_S3_LOG_LEVEL"); | ||
| if (result.ok()) { | ||
| // Extract, trim, and downcase the value of the environment variable | ||
| auto value = | ||
| arrow::internal::AsciiToLower(arrow::internal::TrimString(result.ValueUnsafe())); | ||
| if (value == "fatal") { | ||
| log_level = S3LogLevel::Fatal; | ||
| } else if (value == "error") { | ||
| log_level = S3LogLevel::Error; | ||
| } else if (value == "warn") { | ||
| log_level = S3LogLevel::Warn; | ||
| } else if (value == "info") { | ||
| log_level = S3LogLevel::Info; | ||
| } else if (value == "debug") { | ||
| log_level = S3LogLevel::Debug; | ||
| } else if (value == "trace") { | ||
| log_level = S3LogLevel::Trace; | ||
| } else if (value == "off") { | ||
| log_level = S3LogLevel::Off; | ||
| } | ||
| } | ||
| return S3GlobalOptions{log_level}; | ||
| int num_event_loop_threads = 1; | ||
| // Extract, trim, and downcase the value of the environment variable | ||
| auto value = arrow::internal::GetEnvVar("ARROW_S3_LOG_LEVEL") | ||
| .Map(arrow::internal::AsciiToLower) | ||
| .Map(arrow::internal::TrimString) | ||
| .ValueOr("fatal"); | ||
| if (value == "fatal") { | ||
| log_level = S3LogLevel::Fatal; | ||
| } else if (value == "error") { | ||
| log_level = S3LogLevel::Error; | ||
| } else if (value == "warn") { | ||
| log_level = S3LogLevel::Warn; | ||
| } else if (value == "info") { | ||
| log_level = S3LogLevel::Info; | ||
| } else if (value == "debug") { | ||
| log_level = S3LogLevel::Debug; | ||
| } else if (value == "trace") { | ||
| log_level = S3LogLevel::Trace; | ||
| } else if (value == "off") { | ||
| log_level = S3LogLevel::Off; | ||
| } | ||
| value = arrow::internal::GetEnvVar("ARROW_S3_THREADS").ValueOr("1"); | ||
| if (uint32_t u; ::arrow::internal::ParseUnsigned(value.data(), value.size(), &u)) { | ||
| num_event_loop_threads = u; | ||
| } | ||
| return S3GlobalOptions{log_level, num_event_loop_threads}; | ||
| } | ||
| // ----------------------------------------------------------------------- | ||
| @@ -3615,4 +3643,14 @@ Result<std::string> ResolveS3BucketRegion(const std::string& bucket) { | ||
| return resolver->ResolveRegion(bucket); | ||
| } | ||
| auto kS3FileSystemModule = ARROW_REGISTER_FILESYSTEM( | ||
| "s3", | ||
| [](const arrow::util::Uri& uri, const io::IOContext& io_context, | ||
| std::string* out_path) -> Result<std::shared_ptr<fs::FileSystem>> { | ||
| RETURN_NOT_OK(EnsureS3Initialized()); | ||
| ARROW_ASSIGN_OR_RAISE(auto options, S3Options::FromUri(uri, out_path)); | ||
| return S3FileSystem::Make(options, io_context); | ||
| }, | ||
| [] { DCHECK_OK(EnsureS3Finalized()); }); | ||
| } // namespace arrow::fs | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Licensed to the Apache Software Foundation (ASF) under one | ||
| // or more contributor license agreements. See the NOTICE file | ||
| // distributed with this work for additional information | ||
| // regarding copyright ownership. The ASF licenses this file | ||
| // to you under the Apache License, Version 2.0 (the | ||
| // "License"); you may not use this file except in compliance | ||
| // with the License. You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, | ||
| // software distributed under the License is distributed on an | ||
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| // KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations | ||
| // under the License. | ||
| #include "arrow/filesystem/filesystem_library.h" |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raulcd@pitrou I have rebased and enabled the S3 module build and test here on conda-cpp. Unless there are further comments, I'll merge on Monday