Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f2e749c
telemetry API
TingDaoK Mar 29, 2023
7bc0be0
ops, wrong structure
TingDaoK Mar 29, 2023
7e2bc99
change the structure to private and init
TingDaoK Mar 31, 2023
28cc168
unused variable
TingDaoK Mar 31, 2023
40514cd
WIP
TingDaoK Apr 11, 2023
2ab446c
impl the telemetry stuff
TingDaoK Apr 12, 2023
67a2121
fix compile warning
TingDaoK Apr 12, 2023
aee2407
add get thread id
TingDaoK Apr 12, 2023
ead9746
Merge branch 'main' into telemetry-api
TingDaoK Apr 13, 2023
e5c27e1
add more tests
TingDaoK Apr 13, 2023
c8191e4
Merge branch 'telemetry-api' of github.com:awslabs/aws-c-s3 into tele…
TingDaoK Apr 13, 2023
ba89d2d
add part of the doc and change timestamp to use aws_high_res_clock_ge…
TingDaoK Apr 20, 2023
9ab302b
Merge branch 'main' into telemetry-api
TingDaoK Apr 20, 2023
03fe52e
update document
TingDaoK Apr 24, 2023
8b226e8
Merge branch 'main' into telemetry-api
TingDaoK Apr 26, 2023
09fc4cf
use the new `aws_http_connection_get_remote_endpoint`
TingDaoK Apr 26, 2023
3eb0a8a
Merge branch 'telemetry-api' of github.com:awslabs/aws-c-s3 into tele…
TingDaoK Apr 26, 2023
a13d45b
adapt change from http and more test
TingDaoK Apr 28, 2023
6a75795
address comments
TingDaoK Apr 28, 2023
8f4fdf5
address comments
TingDaoK May 1, 2023
d3abc40
Apply suggestions from code review
TingDaoK May 1, 2023
4d9702f
forget to update the test
TingDaoK May 1, 2023
616172b
Merge branch 'telemetry-api' of github.com:awslabs/aws-c-s3 into tele…
TingDaoK May 1, 2023
70c568d
update the public interface of resume token as well
TingDaoK May 1, 2023
af68680
Revert "update the public interface of resume token as well"
TingDaoK May 1, 2023
3a083b0
Apply suggestions from code review
TingDaoK May 1, 2023
14c986e
add host address for the metric
TingDaoK May 1, 2023
bcb109c
Merge branch 'main' into telemetry-api
TingDaoK May 2, 2023
69b77f0
Merge branch 'main' into telemetry-api
TingDaoK May 2, 2023
665d597
address comments
TingDaoK May 3, 2023
490ad0e
Merge branch 'telemetry-api' of github.com:awslabs/aws-c-s3 into tele…
TingDaoK May 3, 2023
7c0f17b
pointer of pointer
TingDaoK May 3, 2023
f764da2
Merge branch 'main' into telemetry-api
TingDaoK May 3, 2023
862c670
add readme for account settings
TingDaoK May 3, 2023
56a71c1
Merge branch 'telemetry-api' of github.com:awslabs/aws-c-s3 into tele…
TingDaoK May 3, 2023
4569bbf
Merge branch 'main' into telemetry-api
TingDaoK May 3, 2023
883abdf
address comments
TingDaoK May 3, 2023
0d16418
unused variable
TingDaoK May 3, 2023
d772368
address comment
TingDaoK May 9, 2023
5f79af5
memory leak
TingDaoK May 9, 2023
a8ee7e4
remove part number from metrics, which is mostly just confusing
TingDaoK May 10, 2023
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_meta_request_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ struct aws_s3_meta_request {
aws_s3_meta_request_shutdown_fn *shutdown_callback;
aws_s3_meta_request_progress_fn *progress_callback;

aws_s3_meta_request_telemetry_fn *telemetry_callback;

/* Customer specified callbacks to be called by our specialized callback to calculate the response checksum. */
aws_s3_meta_request_headers_callback_fn *headers_user_callback_after_checksum;
aws_s3_meta_request_receive_body_callback_fn *body_user_callback_after_checksum;
Expand Down
75 changes: 75 additions & 0 deletions include/aws/s3/private/s3_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <aws/common/byte_buf.h>
#include <aws/common/linked_list.h>
#include <aws/common/ref_count.h>
#include <aws/common/thread.h>
#include <aws/s3/s3.h>

#include <aws/s3/private/s3_checksums.h>
Expand All @@ -23,6 +24,73 @@ enum aws_s3_request_flags {
AWS_S3_REQUEST_FLAG_ALWAYS_SEND = 0x00000004,
};

/**
* Information sent in the telemetry_callback after each aws_s3_request finished/retried from meta request.
*/
struct aws_s3_request_metrics {
Copy link
Contributor

Choose a reason for hiding this comment

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

should we expose extra things like num_parts, bytes_transferred?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

num_parts is included in the request_path_query, bytes_transferred can be interesting, but most likely will be the part size.

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe i dont understand what request_path_query is, but how would it include num parts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For put parts, it will have the part number in it, but, for multipart get, it is not.

I'll add the part number to be easier for the user

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But, for multipart get, we were actually doing ranged-get, which is different from get parts. I guess it can be confused to the user as well.

struct aws_allocator *allocator;

struct {
/* The time stamp when the request started by S3 client, which is prepared time by the client. Timestamps
* are from `aws_high_res_clock_get_ticks`. This will always be available. */
int64_t start_timestamp_ns;
/* The time stamp when the request finished by S3 client succeed or failed or to be retried. Timestamps
* are from `aws_high_res_clock_get_ticks`. This will always be available. */
int64_t end_timestamp_ns;
/* The time duration for the request from start to finish. end_timestamp_ns - start_timestamp_ns. This will
* always be available. */
int64_t total_duration_ns;

/* The time stamp when the request started to be encoded. -1 means data not available. Timestamp
* are from `aws_high_res_clock_get_ticks` */
int64_t send_start_timestamp_ns;
/* The time stamp when the request finished to be encoded. -1 means data not available.
* Timestamp are from `aws_high_res_clock_get_ticks` */
int64_t send_end_timestamp_ns;
/* The time duration for the request from start encoding to finish encoding (send_end_timestamp_ns -
* send_start_timestamp_ns). When send_end_timestamp_ns is -1, means data not available. */
int64_t sending_duration_ns;

/* The time stamp when the response started to be received from the network channel. -1 means data not
* available. Timestamp are from `aws_high_res_clock_get_ticks` */
int64_t receive_start_timestamp_ns;
/* The time stamp when the response finished to be received from the network channel. -1 means data not
* available. Timestamp are from `aws_high_res_clock_get_ticks` */
int64_t receive_end_timestamp_ns;
/* The time duration for the request from start receiving to finish receiving (receive_end_timestamp_ns -
* receive_start_timestamp_ns). When receive_end_timestamp_ns is 0, means data not available. */
int64_t receiving_duration_ns;
} time_metrics;

struct {
/* Response status code for the request */
int response_status;
/* HTTP Headers of the response received. */
struct aws_http_headers *response_headers;
/* Path and query of the request. */
struct aws_string *request_path_query;
/* The host address of the request. */
struct aws_string *host_address;
/* The the request ID header value. */
struct aws_string *request_id;
} req_resp_info_metrics;

struct {
/* The IP address of the request connected to */
struct aws_string *ip_address;
/* The pointer to the connection that request was made from */
void *connection_id;
/* The aws_thread_id_t to the thread that request ran on */
aws_thread_id_t thread_id;
/* The stream-id, which is the idex when the stream was activated. */
uint32_t stream_id;
/* CRT error code when the aws_s3_request finishes. */
int error_code;
} crt_info_metrics;

struct aws_ref_count ref_count;
};

/* Represents a single request made to S3. */
struct aws_s3_request {

Expand Down Expand Up @@ -103,6 +171,8 @@ struct aws_s3_request {
/* Returned response status of this request. */
int response_status;

/* The metrics for the request telemetry */
struct aws_s3_request_metrics *metrics;
} send_data;

/* When true, response headers from the request will be stored in the request's response_headers variable. */
Expand Down Expand Up @@ -147,6 +217,11 @@ void aws_s3_request_acquire(struct aws_s3_request *request);
AWS_S3_API
void aws_s3_request_release(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);

AWS_EXTERN_C_END

#endif
2 changes: 2 additions & 0 deletions include/aws/s3/private/s3_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ extern const struct aws_byte_cursor g_range_header_name;

extern const struct aws_byte_cursor g_if_match_header_name;

extern const struct aws_byte_cursor g_request_id_header_name;

AWS_S3_API
extern const struct aws_byte_cursor g_content_range_header_name;

Expand Down
1 change: 1 addition & 0 deletions include/aws/s3/s3.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum aws_s3_errors {
AWS_ERROR_S3_RESUME_FAILED,
AWS_ERROR_S3_OBJECT_MODIFIED,
AWS_ERROR_S3_NON_RECOVERABLE_ASYNC_ERROR,
AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE,
AWS_ERROR_S3_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_S3_PACKAGE_ID)
};

Expand Down
183 changes: 183 additions & 0 deletions include/aws/s3/s3_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ struct aws_s3_meta_request_resume_token;
struct aws_uri;
struct aws_string;

struct aws_s3_request_metrics;

/**
* A Meta Request represents a group of generated requests that are being done on behalf of the
* original request. For example, one large GetObject request can be transformed into a series
Expand Down Expand Up @@ -138,6 +140,17 @@ typedef void(aws_s3_meta_request_progress_fn)(
const struct aws_s3_meta_request_progress *progress,
void *user_data);

/**
* Invoked to report the telemetry of the meta request once a single request finishes. Invoked from the thread of the
* connection that request made from.
* Note: *metrics is only valid for the duration of the callback. If you need to keep it around, use
* `aws_s3_request_metrics_acquire`
*/
typedef void(aws_s3_meta_request_telemetry_fn)(
struct aws_s3_meta_request *meta_request,
struct aws_s3_request_metrics *metrics,
void *user_data);

typedef void(aws_s3_meta_request_shutdown_fn)(void *user_data);

typedef void(aws_s3_client_shutdown_complete_callback_fn)(void *user_data);
Expand Down Expand Up @@ -412,6 +425,18 @@ struct aws_s3_meta_request_options {
*/
aws_s3_meta_request_progress_fn *progress_callback;

/**
* Optional.
* To get telemetry metrics when a single request finishes.
Copy link
Member

Choose a reason for hiding this comment

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

is this guaranteed to only be invoked before the finish_callback?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes

* If set the request will keep track of the metrics from `aws_s3_request_metrics`, and fire the callback when the
* request finishes receiving response.
* See `aws_s3_meta_request_telemetry_fn`
*
* Notes:
* - The callback will be invoked multiple times from different threads.
Comment on lines +435 to +436
Copy link
Member

Choose a reason for hiding this comment

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

including concurrently?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes

*/
aws_s3_meta_request_telemetry_fn *telemetry_callback;

/**
* Optional.
* Endpoint override for request. Can be used to override scheme and port of
Expand Down Expand Up @@ -661,12 +686,170 @@ struct aws_s3_meta_request *aws_s3_meta_request_acquire(struct aws_s3_meta_reque
AWS_S3_API
struct aws_s3_meta_request *aws_s3_meta_request_release(struct aws_s3_meta_request *meta_request);

/**
* Initialize the configuration for a default S3 signing.
*/
AWS_S3_API
void aws_s3_init_default_signing_config(
struct aws_signing_config_aws *signing_config,
const struct aws_byte_cursor region,
struct aws_credentials_provider *credentials_provider);

/**
* 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.
* Always returns the same pointer that was passed in.
*/
AWS_S3_API
struct aws_s3_request_metrics *aws_s3_request_metrics_acquire(struct aws_s3_request_metrics *metrics);

/**
* Release a reference.
* When the reference count drops to 0, this object will be cleaned up.
* It's OK to pass in NULL (nothing happens).
* Always returns NULL.
*/
AWS_S3_API
struct aws_s3_request_metrics *aws_s3_request_metrics_release(struct aws_s3_request_metrics *metrics);

/************************************* Getters for s3 request metrics ************************************************/
/**
* Get the request ID from aws_s3_request_metrics.
* If unavailable, AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised.
* If available, out_request_id 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_request_id(
const struct aws_s3_request_metrics *metrics,
const struct aws_string **out_request_id);

/* Get the start time from aws_s3_request_metrics, which is when S3 client prepare the request to be sent. Always
* available. Timestamp are from `aws_high_res_clock_get_ticks` */
AWS_S3_API
void aws_s3_request_metrics_get_start_timestamp_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_start_time);

/* Get the end time from aws_s3_request_metrics. Always available */
AWS_S3_API
void aws_s3_request_metrics_get_end_timestamp_ns(const struct aws_s3_request_metrics *metrics, uint64_t *out_end_time);

/* Get the total duration time from aws_s3_request_metrics. Always available */
AWS_S3_API
void aws_s3_request_metrics_get_total_duration_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_total_duration);

/* Get the time stamp when the request started to be encoded. Timestamps are from `aws_high_res_clock_get_ticks`
* AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if the request ended before it gets sent. */
AWS_S3_API
int aws_s3_request_metrics_get_send_start_timestamp_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_send_start_time);

/* Get the time stamp when the request finished to be encoded. Timestamps are from `aws_high_res_clock_get_ticks`
* AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if data not available. */
AWS_S3_API
int aws_s3_request_metrics_get_send_end_timestamp_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_send_end_time);

/* The time duration for the request from start encoding to finish encoding (send_end_timestamp_ns -
* send_start_timestamp_ns).
* AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if data not available. */
AWS_S3_API
int aws_s3_request_metrics_get_sending_duration_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_sending_duration);

/* Get the time stamp when the response started to be received from the network channel. Timestamps are from
* `aws_high_res_clock_get_ticks` AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if data not available. */
AWS_S3_API
int aws_s3_request_metrics_get_receive_start_timestamp_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_receive_start_time);

/* Get the time stamp when the response finished to be received from the network channel. Timestamps are from
* `aws_high_res_clock_get_ticks` AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if data not available. */
AWS_S3_API
int aws_s3_request_metrics_get_receive_end_timestamp_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_receive_end_time);

/* The time duration for the request from start receiving to finish receiving (receive_end_timestamp_ns -
* receive_start_timestamp_ns).
* AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if data not available. */
AWS_S3_API
int aws_s3_request_metrics_get_receiving_duration_ns(
const struct aws_s3_request_metrics *metrics,
uint64_t *out_receiving_duration);

/* Get the response status code for the request. AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if data not
* available. */
AWS_S3_API
int aws_s3_request_metrics_get_response_status_code(
const struct aws_s3_request_metrics *metrics,
int *out_response_status);

/* Get the HTTP Headers of the response received for the request. AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised
* if data not available. */
AWS_S3_API
int aws_s3_request_metrics_get_response_headers(
const struct aws_s3_request_metrics *metrics,
struct aws_http_headers **out_response_headers);

/**
* Get the path and query of the request.
* If unavailable, AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised.
* If available, out_request_path_query will be set to a string. Be warned this string's lifetime is tied to the metrics
* object.
*/
AWS_S3_API
void aws_s3_request_metrics_get_request_path_query(
const struct aws_s3_request_metrics *metrics,
const struct aws_string **out_request_path_query);
Comment on lines +809 to +811
Copy link
Member

Choose a reason for hiding this comment

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

Can we get the type of request this way (GetObject/HeadObject/UploadPart/etc)?

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 add the type of request in a follow up PR to be more clear

Copy link
Contributor Author

Choose a reason for hiding this comment

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


/**
* Get the host_address of the request.
* If unavailable, AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised.
* If available, out_host_address will be set to a string. Be warned this string's lifetime is tied to the metrics
* object.
*/
AWS_S3_API
void aws_s3_request_metrics_get_host_address(
const struct aws_s3_request_metrics *metrics,
const struct aws_string **out_host_address);

/**
* Get the IP address of the request connected to.
* If unavailable, AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised.
* If available, out_ip_address 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_ip_address(
const struct aws_s3_request_metrics *metrics,
const struct aws_string **out_ip_address);

/* Get the id of connection that request was made from. AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if data
* not available */
AWS_S3_API
int aws_s3_request_metrics_get_connection_id(const struct aws_s3_request_metrics *metrics, size_t *out_connection_id);

/* Get the thread ID of the thread that request was made from. AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be raised if
* data not available */
AWS_S3_API
int aws_s3_request_metrics_get_thread_id(const struct aws_s3_request_metrics *metrics, aws_thread_id_t *out_thread_id);

/* Get the stream-id, which is the idex when the stream was activated. AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE will be
* raised if data not available */
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 AWS CRT error code from request metrics. */
AWS_S3_API
int aws_s3_request_metrics_get_error_code(const struct aws_s3_request_metrics *out_metrics);

AWS_EXTERN_C_END

#endif /* AWS_S3_CLIENT_H */
3 changes: 2 additions & 1 deletion source/s3.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ static struct aws_error_info s_errors[] = {
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RESUMED_PART_CHECKSUM_MISMATCH, "Checksum does not match previously uploaded part"),
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RESUME_FAILED, "Resuming request failed"),
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_OBJECT_MODIFIED, "The object modifed during download."),
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_NON_RECOVERABLE_ASYNC_ERROR, "Async error received from S3 and not recoverable from retry.")
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_NON_RECOVERABLE_ASYNC_ERROR, "Async error received from S3 and not recoverable from retry."),
AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_METRIC_DATA_NOT_AVAILABLE, "The metric data is not available, the requests ends before the metric happens."),
};
/* clang-format on */

Expand Down
9 changes: 6 additions & 3 deletions source/s3_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1596,9 +1596,9 @@ static void s_s3_client_create_connection_for_request_default(
struct aws_http_headers *message_headers = aws_http_message_get_headers(meta_request->initial_request_message);
AWS_ASSERT(message_headers);

int get_header_result = aws_http_headers_get(message_headers, g_host_header_name, &host_header_value);
AWS_ASSERT(get_header_result == AWS_OP_SUCCESS);
(void)get_header_result;
int result = aws_http_headers_get(message_headers, g_host_header_name, &host_header_value);
AWS_ASSERT(result == AWS_OP_SUCCESS);
(void)result;

if (aws_retry_strategy_acquire_retry_token(
client->retry_strategy, &host_header_value, s_s3_client_acquired_retry_token, connection, 0)) {
Expand Down Expand Up @@ -1750,6 +1750,9 @@ void aws_s3_client_notify_connection_finished(

struct aws_s3_endpoint *endpoint = meta_request->endpoint;
AWS_PRECONDITION(endpoint);
if (request->send_data.metrics) {
request->send_data.metrics->crt_info_metrics.error_code = error_code;
}

/* If we're trying to setup a retry... */
if (finish_code == AWS_S3_CONNECTION_FINISH_CODE_RETRY) {
Expand Down
Loading