Skip to content

[WIP] Fixing extra download for filesystem's localization #254

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

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/filesystem/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -619,11 +619,12 @@ ReadBinaryProto(const std::string& path, google::protobuf::MessageLite* msg)
}

Status
MakeDirectory(const std::string& dir, const bool recursive)
MakeDirectory(
const std::string& dir, const bool recursive, const bool allow_dir_exist)
{
std::shared_ptr<FileSystem> fs;
RETURN_IF_ERROR(fsm_.GetFileSystem(dir, fs));
return fs->MakeDirectory(dir, recursive);
return fs->MakeDirectory(dir, recursive, allow_dir_exist);
}

Status
Expand Down
10 changes: 8 additions & 2 deletions src/filesystem/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Status LocalizePath(
/// \param path The path of the directory or file.
/// \param recursive If true, will fetch all sub-directories in
/// the provided path.
/// \param mount_dir If specified, will use provided local directory
/// \param mount_dir If not empty, will use provided local directory
/// for localization.
/// \param localized Returns the LocalizedPath object
/// representing the local copy of the path.
Expand Down Expand Up @@ -205,8 +205,14 @@ Status ReadBinaryProto(
/// \param dir The path to the directory.
/// \param recursive Whether the parent directories will be created
/// if not exist.
/// \param allow_dir_exist Controls the behavior on condition,
/// when `dir` already exists. If true and `dir` exists, returns success.
/// If false and `dir` exists, fails with `Status::Code::INTERNAL`
/// and reports errno EEXIST. Default value: false.
/// \return Error status if the directory can't be created
Status MakeDirectory(const std::string& dir, const bool recursive);
Status MakeDirectory(
const std::string& dir, const bool recursive,
const bool allow_dir_exist = false);

/// Create a temporary directory of the specified filesystem type.
/// \param type The type of the filesystem.
Expand Down
66 changes: 42 additions & 24 deletions src/filesystem/implementations/as.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ class ASFileSystem : public FileSystem {
Status WriteBinaryFile(
const std::string& path, const char* contents,
const size_t content_len) override;
Status MakeDirectory(const std::string& dir, const bool recursive) override;
Status MakeDirectory(
const std::string& dir, const bool recursive,
const bool allow_dir_exist) override;
Status MakeTemporaryDirectory(std::string* temp_dir) override;
Status DeletePath(const std::string& path) override;

Expand All @@ -114,7 +116,8 @@ class ASFileSystem : public FileSystem {

Status DownloadFolder(
const std::string& container, const std::string& path,
const std::string& dest);
const std::string& dest, const bool recursive,
const bool allow_dir_exist);

std::shared_ptr<asb::BlobServiceClient> client_;
re2::RE2 as_regex_;
Expand Down Expand Up @@ -390,7 +393,7 @@ ASFileSystem::FileExists(const std::string& path, bool* exists)
Status
ASFileSystem::DownloadFolder(
const std::string& container, const std::string& path,
const std::string& dest)
const std::string& dest, const bool recursive, const bool allow_dir_exist)
{
auto container_client = client_->GetBlobContainerClient(container);
auto func = [&](const std::vector<asb::Models::BlobItem>& blobs,
Expand All @@ -406,17 +409,21 @@ ASFileSystem::DownloadFolder(
"Failed to download file at " + blob_item.Name + ":" + ex.what());
}
}
for (const auto& directory_item : blob_prefixes) {
const auto& local_path = JoinPath({dest, BaseName(directory_item)});
int status = mkdir(
const_cast<char*>(local_path.c_str()), S_IRUSR | S_IWUSR | S_IXUSR);
if (status == -1) {
return Status(
Status::Code::INTERNAL,
"Failed to create local folder: " + local_path +
", errno:" + strerror(errno));
if (recursive) {
for (const auto& directory_item : blob_prefixes) {
const auto& local_path = JoinPath({dest, BaseName(directory_item)});
int status = mkdir(
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the FileSystem wrapped API be used here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right, for Windows. good catch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed

const_cast<char*>(local_path.c_str()), S_IRUSR | S_IWUSR | S_IXUSR);
if (status == -1 && !(allow_dir_exist && errno == EEXIST)) {
return Status(
Status::Code::INTERNAL,
"Failed to create local folder: " + local_path +
", errno:" + strerror(errno));
}
RETURN_IF_ERROR(DownloadFolder(
container, directory_item, local_path, recursive,
true /*allow_dir_exist*/));
}
RETURN_IF_ERROR(DownloadFolder(container, directory_item, local_path));
}
return Status::Success;
};
Expand All @@ -443,21 +450,31 @@ ASFileSystem::LocalizePath(
"AS file localization not yet implemented " + path);
}

std::string folder_template = "/tmp/folderXXXXXX";
char* tmp_folder = mkdtemp(const_cast<char*>(folder_template.c_str()));
if (tmp_folder == nullptr) {
return Status(
Status::Code::INTERNAL,
"Failed to create local temp folder: " + folder_template +
", errno:" + strerror(errno));
// Create a local directory for s3 model store.
// If `mount_dir` or ENV variable are not set,
// creates a temporary directory under `/tmp` with the format: "folderXXXXXX".
// Otherwise, will create a folder under specified directory with the name
// indicated in path (i.e. everything after the last encounter of `/`).
const char* env_mount_dir = std::getenv("TRITON_AZURE_MOUNT_DIRECTORY");
std::string tmp_folder;
if (mount_dir.empty() && env_mount_dir == nullptr) {
Comment on lines +451 to +453
Copy link
Contributor

Choose a reason for hiding this comment

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

If there's already a follow-up ticket, I'd like to see use of some helper function like this one that safely checks if the env var is set or is nullptr, and just returns a std::string. This helper could probably just take an env var name as an argument to condense the usage.

Currently this if/else is OK, but if the conditions were to be tweaked in the future, there is some risk for std::string(env_mount_dir) when it is a nullptr.

RETURN_IF_ERROR(triton::core::MakeTemporaryDirectory(
FileSystemType::LOCAL, &tmp_folder));
} else {
tmp_folder = mount_dir.empty() ? std::string(env_mount_dir) : mount_dir;
tmp_folder =
JoinPath({tmp_folder, path.substr(path.find_last_of('/') + 1)});
RETURN_IF_ERROR(triton::core::MakeDirectory(
tmp_folder, true /*recursive*/, true /*allow_dir_exist*/));
Comment on lines +457 to +461
Copy link
Contributor

Choose a reason for hiding this comment

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

@oandreeva-nv just checking my understanding, I see your comment saying:

Finally, current requirements for mount directory:

  1. Should exist before

But in the code I'm seeing calls to MakeDirectory(), which I'm inferring would create the directory if it doesn't exist.

So to clarify, is the mount directory required to exist before server startup? Or will Triton create it on demand, similar to tmp folder workflow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will clarify this in a follow-up commit. This PR would need more de-bugging and testing

}
localized->reset(new LocalizedPath(path, tmp_folder));

std::string dest(folder_template);
localized->reset(new LocalizedPath(path, tmp_folder));

std::string dest(tmp_folder);
std::string container, blob;
RETURN_IF_ERROR(ParsePath(path, &container, &blob));
return DownloadFolder(container, blob, dest);
return DownloadFolder(
container, blob, dest, recursive, true /*allow_dir_exist*/);
}

Status
Expand Down Expand Up @@ -489,7 +506,8 @@ ASFileSystem::WriteBinaryFile(
}

Status
ASFileSystem::MakeDirectory(const std::string& dir, const bool recursive)
ASFileSystem::MakeDirectory(
const std::string& dir, const bool recursive, const bool allow_dir_exist)
{
return Status(
Status::Code::UNSUPPORTED,
Expand Down
3 changes: 2 additions & 1 deletion src/filesystem/implementations/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ class FileSystem {
const std::string& path, const char* contents,
const size_t content_len) = 0;
virtual Status MakeDirectory(
const std::string& dir, const bool recursive) = 0;
const std::string& dir, const bool recursive,
const bool allow_dir_exist) = 0;
virtual Status MakeTemporaryDirectory(std::string* temp_dir) = 0;
virtual Status DeletePath(const std::string& path) = 0;
};
Expand Down
29 changes: 23 additions & 6 deletions src/filesystem/implementations/gcs.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ class GCSFileSystem : public FileSystem {
Status WriteBinaryFile(
const std::string& path, const char* contents,
const size_t content_len) override;
Status MakeDirectory(const std::string& dir, const bool recursive) override;
Status MakeDirectory(
const std::string& dir, const bool recursive,
const bool allow_dir_exist) override;
Status MakeTemporaryDirectory(std::string* temp_dir) override;
Status DeletePath(const std::string& path) override;

Expand Down Expand Up @@ -382,9 +384,23 @@ GCSFileSystem::LocalizePath(
"GCS file localization not yet implemented " + path);
}

// Create a local directory for s3 model store.
// If `mount_dir` or ENV variable are not set,
// creates a temporary directory under `/tmp` with the format: "folderXXXXXX".
// Otherwise, will create a folder under specified directory with the name
// indicated in path (i.e. everything after the last encounter of `/`).
const char* env_mount_dir = std::getenv("TRITON_GCS_MOUNT_DIRECTORY");
std::string tmp_folder;
RETURN_IF_ERROR(
triton::core::MakeTemporaryDirectory(FileSystemType::LOCAL, &tmp_folder));
if (mount_dir.empty() && env_mount_dir == nullptr) {
RETURN_IF_ERROR(triton::core::MakeTemporaryDirectory(
FileSystemType::LOCAL, &tmp_folder));
} else {
tmp_folder = mount_dir.empty() ? std::string(env_mount_dir) : mount_dir;
tmp_folder =
JoinPath({tmp_folder, path.substr(path.find_last_of('/') + 1)});
RETURN_IF_ERROR(triton::core::MakeDirectory(
tmp_folder, true /*recursive*/, true /*allow_dir_exist*/));
}

localized->reset(new LocalizedPath(path, tmp_folder));

Expand All @@ -404,7 +420,7 @@ GCSFileSystem::LocalizePath(
std::string local_fpath =
JoinPath({(*localized)->Path(), gcs_removed_path});
RETURN_IF_ERROR(IsDirectory(gcs_fpath, &is_subdir));
if (is_subdir) {
if (recursive && is_subdir) {
// Create local mirror of sub-directories
#ifdef _WIN32
int status = mkdir(const_cast<char*>(local_fpath.c_str()));
Expand All @@ -427,7 +443,7 @@ GCSFileSystem::LocalizePath(
++itr) {
contents.insert(JoinPath({gcs_fpath, *itr}));
}
} else {
} else if (!is_subdir) {
// Create local copy of file
std::string file_bucket, file_object;
RETURN_IF_ERROR(ParsePath(gcs_fpath, &file_bucket, &file_object));
Expand Down Expand Up @@ -474,7 +490,8 @@ GCSFileSystem::WriteBinaryFile(
}

Status
GCSFileSystem::MakeDirectory(const std::string& dir, const bool recursive)
GCSFileSystem::MakeDirectory(
const std::string& dir, const bool recursive, const bool allow_dir_exist)
{
return Status(
Status::Code::UNSUPPORTED,
Expand Down
13 changes: 8 additions & 5 deletions src/filesystem/implementations/local.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ class LocalFileSystem : public FileSystem {
Status WriteBinaryFile(
const std::string& path, const char* contents,
const size_t content_len) override;
Status MakeDirectory(const std::string& dir, const bool recursive) override;
Status MakeDirectory(
const std::string& dir, const bool recursive,
const bool allow_dir_exist) override;
Status MakeTemporaryDirectory(std::string* temp_dir) override;
Status DeletePath(const std::string& path) override;
};
Expand Down Expand Up @@ -248,22 +250,23 @@ LocalFileSystem::WriteBinaryFile(
}

Status
LocalFileSystem::MakeDirectory(const std::string& dir, const bool recursive)
LocalFileSystem::MakeDirectory(
const std::string& dir, const bool recursive, const bool allow_dir_exist)
{
#ifdef _WIN32
if (mkdir(dir.c_str()) == -1)
#else
if (mkdir(dir.c_str(), S_IRWXU) == -1)
#endif
{
// Return success if directory already exists
if (errno == EEXIST) {
// Return success if directory already exists and it is permitted
if (allow_dir_exist && errno == EEXIST) {
return Status::Success;
}
// In all other cases only allow the error due to parent directory
// does not exist, if 'recursive' is requested
if ((errno == ENOENT) && (!dir.empty()) && recursive) {
RETURN_IF_ERROR(MakeDirectory(DirName(dir), recursive));
RETURN_IF_ERROR(MakeDirectory(DirName(dir), recursive, allow_dir_exist));
// Retry the creation
#ifdef _WIN32
if (mkdir(dir.c_str()) == -1)
Expand Down
10 changes: 7 additions & 3 deletions src/filesystem/implementations/s3.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ class S3FileSystem : public FileSystem {
Status WriteBinaryFile(
const std::string& path, const char* contents,
const size_t content_len) override;
Status MakeDirectory(const std::string& dir, const bool recursive) override;
Status MakeDirectory(
const std::string& dir, const bool recursive,
const bool allow_dir_exist) override;
Status MakeTemporaryDirectory(std::string* temp_dir) override;
Status DeletePath(const std::string& path) override;

Expand Down Expand Up @@ -668,7 +670,8 @@ S3FileSystem::LocalizePath(
tmp_folder = mount_dir.empty() ? std::string(env_mount_dir) : mount_dir;
tmp_folder =
JoinPath({tmp_folder, path.substr(path.find_last_of('/') + 1)});
RETURN_IF_ERROR(triton::core::MakeDirectory(tmp_folder, true));
RETURN_IF_ERROR(triton::core::MakeDirectory(
tmp_folder, true /*recursive*/, true /*allow_dir_exist*/));
}

// Specify contents to be downloaded
Expand Down Expand Up @@ -780,7 +783,8 @@ S3FileSystem::WriteBinaryFile(
}

Status
S3FileSystem::MakeDirectory(const std::string& dir, const bool recursive)
S3FileSystem::MakeDirectory(
const std::string& dir, const bool recursive, const bool allow_dir_exist)
{
return Status(
Status::Code::UNSUPPORTED,
Expand Down