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

Report S3 operation name of specific request that failed. #377

Merged
merged 14 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions include/aws/s3/private/s3_default_meta_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ struct aws_s3_meta_request_default {

size_t content_length;

struct aws_string *operation_name;

/* Members to only be used when the mutex in the base type is locked. */
struct {
int cached_response_status;
Expand Down
12 changes: 10 additions & 2 deletions include/aws/s3/private/s3_meta_request_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,16 @@ struct aws_s3_meta_request_vtable {
/* Pause the given request */
int (*pause)(struct aws_s3_meta_request *meta_request, struct aws_s3_meta_request_resume_token **resume_token);

/* Get the type of the aws_s3_request */
int (*get_request_type)(const struct aws_s3_request *request);
/* Get the type of the aws_s3_request.
* Not implemented for DEFAULT type, which doesn't know its aws_s3_request_type enum value */
enum aws_s3_request_type (*get_request_type)(const struct aws_s3_request *request);

/* Get the S3 operation name for the meta-request.
* This is only implemented for DEFAULT type, which doesn't know its
* aws_s3_request_type enum value, but might know its operation name.
* Returns empty string if the name isn't known.
* Note that this C-string's lifetime is tied to the meta-request. */
const char *(*get_operation_name)(const struct aws_s3_meta_request *meta_request);
};

/**
Expand Down
14 changes: 13 additions & 1 deletion include/aws/s3/private/s3_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ struct aws_s3_request_metrics {
struct aws_string *host_address;
/* The the request ID header value. */
struct aws_string *request_id;
/* S3 operation name for the request (NULL if unknown) */
struct aws_string *operation_name;
/* The type of request made */
enum aws_s3_request_type request_type;
} req_resp_info_metrics;
Expand Down Expand Up @@ -242,10 +244,20 @@ struct aws_s3_request *aws_s3_request_acquire(struct aws_s3_request *request);
AWS_S3_API
struct aws_s3_request *aws_s3_request_release(struct aws_s3_request *request);

/* Return type enum, if it is known. Otherwise AWS_S3_REQUEST_TYPE_MAX is returned. */
AWS_S3_API
enum aws_s3_request_type aws_s3_request_get_type(const struct aws_s3_request *request);

/* Return S3 operation name, if it is known. Otherwise an empty string is returned.
* Note that this C-string's lifetime is tied to the aws_s3_request. */
AWS_S3_API
const char *aws_s3_request_get_operation_name(const struct aws_s3_request *request);

AWS_S3_API
struct aws_s3_request_metrics *aws_s3_request_metrics_new(
struct aws_allocator *allocator,
struct aws_http_message *message);
const struct aws_s3_request *request,
const struct aws_http_message *message);

AWS_EXTERN_C_END

Expand Down
50 changes: 48 additions & 2 deletions include/aws/s3/s3_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ enum aws_s3_meta_request_type {

/**
* The type of S3 request made. Used by metrics.
* aws_s3_request_type_operation_name() returns the S3 operation name
* for types that map (e.g. AWS_S3_REQUEST_TYPE_HEAD_OBJECT -> "HeadObject"),
* or empty string for types that don't map (e.g. AWS_S3_REQUEST_TYPE_DEFAULT -> "").
*/
enum aws_s3_request_type {
/* Same as the original HTTP request passed to aws_s3_meta_request_options */
Expand All @@ -100,6 +103,7 @@ enum aws_s3_request_type {
AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD,
AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD,
AWS_S3_REQUEST_TYPE_UPLOAD_PART_COPY,
AWS_S3_REQUEST_TYPE_COPY_OBJECT,

AWS_S3_REQUEST_TYPE_MAX,
};
Expand Down Expand Up @@ -481,6 +485,15 @@ struct aws_s3_meta_request_options {
/* The type of meta request we will be trying to accelerate. */
enum aws_s3_meta_request_type type;

/**
* Optional.
* The S3 operation name (e.g. "CreateBucket").
* You should only set this when type is AWS_S3_META_REQUEST_TYPE_DEFAULT;
graebm marked this conversation as resolved.
Show resolved Hide resolved
* it is automatically populated for other meta-request types.
* This name is used to fill out details in metrics and error reports.
*/
struct aws_byte_cursor operation_name;
graebm marked this conversation as resolved.
Show resolved Hide resolved

/* Signing options to be used for each request created for this meta request. If NULL, options in the client will
* be used. If not NULL, these options will override the client options. */
const struct aws_signing_config_aws *signing_config;
Expand Down Expand Up @@ -602,12 +615,23 @@ struct aws_s3_meta_request_options {
*/
struct aws_s3_meta_request_result {

/* HTTP Headers for the failed request that triggered finish of the meta request. NULL if no request failed. */
/* If meta request failed due to an HTTP error response from S3, these are the headers.
* NULL if meta request failed for another reason. */
struct aws_http_headers *error_response_headers;

/* Response body for the failed request that triggered finishing of the meta request. NULL if no request failed.*/
/* If meta request failed due to an HTTP error response from S3, this the body.
* NULL if meta request failed for another reason, or if the response had no body (such as a HEAD response). */
struct aws_byte_buf *error_response_body;

/* If meta request failed due to an HTTP error response from S3,
* this is the name of the S3 operation it was responding to.
* For example, if a AWS_S3_META_REQUEST_TYPE_PUT_OBJECT fails, this could be
* "CreateMultipartUpload", "UploadPart", "CompleteMultipartUpload", or others.
* For AWS_S3_META_REQUEST_TYPE_DEFAULT, this is the same value passed to
* aws_s3_meta_request_options.operation_name.
* NULL if the meta request failed for another reason, or the operation name is not known. */
struct aws_string *error_response_operation_name;

/* Response status of the failed request or of the entire meta request. */
int response_status;

Expand Down Expand Up @@ -823,6 +847,17 @@ void aws_s3_init_default_signing_config(
const struct aws_byte_cursor region,
struct aws_credentials_provider *credentials_provider);

/**
* Return operation name for aws_s3_request_type,
* or empty string if the type doesn't map to an actual operation.
* For example:
* AWS_S3_REQUEST_TYPE_HEAD_OBJECT -> "HeadObject"
* AWS_S3_REQUEST_TYPE_DEFAULT -> ""
* AWS_S3_REQUEST_TYPE_MAX -> ""
*/
AWS_S3_API
const char *aws_s3_request_type_operation_name(enum aws_s3_request_type type);

/**
* Add a reference, keeping this object alive.
* The reference must be released when you are done with it, or it's memory will never be cleaned up.
Expand Down Expand Up @@ -974,6 +1009,17 @@ int aws_s3_request_metrics_get_thread_id(const struct aws_s3_request_metrics *me
AWS_S3_API
int aws_s3_request_metrics_get_request_stream_id(const struct aws_s3_request_metrics *metrics, uint32_t *out_stream_id);

/**
* Get the S3 operation name of the request (e.g. "HeadObject").
* If unavailable, AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised.
* If available, out_operation_name will be set to a string.
* Be warned this string's lifetime is tied to the metrics object.
*/
AWS_S3_API
int aws_s3_request_metrics_get_operation_name(
const struct aws_s3_request_metrics *metrics,
const struct aws_string **out_operation_name);

/* Get the request type from request metrics. */
AWS_S3_API
void aws_s3_request_metrics_get_request_type(
Copy link
Member

Choose a reason for hiding this comment

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

IIRC we added this function and all the aws_s3_request_type stuff specifically for Mountpoint for use in metrics. If Mountpoint is still the only customer for it and you feel like you want to break it, it would be really easy for us to switch to this new interface instead, since we really only needed it to get operation names anyway.

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'll keep the enums. I'm finding uses for both enum and string

Expand Down
4 changes: 2 additions & 2 deletions source/s3_auto_ranged_get.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static void s_s3_auto_ranged_get_request_finished(
struct aws_s3_request *request,
int error_code);

static int s_s3_auto_ranged_get_request_type(const struct aws_s3_request *request);
static enum aws_s3_request_type s_s3_auto_ranged_get_request_type(const struct aws_s3_request *request);

static struct aws_s3_meta_request_vtable s_s3_auto_ranged_get_vtable = {
.update = s_s3_auto_ranged_get_update,
Expand All @@ -42,7 +42,7 @@ static struct aws_s3_meta_request_vtable s_s3_auto_ranged_get_vtable = {
.get_request_type = s_s3_auto_ranged_get_request_type,
};

static int s_s3_auto_ranged_get_request_type(const struct aws_s3_request *request) {
static enum aws_s3_request_type s_s3_auto_ranged_get_request_type(const struct aws_s3_request *request) {
switch (request->request_tag) {
case AWS_S3_AUTO_RANGE_GET_REQUEST_TYPE_HEAD_OBJECT:
return AWS_S3_REQUEST_TYPE_HEAD_OBJECT;
Expand Down
2 changes: 1 addition & 1 deletion source/s3_auto_ranged_put.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ static int s_try_init_resume_state_from_persisted_data(
return AWS_OP_SUCCESS;
}

static int s_s3_auto_ranged_put_request_type(const struct aws_s3_request *request) {
static enum aws_s3_request_type s_s3_auto_ranged_put_request_type(const struct aws_s3_request *request) {
switch (request->request_tag) {
case AWS_S3_AUTO_RANGED_PUT_REQUEST_TAG_LIST_PARTS:
return AWS_S3_REQUEST_TYPE_LIST_PARTS;
Expand Down
5 changes: 2 additions & 3 deletions source/s3_copy_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,13 @@ static void s_s3_copy_object_request_finished(
struct aws_s3_request *request,
int error_code);

static int s_s3_copy_object_request_type(const struct aws_s3_request *request) {
static enum aws_s3_request_type s_s3_copy_object_request_type(const struct aws_s3_request *request) {
switch (request->request_tag) {
case AWS_S3_COPY_OBJECT_REQUEST_TAG_GET_OBJECT_SIZE:
/* It's a HEAD request of GetObject call */
return AWS_S3_REQUEST_TYPE_HEAD_OBJECT;
case AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS:
/* A single copy object request, same as default */
return AWS_S3_REQUEST_TYPE_DEFAULT;
return AWS_S3_REQUEST_TYPE_COPY_OBJECT;
TingDaoK marked this conversation as resolved.
Show resolved Hide resolved
case AWS_S3_COPY_OBJECT_REQUEST_TAG_CREATE_MULTIPART_UPLOAD:
return AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD;
case AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY:
Expand Down
14 changes: 14 additions & 0 deletions source/s3_default_meta_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ static void s_s3_meta_request_default_request_finished(
struct aws_s3_request *request,
int error_code);

static const char *s_s3_meta_request_default_operation_name(const struct aws_s3_meta_request *meta_request);

static struct aws_s3_meta_request_vtable s_s3_meta_request_default_vtable = {
.update = s_s3_meta_request_default_update,
.send_request_finish = aws_s3_meta_request_send_request_finish_default,
Expand All @@ -45,6 +47,7 @@ static struct aws_s3_meta_request_vtable s_s3_meta_request_default_vtable = {
.finished_request = s_s3_meta_request_default_request_finished,
.destroy = s_s3_meta_request_default_destroy,
.finish = aws_s3_meta_request_finish_default,
.get_operation_name = s_s3_meta_request_default_operation_name,
};

/* Allocate a new default meta request. */
Expand Down Expand Up @@ -104,6 +107,10 @@ struct aws_s3_meta_request *aws_s3_meta_request_default_new(

meta_request_default->content_length = (size_t)content_length;

if (options->operation_name.len != 0) {
meta_request_default->operation_name = aws_string_new_from_cursor(allocator, &options->operation_name);
}

AWS_LOGF_DEBUG(AWS_LS_S3_META_REQUEST, "id=%p Created new Default Meta Request.", (void *)meta_request_default);

return &meta_request_default->base;
Expand All @@ -114,6 +121,7 @@ static void s_s3_meta_request_default_destroy(struct aws_s3_meta_request *meta_r
AWS_PRECONDITION(meta_request->impl);

struct aws_s3_meta_request_default *meta_request_default = meta_request->impl;
aws_string_destroy(meta_request_default->operation_name);
aws_mem_release(meta_request->allocator, meta_request_default);
}

Expand Down Expand Up @@ -399,3 +407,9 @@ static void s_s3_meta_request_default_request_finished(
}
/* END CRITICAL SECTION */
}

static const char *s_s3_meta_request_default_operation_name(const struct aws_s3_meta_request *meta_request) {
struct aws_s3_meta_request_default *meta_request_default = meta_request->impl;
const struct aws_string *operation_name = meta_request_default->operation_name;
return operation_name ? aws_string_c_str(operation_name) : "";
}
7 changes: 7 additions & 0 deletions source/s3_meta_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -1811,6 +1811,11 @@ void aws_s3_meta_request_result_setup(
aws_byte_buf_init_copy(
result->error_response_body, meta_request->allocator, &failed_request->send_data.response_body);
}

const char *operation_name = aws_s3_request_get_operation_name(failed_request);
if (operation_name[0] != '\0') {
result->error_response_operation_name = aws_string_new_from_c_str(meta_request->allocator, operation_name);
}
}

result->response_status = response_status;
Expand All @@ -1830,6 +1835,8 @@ void aws_s3_meta_request_result_clean_up(
aws_mem_release(meta_request->allocator, result->error_response_body);
}

aws_string_destroy(result->error_response_operation_name);

AWS_ZERO_STRUCT(*result);
}

Expand Down
86 changes: 77 additions & 9 deletions source/s3_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,7 @@ void aws_s3_request_setup_send_data(struct aws_s3_request *request, struct aws_h
aws_s3_request_clean_up_send_data(request);

request->send_data.message = message;
struct aws_s3_meta_request *meta_request = request->meta_request;
request->send_data.metrics = aws_s3_request_metrics_new(request->allocator, message);
if (!meta_request->vtable->get_request_type) {
request->send_data.metrics->req_resp_info_metrics.request_type = AWS_S3_REQUEST_TYPE_DEFAULT;
} else {
request->send_data.metrics->req_resp_info_metrics.request_type =
meta_request->vtable->get_request_type(request);
}
request->send_data.metrics = aws_s3_request_metrics_new(request->allocator, request, message);
/* Start the timestamp */
aws_high_res_clock_get_ticks((uint64_t *)&request->send_data.metrics->time_metrics.start_timestamp_ns);

Expand Down Expand Up @@ -128,6 +121,59 @@ static void s_s3_request_destroy(void *user_data) {

aws_mem_release(request->allocator, request);
}
enum aws_s3_request_type aws_s3_request_get_type(const struct aws_s3_request *request) {
if (!request->meta_request->vtable->get_request_type) {
return AWS_S3_REQUEST_TYPE_DEFAULT;
} else {
return request->meta_request->vtable->get_request_type(request);
}
}

const char *aws_s3_request_get_operation_name(const struct aws_s3_request *request) {
/* Try to get name from type */
enum aws_s3_request_type request_type = aws_s3_request_get_type(request);
const char *operation_name = aws_s3_request_type_operation_name(request_type);
if (operation_name[0] != '\0') {
return operation_name;
}

/* Try to get name from meta-request */
const struct aws_s3_meta_request *meta_request = request->meta_request;
if (meta_request->vtable->get_operation_name != NULL) {
operation_name = meta_request->vtable->get_operation_name(meta_request);
if (operation_name[0] != '\0') {
return operation_name;
}
}

/* Name unknown */
return "";
}

const char *aws_s3_request_type_operation_name(enum aws_s3_request_type type) {
switch (type) {
case AWS_S3_REQUEST_TYPE_HEAD_OBJECT:
return "HeadObject";
case AWS_S3_REQUEST_TYPE_GET_OBJECT:
return "GetObject";
case AWS_S3_REQUEST_TYPE_LIST_PARTS:
return "ListParts";
case AWS_S3_REQUEST_TYPE_CREATE_MULTIPART_UPLOAD:
return "CreateMultipartUpload";
case AWS_S3_REQUEST_TYPE_UPLOAD_PART:
return "UploadPart";
case AWS_S3_REQUEST_TYPE_ABORT_MULTIPART_UPLOAD:
return "AbortMultipartUpload";
case AWS_S3_REQUEST_TYPE_COMPLETE_MULTIPART_UPLOAD:
return "CompleteMultipartUpload";
case AWS_S3_REQUEST_TYPE_UPLOAD_PART_COPY:
return "UploadPartCopy";
case AWS_S3_REQUEST_TYPE_COPY_OBJECT:
return "CopyObject";
default:
return "";
}
}

static void s_s3_request_metrics_destroy(void *arg) {
struct aws_s3_request_metrics *metrics = arg;
Expand All @@ -138,14 +184,17 @@ static void s_s3_request_metrics_destroy(void *arg) {
aws_string_destroy(metrics->req_resp_info_metrics.request_path_query);
aws_string_destroy(metrics->req_resp_info_metrics.host_address);
aws_string_destroy(metrics->req_resp_info_metrics.request_id);
aws_string_destroy(metrics->req_resp_info_metrics.operation_name);
aws_string_destroy(metrics->crt_info_metrics.ip_address);

aws_mem_release(metrics->allocator, metrics);
}

struct aws_s3_request_metrics *aws_s3_request_metrics_new(
struct aws_allocator *allocator,
struct aws_http_message *message) {
const struct aws_s3_request *request,
const struct aws_http_message *message) {

struct aws_s3_request_metrics *metrics = aws_mem_calloc(allocator, 1, sizeof(struct aws_s3_request_metrics));
metrics->allocator = allocator;
struct aws_byte_cursor out_path;
Expand All @@ -166,6 +215,13 @@ struct aws_s3_request_metrics *aws_s3_request_metrics_new(
metrics->req_resp_info_metrics.host_address = aws_string_new_from_cursor(allocator, &host_header_value);
AWS_ASSERT(metrics->req_resp_info_metrics.host_address != NULL);

metrics->req_resp_info_metrics.request_type = aws_s3_request_get_type(request);

const char *operation_name = aws_s3_request_get_operation_name(request);
if (operation_name[0] != '\0') {
metrics->req_resp_info_metrics.operation_name = aws_string_new_from_c_str(allocator, operation_name);
}

metrics->time_metrics.start_timestamp_ns = -1;
metrics->time_metrics.end_timestamp_ns = -1;
metrics->time_metrics.total_duration_ns = -1;
Expand Down Expand Up @@ -384,6 +440,18 @@ int aws_s3_request_metrics_get_request_stream_id(const struct aws_s3_request_met
return AWS_OP_SUCCESS;
}

int aws_s3_request_metrics_get_operation_name(
const struct aws_s3_request_metrics *metrics,
const struct aws_string **out_operation_name) {
AWS_PRECONDITION(metrics);
AWS_PRECONDITION(out_operation_name);
if (metrics->req_resp_info_metrics.operation_name == NULL) {
return aws_raise_error(AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE);
}
*out_operation_name = metrics->req_resp_info_metrics.operation_name;
return AWS_OP_SUCCESS;
}

void aws_s3_request_metrics_get_request_type(
const struct aws_s3_request_metrics *metrics,
enum aws_s3_request_type *out_request_type) {
Expand Down
Loading