Skip to content

Conversation

@mxxo
Copy link
Contributor

@mxxo mxxo commented Jun 23, 2021

Add a new page source and sink that use S3 as the backing store. Davix
is used as the S3 interface. The implementation is nearly identical
to the DAOS backend and there is a lot of duplicated code.

std::string s3Uri("s3://$(S3_BUCKET).$(S3_HOST)");
{
   auto model = RNTupleModel::Create();
   auto pt = model->MakeField<float>("pt");
   auto vec = model->MakeField<std::vector<int>>("vec");
   // create a new RNTuple named `my_ntuple`
   // -- objects are written to /bucket/my_ntuple/
   auto writer = RNTupleWriter::Recreate(std::move(model), "my_ntuple", s3Uri);
   for (int i = 0; i < 100; i++) {
      *pt = 42.0;
      *vec = {1, 2, 3};
      writer->Fill();
   }
}

// opens the RNTuple at the path /bucket/my_ntuple/
auto ntuple = RNTupleReader::Open("my_ntuple", s3Uri);

results in the following objects stored in the bucket:

my_ntuple/0 # page 0, 1, ... 
my_ntuple/1
my_ntuple/18446744073709551613 # footer
my_ntuple/18446744073709551614 # header
my_ntuple/18446744073709551615 # anchor
my_ntuple/2

Like the current DAOS backend, one object is allocated for every page,
plus three for the header, footer, and anchor. Performance will not be
very good yet as only a single request at a time is issued.

Pages are issued keys sequentially from 0, like the DAOS backend. There
are three reserved keys:

  • anchor: u64(-1)
  • header: u64(-2)
  • footer: u64(-3)

S3 access is controlled using the (ROOT & Davix-compatible) envvars:

  • S3_REGION
  • S3_SECRET_KEY
  • S3_ACCESS_KEY

Perhaps these should be changed to the official AWS envvars.

Todo:

  • re-add cluster caching functionality to PopulatePageFromCluster
  • implement LoadCluster
  • test mocks
  • test with real RNTuples
  • issue concurrent requests to S3

mxxo added 2 commits June 23, 2021 10:19
Add a new page source and sink that use S3 as the backing store. Davix
is used as the S3 interface. The implementation is nearly identical
to the DAOS backend and there is a lot of duplicated code.

Like the current DAOS backend, one object is allocated for every page,
plus three for the header, footer, and anchor. Performance will not be
very good yet as only a single request at a time is issued.

Pages are issued keys sequentially from 0, like the DAOS backend. There
are three reserved keys:
* anchor: u64(-1)
* header: u64(-2)
* footer: u64(-3)

S3 access is controlled using the (ROOT & Davix-compatible) envvars:
* S3_REGION
* S3_SECRET_KEY
* S3_ACCESS_KEY

Perhaps these should be changed to the official AWS envvars.
@phsft-bot
Copy link

Starting build on ROOT-debian10-i386/cxx14, ROOT-performance-centos8-multicore/default, ROOT-ubuntu16/nortcxxmod, mac1014/python3, mac11.0/cxx17, windows10/cxx14
How to customize builds

@mxxo mxxo requested review from jalopezg-git and jblomer June 23, 2021 14:59
@phsft-bot
Copy link

Build failed on mac11.0/cxx17.
Running on macphsft23.dyndns.cern.ch:/Users/sftnight/build/workspace/root-pullrequests-build
See console output.

Failing tests:

This lets users pass in either
s3://bucket.s3_host or s3://bucket.s3_host/ for the location and
my_ntuple or my_ntuple/ for the RNTuple name.
@phsft-bot
Copy link

Starting build on ROOT-debian10-i386/cxx14, ROOT-performance-centos8-multicore/default, ROOT-ubuntu16/nortcxxmod, mac1014/python3, mac11.0/cxx17, windows10/cxx14
How to customize builds

@phsft-bot
Copy link

Build failed on mac11.0/cxx17.
Running on macphsft20.dyndns.cern.ch:/Users/sftnight/build/workspace/root-pullrequests-build
See console output.

Failing tests:

Copy link
Contributor

@jalopezg-git jalopezg-git left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, @mxxo! Much appreciated!

@jblomer and I will address both, the pending tasks in the PR description and the comments added here! We should also look into appropriate mapping from RNTuple pages/clusters to S3 objects.

Comment on lines +97 to +106
# Enable RNTuple support for S3 backend through Davix
if(davix OR builtin_davix)
set(ROOTNTuple_EXTRA_HEADERS ${ROOTNTuple_EXTRA_HEADERS} ROOT/RPageStorageS3.hxx)
target_sources(ROOTNTuple PRIVATE v7/src/RPageStorageS3.cxx)
target_compile_definitions(ROOTNTuple PRIVATE R__ENABLE_DAVIX)

target_include_directories(ROOTNTuple PRIVATE ${DAVIX_INCLUDE_DIR})
target_link_libraries(ROOTNTuple PRIVATE ${DAVIX_LIBRARY})
endif()

Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we can foresee the development of other backends in the future, we might want to have a better way of specifying which backends get built, e.g. -Drntuple_opt_backends=daos,s3 (the file backend should probably be always built).

Perhaps we could discuss the advantages at some point.

Comment on lines 67 to +76
#else
throw RException(R__FAIL("This RNTuple build does not support DAOS."));
#endif

if (location.find("s3://") == 0) {
#ifdef R__ENABLE_DAVIX
return std::make_unique<RPageSourceS3>(ntupleName, location, options);
#else
throw RException(R__FAIL("This RNTuple build does not support S3."));
#endif
}
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO: if we are going to support many backends in the future, maybe we should make the (conditional) construction of the RPageStorageXxx a bit more elegant.

Comment on lines 256 to 265
#else
throw RException(R__FAIL("This RNTuple build does not support DAOS."));
#endif
} else if (location.find("s3://") == 0) {
#ifdef R__ENABLE_DAVIX
return std::make_unique<RPageSinkS3>(ntupleName, location, options);
#else
throw RException(R__FAIL("This RNTuple build does not support S3."));
#endif
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here (see above).

Comment on lines +55 to +60
const char *s3_sec = getenv("S3_SECRET_KEY");
const char *s3_acc = getenv("S3_ACCESS_KEY");
if (s3_sec && s3_acc) {
fReqParams.setAwsAuthorizationKeys(s3_sec, s3_acc);
}
const char *s3_reg = getenv("S3_REGION");
Copy link
Contributor

Choose a reason for hiding this comment

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

@jblomer, maybe we should discuss if environment variables is the right way to pass this information?

namespace Experimental {
namespace Detail {

class RS3Handle {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe change the name of the class, e.g. RS3Bucket, given that it provides read/write access to object in a bucket.

Comment on lines +74 to +76
// Danger: there are no size limits on the amount of data read into the buffer
try {
obj.get(nullptr, buf);
Copy link
Contributor

Choose a reason for hiding this comment

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

Unsure if it is supported by Davix S3 implementation, but we could use DavFile::readPartial() for this.

Comment on lines +290 to +292
std::vector<char> buf;
buf.reserve(RS3NTupleAnchor::GetSize());
fS3Handle->ReadObject(kOidAnchor, buf);
Copy link
Contributor

Choose a reason for hiding this comment

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

If we happen to use DavFile::readPartial(), we should be able to provide a plain char[] buffer here.

(The same applies for the rest below).

Comment on lines +346 to +351
std::vector<char> buf;
buf.reserve(bytesOnStorage);
fS3Handle->ReadObject(std::to_string(pageInfo.fLocator.fPosition), buf);
R__ASSERT(buf.size() == bytesOnStorage);
directReadBuffer = std::make_unique<unsigned char[]>(bytesOnStorage);
memcpy(directReadBuffer.get(), buf.data(), bytesOnStorage);
Copy link
Contributor

Choose a reason for hiding this comment

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

See the comment above.


TEST(RNTuple, S3Basics)
{
std::string s3Uri("s3://ntpl0.s3.us-east-2.amazonaws.com");
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that the test failing in CI because we are not setting the S3_xxx environment variables?

@mxxo
Copy link
Contributor Author

mxxo commented Jul 30, 2021

Davix apparently also supports Azure to some degree: https://davix.web.cern.ch/davix/docs/master/cloud-support.html#microsoft-azure. If there's an implementation that works for S3, Azure support might be as straightforward as swapping out the S3 setup steps for Azure setup. But this is scope creep for this PR and I don't know if Azure is a desired feature for users, etc.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants