Skip to content
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

cst/inv: locate latest report #16962

Merged
merged 7 commits into from
Mar 14, 2024

Conversation

abhijat
Copy link
Contributor

@abhijat abhijat commented Mar 8, 2024

Adds a utility to scan through all the dates available for a given inventory config and find the latest report which has been generated and is ready for download.

Returns the path to manifest describing the report found as well as report paths and the datetime.

The caller can then use the metadata to download the actual report.

Backports Required

  • none - not a bug fix
  • none - this is a backport
  • none - issue does not exist in previous branches
  • none - papercut/not impactful enough to backport
  • v23.3.x
  • v23.2.x

Release Notes

  • none

An object-exists method is extracted from the segment-exists method, and
the segment existence check is made to reuse the new method. The new
method will be used in the next couple of changes.
@mergify mergify bot mentioned this pull request Mar 8, 2024
6 tasks
@vbotbuildovich
Copy link
Collaborator

vbotbuildovich commented Mar 8, 2024

new failures in https://buildkite.com/redpanda/redpanda/builds/45839#018e1bf6-0767-458b-a486-baed655953d9:

"rptest.tests.consumer_group_balancing_test.ConsumerGroupBalancingTest.test_coordinator_nodes_balance"

new failures in https://buildkite.com/redpanda/redpanda/builds/46107#018e37ac-1422-4a69-a397-aa0b1722f6c1:

"rptest.tests.data_transforms_test.DataTransformsLeadershipChangingTest.test_leadership_changing_randomly"

list-object and object-exists methods are moved up to the
cloud_storage_api interface, to ease testing in the next set of changes.
@abhijat abhijat force-pushed the feat/inv-scrub/locate-reports branch 5 times, most recently from 35d45a4 to fa41608 Compare March 8, 2024 14:43
@abhijat
Copy link
Contributor Author

abhijat commented Mar 8, 2024

Note for review: the core of the changes is in https://github.com/redpanda-data/redpanda/blob/fa416080b77b3a1add63bb75bd374b503aa6c1e1/src/v/cloud_storage/inventory/aws_ops.cc where a new method is added latest_report_metadata

The rest of the changes are unit tests and the supporting types.

@abhijat abhijat force-pushed the feat/inv-scrub/locate-reports branch from fa41608 to 2a94838 Compare March 11, 2024 09:14
@abhijat
Copy link
Contributor Author

abhijat commented Mar 11, 2024

CI failure (gtest raft timeout) #14425

@dotnwat dotnwat self-requested a review March 11, 2024 15:54
Lazin
Lazin previously approved these changes Mar 12, 2024
Copy link
Contributor

@Lazin Lazin left a comment

Choose a reason for hiding this comment

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

LGTM overall
I think that the PR can be improved in two ways:

  • error handling
  • some extra logging in aws_ops.cc

@@ -547,4 +547,14 @@ void transfer_details::on_backoff(remote_probe& probe) {
run_callback(backoff_cb, probe);
}

std::ostream& operator<<(std::ostream& os, existence_check_type head) {
switch (head) {
using enum cloud_storage::existence_check_type;
Copy link
Contributor

Choose a reason for hiding this comment

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

nice trick

// inventory is ready, so we probe the checksum files from latest to
// earliest. We stop at the first checksum we find.
for (const auto& path_with_datetime : checksum_paths) {
if (const auto result = co_await remote.object_exists(
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have to probe checksum objects?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a pattern that all three cloud vendors follow that the checksum file for a report is written to bucket last, so the checksum file being present guarantees that the actual report and its manifest are ready for download.

@@ -111,4 +146,120 @@ ss::future<bool> aws_ops::inventory_configuration_exists(
co_return dl_res == download_result::success;
}

ss::future<result<report_metadata, error_outcome>>
aws_ops::latest_report_metadata(
cloud_storage::cloud_storage_api& remote, retry_chain_node& parent_rtc) {
Copy link
Contributor

Choose a reason for hiding this comment

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

could this method be noexcept?
it returns a result which can contain an error but it can also throw an exception which makes error handling more complicated because you have to check the result and catch potential exceptions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point, I will try it out

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added for this and the other method

src/v/cloud_storage/inventory/aws_ops.cc Show resolved Hide resolved
@Lazin
Copy link
Contributor

Lazin commented Mar 12, 2024

Maybe I also missed something or it's planned for later PR's but it'd be great to add a metric just to see if the feature is working and how much work is done using the inventory and with probing.

cloud_storage::cloud_storage_api& remote, retry_chain_node& parent_rtc) {
// The prefix is generated according to
// https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-inventory-location.html
const auto prefix = cloud_storage_clients::object_key{
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: maybe call this full_prefix or something to differentiate it from _prefix?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

return checksum_path(prefix().native(), datetime);
};

// The prefix may contain non-datetime folders such as hive data, which we
Copy link
Contributor

Choose a reason for hiding this comment

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

Wow, cool that S3's inventory natively supports Apache Hive as a first-class citizen

Comment on lines +244 to +307
const auto doc = parse_json_response(std::move(json_response));
if (doc.HasParseError()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: here may not be the best place, but it would be great for debugging if somewhere in the error path, we logged the bad json (maybe truncated, in case it's huge)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

using report_datetime = named_type<ss::sstring, struct report_datetime_t>;

// Stores a path to one or more reports. While in most cases one report run will
// generate just one CSV file, in some cases (e.g. Google with more than 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this refer to the manifest.json example here https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-inventory-location.html ? It's curious that the manifest's files field is an array if it only expects to generate one file. (not really anything to do about it, just seems a bit odd)

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 think this is a forward compatibility thing, they might be prepping for sharding the results into different files like google does. Our implementation is expected to always work with a list of files.

inv_ops::maybe_create_inventory_configuration(
cloud_storage_api& remote, retry_chain_node& parent) {
if (const auto exists = co_await inventory_configuration_exists(
remote, parent);
exists) {
exists.has_value() && exists.value()) {
co_return inventory_creation_result::already_exists;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

If the result contains an error, should we propagate that error up instead of creating a new one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this specific line there is no error as the config exists, so we return a success value. In general though, in earlier PRs we propagated the http client errors up to the caller, I initially thought of somehow mapping http client errors to values in the new error_outcome enum that we are using for inventory module, but I decided against it as the caller does not care about the http error type in this case, just that something has failed.

We need logging of the underlying error here which is missing right now, once logs are added here (WIP) I think this should be sufficient for troubleshooting.

However if you think returning the http client errors is clearer then I can give mapping the error outcomes another try.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think progragating the exact http error is necessarily important (especially if you're adding some logging), but at least it seems like inventory_configuration_exists() can return failed_to_check_inv_status which seems like it should be bubbled up? As is it looks like we proceed to creation. I suppose it's likely that whatever error was seen in checking existence will also be seen below, but still seems like surprising behavior

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 see what you mean now. Yeah that needs to be fixed, will change this now. Thanks for pointing out.

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 the structure and added error logging

path_with_datetime.path,
parent_rtc,
existence_check_type::object);
result == download_result::success) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's a bit surprising to proceed in the event this result is an error. Wouldn't that mean we'd proceed with a knowingly stale inventory file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My assumption was that if there is an error, it would be not_found because the report is still being prepared.

Additionally the caller (WIP PR) is going to compare the returned metadata's datetime with whatever data the caller has last consumed, so it will only every consume newer reports than whatever it last saw, but there is still a problem here.

If we are encountering a non-404 error even after retries built into remote, then we will probably encounter the same errors for all reports and should probably bail out at the first non 404 error instead of progressively trying older reports.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, thanks for explaining. Would you mind documenting that assumption in a comment, and/or make sure that if the assumption isn't true that we'll still do something reasonable? For instance, it doesn't necessarily seem impossible for some calls to get unlucky and time out, while subsequent calls succeed.

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've made the code path more explicit in the latest push, now if we fail with 404, we try the next latest report. In all other failure cases we abort and the caller can retry later.

@abhijat abhijat force-pushed the feat/inv-scrub/locate-reports branch 5 times, most recently from 6de8bdf to bc602ac Compare March 13, 2024 08:53
A new method is added which performs the following actions:

* scan the bucket for inventory reports
* find the latest report which is ready for download
* download the manifest for report and extract paths to report files

Finally metadata for the report is returned which contains the path to
manifest, paths to actual reports and the date-time as an ISO-8601
formatted string.

This metadata can be used by the caller to determine if it wants the
report (i.e. the date-time is newer than last consumed report by caller)
and to actually download the report files.
@abhijat abhijat force-pushed the feat/inv-scrub/locate-reports branch from bc602ac to 1da4b1c Compare March 13, 2024 08:58
@abhijat
Copy link
Contributor Author

abhijat commented Mar 13, 2024

/ci-repeat

@abhijat abhijat merged commit fd49299 into redpanda-data:dev Mar 14, 2024
16 checks passed
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.

None yet

4 participants