-
Notifications
You must be signed in to change notification settings - Fork 287
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
AWS S3 cache miss should not issue warning #1404
AWS S3 cache miss should not issue warning #1404
Conversation
@microsoft-github-policy-service agree company="Formlabs" |
src/vcpkg/binarycaching.cpp
Outdated
@@ -951,14 +951,22 @@ namespace | |||
static constexpr StringLiteral m_accept_header = "Accept: application/json;api-version=6.0-preview.1"; | |||
}; | |||
|
|||
enum class CacheResult |
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.
Can this use RestoreResult
instead of making up a new value?
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.
Absolutely, I just didn't find that enum.
src/vcpkg/binarycaching.cpp
Outdated
struct IObjectStorageTool | ||
{ | ||
virtual ~IObjectStorageTool() = default; | ||
|
||
virtual LocalizedString restored_message(size_t count, | ||
std::chrono::high_resolution_clock::duration elapsed) const = 0; | ||
virtual ExpectedL<Unit> stat(StringView url) const = 0; | ||
virtual ExpectedL<Unit> download_file(StringView object, const Path& archive) const = 0; | ||
virtual ExpectedL<CacheResult> stat(StringView url) const = 0; |
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.
Should stat
return CacheAvailability
instead?
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.
Do you mean ExpectedL<CacheAvailability>
, or plain CacheAvailability
? In either case, how should we treat unknown
in the call sites?
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.
OK, I've found your suggestion for precheck()
, but it's still unclear to me how to handle it in AwsStorageTool::download_file()
. Should I simply return RestoreResult:: unavailable
?
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.
Do you mean
ExpectedL<CacheAvailability>
, or plainCacheAvailability
? In either case, how should we treatunknown
in the call sites?
Sorry, I meant ExpectedL<CacheAvailability>
:)
src/vcpkg/binarycaching.cpp
Outdated
@@ -990,11 +998,11 @@ namespace | |||
const auto& abi = action.package_abi().value_or_exit(VCPKG_LINE_INFO); | |||
auto tmp = make_temp_archive_path(m_buildtrees, action.spec); | |||
auto res = m_tool->download_file(make_object_path(m_prefix, abi), tmp); | |||
if (res) | |||
if (res.value_or(CacheResult::miss) == CacheResult::hit) |
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.
I think this value_or
is less clear than just spelling it out:
if (auto cache_result = res.get()) {
if (*cache_result == CacheResult::hit) {
out_zip_paths[idx].emplace(std::move(tmp), RemoveWhen::always);
return;
}
} else {
out_sink.println_warning(res.error());
}
src/vcpkg/binarycaching.cpp
Outdated
@@ -1007,7 +1015,8 @@ namespace | |||
{ | |||
auto&& action = *actions[idx]; | |||
const auto& abi = action.package_abi().value_or_exit(VCPKG_LINE_INFO); | |||
if (m_tool->stat(make_object_path(m_prefix, abi))) | |||
auto res = m_tool->stat(make_object_path(m_prefix, abi)); |
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.
If stat returns CacheAvailability
then this can be:
auto maybe_res = m_tool->stat(make_object_path(m_prefix, abi));
if (auto res = maybe_res.get()) {
cache_status[idx] = *res;
}
src/vcpkg/binarycaching.cpp
Outdated
cmd_execute_and_capture_output(Command{m_tool}.string_arg("-q").string_arg("stat").string_arg(url)), | ||
Tools::GSUTIL); | ||
auto cmd = Command{m_tool}.string_arg("-q").string_arg("stat").string_arg(url); | ||
return flatten(cmd_execute_and_capture_output(cmd), Tools::GSUTIL).map(unit_to_cache_hit); |
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.
Is this a case where we should be looking at the output to confirm we are in the case we expect?
ExpectedL<CacheResult> stat_flatten_pseudocode(const ExpectedL<ExitCodeAndOutput>& maybe_exit, StringView tool_name)
{
if (auto exit = maybe_exit.get())
{
if (exit->exit_code == 0)
{
return {CacheResult::hit};
}
if (exit->exit_code == 1 && exit->output.empty())
{
return {CacheResult::miss};
}
return {msg::format_error(
msgProgramReturnedNonzeroExitCode, msg::tool_name = tool_name, msg::exit_code = exit->exit_code)
.append_raw('\n')
.append_raw(exit->output)};
}
return {msg::format_error(msgLaunchingProgramFailed, msg::tool_name = tool_name)
.append_raw(' ')
.append_raw(maybe_exit.error().to_string())};
}
(this would of course remove the need for unit_to_cache_hit
)
I'm a bit nervous to treat all potentially failing exit codes as cache miss. For example, if there's an authentication failure, that probably prints something about what happened or similar, and probably also returns EXIT_FAILURE
. But with this PR's suggestion that cache provider would be silently broken.
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.
I think you might've misread my code. I also wouldn't want to treat all nonzero exit codes as cache misses, that's why I only changed the behaviour in the AWS provider, and only in case the output is empty, and the exit code is 0 or 1.
This specifically is the GCS provider, for which I tried to keep the original behaviour: it's either a cache hit, or an error with an error message. flatten()
treats every nonzero exit code as an error, and returns a "right-filled" Expected
with an error message, which I return as-is. Zero exit codes result in a Unit
, which I treat as a hit like before. This function cannot return a cache miss at all, because I don't know what the right condition for that is in GCS caches.
Your function is almost equivalent to my AWS stat version, except that I treat "0 exit code, but empty stdout" as a cache miss, to handle if AWS maintainers decide to change aws ls
behaviour to not return an error when there are no hits. However, I don't know how GCS reacts in the "not found" case, so I didn't want to change the behaviour from what it was before.
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.
I see. Yes, I was reviewing what this was trying to do based on what your PR description said and I didn't notice that this wasn't the AWS one. (Or if I did notice, I didn't internalize the difference...)
GitHub only showing like 2 lines of context == review pain :(
src/vcpkg/binarycaching.cpp
Outdated
// When the file is not found, "aws s3 ls" prints nothing, and returns exit code 1. | ||
// flatten() would treat this as an error, but we want to treat it as a (silent) cache miss instead. | ||
// See https://github.com/aws/aws-cli/issues/5544 for the related aws-cli bug report. | ||
if (auto exit = maybe_exit.get()) | ||
{ | ||
// We want to return CacheResult::miss even if aws-cli starts to return success | ||
const bool success_or_exit_code_1 = exit->exit_code == 0 || exit->exit_code == 1; | ||
const bool output_is_empty = Strings::trim(exit->output) == ""; | ||
if (success_or_exit_code_1 && output_is_empty) | ||
{ | ||
return CacheResult::miss; | ||
} | ||
} |
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.
Ditto stat_flatten_pseudocode
above
Thanks for the fix! |
Thanks for your detailed review! I'll address your comments in a couple days. I added some questions in replies where something was unclear to me. |
@BillyONeal |
There isn't widespread surgery here so I think you can do whatever you want :) |
c3c6df5
to
d4b3097
Compare
@BillyONeal: I've updated my branch. I split the changes into two commits:
If you have a better name for |
src/vcpkg/binarycaching.cpp
Outdated
auto maybe_res = m_tool->stat(make_object_path(m_prefix, abi)); | ||
if (auto res = maybe_res.get()) | ||
{ | ||
cache_status[idx] = CacheAvailability::unavailable; | ||
cache_status[idx] = *res; | ||
} |
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.
Just realized that this is not an equivalent transformation: if stat()
returns an error, the original version sets cache_status[idx]
to CacheAvailability::unavailable
, while the new version leaves it as CacheAvailability::unknown
. Should I add an else
branch to keep the behaviour the same as before? (My understanding is that leaving it unknown
may at worst cause an extra query to the provider, but I'm not 100% sure I'm reading the rest of the code right.)
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.
GitHub's not showing the diff properly.
Original:
if (m_tool->stat(make_object_path(m_prefix, abi)))
{
cache_status[idx] = CacheAvailability::available;
}
else
{
cache_status[idx] = CacheAvailability::unavailable;
}
Current:
auto maybe_res = m_tool->stat(make_object_path(m_prefix, abi));
if (auto res = maybe_res.get())
{
cache_status[idx] = *res;
}
Possible fix to keep old behaviour:
auto maybe_res = m_tool->stat(make_object_path(m_prefix, abi));
if (auto res = maybe_res.get())
{
cache_status[idx] = *res;
}
else
{
cache_status[idx] = CacheAvailability::unavailable;
}
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.
I decided to go with the real equivalent change (i.e. I readded the else
branch), and force-pushed.
This commit does not change any behavior, just prepares the way for the next commit.
d4b3097
to
80dcf3f
Compare
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.
This is great, thank you!
This fixes microsoft/vcpkg#31932
Previously, when using the AWS S3 binary cache, every cache miss triggered the following message:
This was both noisy and misleading (because it was not an actual error).
Now AWS cache misses are treated silently, the same way as cache misses from the local cache are.
Note
I don't have access to either Google Cloud Storage or Tencent Cloud Object Storage sources, so I don't know whether the same kind of issue happens there too; in any case, fixing those should be easy using the newly introduced interface. (Their
stat()
anddownload_file()
methods need to be adapted to returnCacheResult::miss
in the relevant cases.)