Skip to content

Commit 47ed041

Browse files
authored
Merge pull request facebook#12 from pikiwidb/async/download
feat: support async download sst files when applying manifest
2 parents a654250 + 908f10b commit 47ed041

10 files changed

+202
-14
lines changed

cloud/aws/aws_retry.cc

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ Status AwsCloudOptions::GetClientConfiguration(
154154
config->useVirtualAddressing = false;
155155

156156
config->region = ToAwsString(region);
157+
config->throughputTargetGbps = cloud_fs_options.throughput_target_gbps;
157158
return Status::OK();
158159
}
159160
#else

cloud/aws/aws_s3.cc

+108-7
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ class AwsS3ClientWrapper {
201201
return outcome;
202202
}
203203

204+
void GetCloudObjectAsync(
205+
const Aws::S3Crt::Model::GetObjectRequest& request,
206+
const Aws::S3Crt::GetObjectResponseReceivedHandler& handler) {
207+
client_->GetObjectAsync(request, handler, nullptr);
208+
}
209+
204210
template <class... Args>
205211
std::shared_ptr<Aws::Transfer::TransferHandle> DownloadFile(Args... args) {
206212
CloudRequestCallbackGuard guard(cloud_request_callback_.get(),
@@ -434,6 +440,11 @@ class S3StorageProvider : public CloudStorageProviderImpl {
434440
IODebugContext* dbg) override;
435441
Status PrepareOptions(const ConfigOptions& options) override;
436442
protected:
443+
IOStatus DoGetCloudObjectAsync(
444+
const std::string& bucket_name, const std::string& object_path,
445+
const std::string& local_path,
446+
std::shared_ptr<std::promise<bool>> prom_ptr) override;
447+
437448
IOStatus DoGetCloudObject(const std::string& bucket_name,
438449
const std::string& object_path,
439450
const std::string& destination,
@@ -729,7 +740,7 @@ IOStatus S3StorageProvider::ExistsCloudObject(const std::string& bucket_name,
729740
IOStatus S3StorageProvider::GetCloudObjectSize(const std::string& bucket_name,
730741
const std::string& object_path,
731742
uint64_t* filesize) {
732-
HeadObjectResult result;
743+
HeadObjectResult result;
733744
result.size = filesize;
734745
return HeadObject(bucket_name, object_path, &result);
735746
}
@@ -900,6 +911,15 @@ class WritableFileStreamBuf : public std::streambuf {
900911
*fileCloseStatus_ = fileWriter_->Close();
901912
}
902913

914+
// Flushes any buffered data
915+
// move sync() from protected region to public
916+
// because Close() is called after async handler been executed
917+
// sync() must be called in async handler, or the file is incomplete
918+
int sync() override {
919+
auto st = fileWriter_->Flush();
920+
return st.ok() ? 0 : -1;
921+
}
922+
903923
protected:
904924
// Appends a block of data to the stream. Must always write n if possible
905925
std::streamsize xsputn(const char* s, std::streamsize n) override {
@@ -924,17 +944,26 @@ class WritableFileStreamBuf : public std::streambuf {
924944
return ch;
925945
}
926946

927-
// Flushes any buffered data
928-
int sync() override {
929-
auto st = fileWriter_->Flush();
930-
return st.ok() ? 0 : -1;
931-
}
932-
933947
private:
934948
IOStatus *fileCloseStatus_;
935949
std::unique_ptr<WritableFileWriter> fileWriter_;
936950
};
937951

952+
// shared_ptr version of IOStreamWithOwnedBuf
953+
// because Writablefile pointer must be reachable in async callback
954+
// std::iostream takes a raw pointer to std::streambuf. This subclass
955+
// takes a shared_ptr to the streambuf, tying the std::streambuf's
956+
// lifetime to the iostream's.
957+
template <class T>
958+
class IOStreamWithOwnedBufSPtr : public std::iostream {
959+
public:
960+
IOStreamWithOwnedBufSPtr(std::shared_ptr<T> s)
961+
: std::iostream(s.get()), s_(s) {}
962+
963+
private:
964+
std::shared_ptr<T> s_;
965+
};
966+
938967
// std::iostream takes a raw pointer to std::streambuf. This subclass
939968
// takes a unique_ptr to the streambuf, tying the std::streambuf's
940969
// lifetime to the iostream's.
@@ -950,6 +979,78 @@ class IOStreamWithOwnedBuf : public std::iostream {
950979

951980
} // namespace
952981

982+
IOStatus S3StorageProvider::DoGetCloudObjectAsync(
983+
const std::string& bucket_name, const std::string& object_path,
984+
const std::string& local_path,
985+
std::shared_ptr<std::promise<bool>> prom_ptr) {
986+
std::string tmp_destination =
987+
local_path + ".tmp-" + std::to_string(rng_.Next());
988+
989+
std::shared_ptr<IOStatus> fileCloseStatus = std::make_shared<IOStatus>();
990+
991+
FileOptions foptions;
992+
foptions.use_direct_writes =
993+
cfs_->GetCloudFileSystemOptions().use_direct_io_for_cloud_download;
994+
std::unique_ptr<FSWritableFile> file;
995+
auto st = NewWritableFile(cfs_->GetBaseFileSystem().get(), tmp_destination,
996+
&file, foptions);
997+
if (!st.ok()) {
998+
Log(InfoLogLevel::ERROR_LEVEL, cfs_->GetLogger(),
999+
"create writeablefile for async download failed, msg: %s",
1000+
st.ToString().c_str());
1001+
prom_ptr->set_value(false);
1002+
return st;
1003+
}
1004+
std::shared_ptr<WritableFileStreamBuf> file_stream_buf =
1005+
std::make_shared<WritableFileStreamBuf>(
1006+
fileCloseStatus.get(),
1007+
std::unique_ptr<WritableFileWriter>(new WritableFileWriter(
1008+
std::move(file), tmp_destination, foptions)));
1009+
1010+
auto ioStreamFactory = [file_stream_buf,
1011+
fileCloseStatus]() -> Aws::IOStream* {
1012+
return Aws::New<IOStreamWithOwnedBufSPtr<WritableFileStreamBuf>>(
1013+
Aws::Utils::ARRAY_ALLOCATION_TAG, file_stream_buf);
1014+
};
1015+
1016+
Aws::S3Crt::Model::GetObjectRequest request;
1017+
request.SetBucket(ToAwsString(bucket_name));
1018+
request.SetKey(ToAwsString(object_path));
1019+
request.SetResponseStreamFactory(std::move(ioStreamFactory));
1020+
1021+
auto handler = Aws::S3Crt::GetObjectResponseReceivedHandler{
1022+
[this, tmp_destination, local_path, prom_ptr, file_stream_buf](
1023+
const Aws::S3Crt::S3CrtClient*,
1024+
const Aws::S3Crt::Model::GetObjectRequest&,
1025+
Aws::S3Crt::Model::GetObjectOutcome outcome,
1026+
const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) {
1027+
file_stream_buf->sync();
1028+
const auto& local_fs = cfs_->GetBaseFileSystem();
1029+
const IOOptions io_opts;
1030+
IODebugContext* dbg = nullptr;
1031+
auto remote_size = outcome.GetResult().GetContentLength();
1032+
uint64_t local_size{0};
1033+
auto s =
1034+
local_fs->GetFileSize(tmp_destination, io_opts, &local_size, dbg);
1035+
if (!outcome.IsSuccess() || !s.ok() ||
1036+
local_size != uint64_t(remote_size)) {
1037+
local_fs->DeleteFile(tmp_destination, io_opts, dbg);
1038+
Log(InfoLogLevel::ERROR_LEVEL, cfs_->GetLogger(),
1039+
"async download error, outcome: %d, local_size: %lu, "
1040+
"remote_size: %lu",
1041+
outcome.IsSuccess(), local_size, uint64_t(remote_size));
1042+
prom_ptr->set_value(false);
1043+
return;
1044+
}
1045+
local_fs->RenameFile(tmp_destination, local_path, io_opts, dbg);
1046+
cfs_->FileCacheInsert(local_path, local_size);
1047+
prom_ptr->set_value(true);
1048+
}};
1049+
1050+
s3client_->GetCloudObjectAsync(request, handler);
1051+
return IOStatus::OK();
1052+
}
1053+
9531054
IOStatus S3StorageProvider::DoGetCloudObject(const std::string& bucket_name,
9541055
const std::string& object_path,
9551056
const std::string& destination,

cloud/cloud_file_system_impl.cc

+30
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ IOStatus CloudFileSystemImpl::GetCloudObject(const std::string& fname) {
7878
return st;
7979
}
8080

81+
IOStatus CloudFileSystemImpl::GetCloudObjectAsync(
82+
const std::string& fname, std::shared_ptr<std::promise<bool>> prom_ptr) {
83+
auto st = GetStorageProvider()->GetCloudObjectAsync(
84+
GetDestBucketName(), destname(fname), fname, prom_ptr);
85+
return st;
86+
}
87+
8188
IOStatus CloudFileSystemImpl::GetCloudObjectSize(const std::string& fname,
8289
uint64_t* remote_size) {
8390
auto st = IOStatus::NotFound();
@@ -134,6 +141,29 @@ IOStatus CloudFileSystemImpl::ListCloudObjects(
134141
return st;
135142
}
136143

144+
IOStatus CloudFileSystemImpl::DownloadAsync(
145+
const std::string& logical_fname,
146+
std::shared_ptr<std::promise<bool>> prom_ptr) {
147+
IOOptions io_opts;
148+
auto fname = RemapFilename(logical_fname);
149+
auto file_type = GetFileType(fname);
150+
IOStatus st;
151+
assert(file_type == RocksDBFileType::kSstFile);
152+
Log(InfoLogLevel::INFO_LEVEL, info_log_, "async download fname: %s",
153+
logical_fname.c_str(), fname.c_str());
154+
if (cloud_fs_options.hasSstFileCache()) {
155+
st = base_fs_->FileExists(fname, io_opts, nullptr);
156+
if (st.ok()) {
157+
FileCacheAccess(fname);
158+
prom_ptr->set_value(true);
159+
return st;
160+
}
161+
}
162+
163+
GetCloudObjectAsync(fname, prom_ptr);
164+
return IOStatus::OK();
165+
}
166+
137167
IOStatus CloudFileSystemImpl::NewCloudReadableFile(
138168
const std::string& fname, const FileOptions& options,
139169
std::unique_ptr<CloudStorageReadableFile>* result, IODebugContext* dbg) {

cloud/cloud_file_system_impl.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class CloudFileSystemImpl : public CloudFileSystem {
5959
std::unique_ptr<FSWritableFile>* result,
6060
IODebugContext* dbg) override;
6161

62+
IOStatus DownloadAsync(const std::string& fname,
63+
std::shared_ptr<std::promise<bool>> prom_ptr) override;
64+
6265
IOStatus ReopenWritableFile(const std::string& fname,
6366
const FileOptions& options,
6467
std::unique_ptr<FSWritableFile>* result,
@@ -155,7 +158,7 @@ class CloudFileSystemImpl : public CloudFileSystem {
155158

156159
// Find all live files based on cloud_manifest_ and local MANIFEST FILE
157160
// If local MANIFEST file doesn't exist, it will pull from cloud
158-
//
161+
//
159162
// REQUIRES: cloud_manifest_ is loaded
160163
// REQUIRES: cloud_manifest_ is not updated when calling this function
161164
IOStatus FindAllLiveFiles(const std::string& local_dbname,
@@ -381,6 +384,9 @@ class CloudFileSystemImpl : public CloudFileSystem {
381384
private:
382385
bool WaitPendingObjects() override;
383386

387+
IOStatus GetCloudObjectAsync(const std::string& fname,
388+
std::shared_ptr<std::promise<bool>> prom_ptr);
389+
384390
// Delete all local files that are invisible
385391
IOStatus DeleteLocalInvisibleFiles(
386392
const std::string& dbname,

cloud/cloud_storage_provider.cc

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#include "rocksdb/cloud/cloud_storage_provider.h"
55

6+
#include <aws/s3-crt/S3CrtServiceClientModel.h>
7+
68
#include <cinttypes>
79
#include <mutex>
810
#include <set>
@@ -316,6 +318,14 @@ IOStatus CloudStorageProviderImpl::NewCloudReadableFile(
316318
options, result, dbg);
317319
}
318320

321+
IOStatus CloudStorageProviderImpl::GetCloudObjectAsync(
322+
const std::string& bucket_name, const std::string& object_path,
323+
const std::string& local_path,
324+
std::shared_ptr<std::promise<bool>> prom_ptr) {
325+
const auto& local_fs = cfs_->GetBaseFileSystem();
326+
return DoGetCloudObjectAsync(bucket_name, object_path, local_path, prom_ptr);
327+
}
328+
319329
IOStatus CloudStorageProviderImpl::GetCloudObject(
320330
const std::string& bucket_name, const std::string& object_path,
321331
const std::string& local_destination) {

cloud/cloud_storage_provider_impl.h

+11
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ class CloudStorageProviderImpl : public CloudStorageProvider {
128128
IOStatus PutCloudObject(const std::string& local_file,
129129
const std::string& bucket_name,
130130
const std::string& object_path) override;
131+
132+
IOStatus GetCloudObjectAsync(
133+
const std::string& bucket_name, const std::string& object_path,
134+
const std::string& local_path,
135+
std::shared_ptr<std::promise<bool>> prom_ptr) override;
136+
131137
IOStatus NewCloudReadableFile(
132138
const std::string& bucket, const std::string& fname,
133139
const FileOptions& options,
@@ -143,6 +149,11 @@ class CloudStorageProviderImpl : public CloudStorageProvider {
143149
std::unique_ptr<CloudStorageReadableFile>* result,
144150
IODebugContext* dbg) = 0;
145151

152+
virtual IOStatus DoGetCloudObjectAsync(
153+
const std::string& bucket_name, const std::string& object_path,
154+
const std::string& local_path,
155+
std::shared_ptr<std::promise<bool>> prom_ptr) = 0;
156+
146157
// Downloads object from the cloud into a local directory
147158
virtual IOStatus DoGetCloudObject(const std::string& bucket_name,
148159
const std::string& object_path,

db/version_builder.cc

+20-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <atomic>
1414
#include <cinttypes>
1515
#include <functional>
16+
#include <future>
1617
#include <map>
1718
#include <memory>
1819
#include <set>
@@ -233,7 +234,7 @@ class VersionBuilder::Rep {
233234
};
234235

235236
const FileOptions& file_options_;
236-
const ImmutableCFOptions* const ioptions_;
237+
const ImmutableOptions* const ioptions_;
237238
TableCache* table_cache_;
238239
VersionStorageInfo* base_vstorage_;
239240
VersionSet* version_set_;
@@ -261,7 +262,7 @@ class VersionBuilder::Rep {
261262
std::shared_ptr<CacheReservationManager> file_metadata_cache_res_mgr_;
262263

263264
public:
264-
Rep(const FileOptions& file_options, const ImmutableCFOptions* ioptions,
265+
Rep(const FileOptions& file_options, const ImmutableOptions* ioptions,
265266
TableCache* table_cache, VersionStorageInfo* base_vstorage,
266267
VersionSet* version_set,
267268
std::shared_ptr<CacheReservationManager> file_metadata_cache_res_mgr)
@@ -594,7 +595,7 @@ class VersionBuilder::Rep {
594595
// Note: we use C++11 for now but in C++14, this could be done in a more
595596
// elegant way using generalized lambda capture.
596597
VersionSet* const vs = version_set_;
597-
const ImmutableCFOptions* const ioptions = ioptions_;
598+
const ImmutableOptions* const ioptions = ioptions_;
598599

599600
auto deleter = [vs, ioptions](SharedBlobFileMetaData* shared_meta) {
600601
if (vs) {
@@ -1245,12 +1246,20 @@ class VersionBuilder::Rep {
12451246
// <file metadata, level>
12461247
std::vector<std::pair<FileMetaData*, int>> files_meta;
12471248
std::vector<Status> statuses;
1249+
std::vector<std::future<bool>> pending_downloads;
12481250
for (int level = 0; level < num_levels_; level++) {
12491251
for (auto& file_meta_pair : levels_[level].added_files) {
12501252
auto* file_meta = file_meta_pair.second;
12511253
// If the file has been opened before, just skip it.
12521254
if (!file_meta->table_reader_handle) {
1255+
std::shared_ptr<std::promise<bool>> prom_ptr =
1256+
std::make_shared<std::promise<bool>>();
12531257
files_meta.emplace_back(file_meta, level);
1258+
pending_downloads.emplace_back(prom_ptr->get_future());
1259+
std::string fname =
1260+
TableFileName(ioptions_->cf_paths, file_meta->fd.GetNumber(),
1261+
file_meta->fd.GetPathId());
1262+
ioptions_->fs->DownloadAsync(fname, prom_ptr);
12541263
statuses.emplace_back(Status::OK());
12551264
}
12561265
if (files_meta.size() >= max_load) {
@@ -1262,6 +1271,13 @@ class VersionBuilder::Rep {
12621271
}
12631272
}
12641273

1274+
// wait until all async download finished
1275+
// here we skip validating future returned value
1276+
// because it will redownload if local files were missing
1277+
for (auto& fut : pending_downloads) {
1278+
fut.get();
1279+
}
1280+
12651281
std::atomic<size_t> next_file_meta_idx(0);
12661282
std::function<void()> load_handlers_func([&]() {
12671283
while (true) {
@@ -1309,7 +1325,7 @@ class VersionBuilder::Rep {
13091325
};
13101326

13111327
VersionBuilder::VersionBuilder(
1312-
const FileOptions& file_options, const ImmutableCFOptions* ioptions,
1328+
const FileOptions& file_options, const ImmutableOptions* ioptions,
13131329
TableCache* table_cache, VersionStorageInfo* base_vstorage,
13141330
VersionSet* version_set,
13151331
std::shared_ptr<CacheReservationManager> file_metadata_cache_res_mgr)

db/version_builder.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
namespace ROCKSDB_NAMESPACE {
1818

19-
struct ImmutableCFOptions;
19+
struct ImmutableOptions;
2020
class TableCache;
2121
class VersionStorageInfo;
2222
class VersionEdit;
@@ -33,7 +33,7 @@ class CacheReservationManager;
3333
class VersionBuilder {
3434
public:
3535
VersionBuilder(const FileOptions& file_options,
36-
const ImmutableCFOptions* ioptions, TableCache* table_cache,
36+
const ImmutableOptions* ioptions, TableCache* table_cache,
3737
VersionStorageInfo* base_vstorage, VersionSet* version_set,
3838
std::shared_ptr<CacheReservationManager>
3939
file_metadata_cache_res_mgr = nullptr);

0 commit comments

Comments
 (0)