Skip to content

Commit

Permalink
Report S3 operation name of specific request that failed.
Browse files Browse the repository at this point in the history
  • Loading branch information
graebm committed Nov 21, 2023
1 parent f961971 commit aab5101
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 20 deletions.
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;
* 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;

/* 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(
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;
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
13 changes: 13 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 Down Expand Up @@ -399,3 +406,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

0 comments on commit aab5101

Please sign in to comment.