Skip to content

Commit 73af2aa

Browse files
authored
1 parent f716114 commit 73af2aa

File tree

8 files changed

+152
-8
lines changed

8 files changed

+152
-8
lines changed

include/aws/http/connection_manager.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ typedef void(aws_http_connection_manager_on_connection_setup_fn)(
2525

2626
typedef void(aws_http_connection_manager_shutdown_complete_fn)(void *user_data);
2727

28+
/**
29+
* Metrics for logging and debugging purpose.
30+
*/
31+
struct aws_http_manager_metrics {
32+
/**
33+
* The number of additional concurrent requests that can be supported by the HTTP manager without needing to
34+
* establish additional connections to the target server.
35+
*
36+
* For connection manager, it equals to connections that's idle.
37+
* For stream manager, it equals to the number of streams that are possible to be made without creating new
38+
* connection, although the implementation can create new connection without fully filling it.
39+
*/
40+
size_t available_concurrency;
41+
/* The number of requests that are awaiting concurrency to be made available from the HTTP manager. */
42+
size_t pending_concurrency_acquires;
43+
};
44+
2845
/*
2946
* Connection manager configuration struct.
3047
*
@@ -143,6 +160,14 @@ int aws_http_connection_manager_release_connection(
143160
struct aws_http_connection_manager *manager,
144161
struct aws_http_connection *connection);
145162

163+
/**
164+
* Fetch the current manager metrics from connection manager.
165+
*/
166+
AWS_HTTP_API
167+
void aws_http_connection_manager_fetch_metrics(
168+
const struct aws_http_connection_manager *manager,
169+
struct aws_http_manager_metrics *out_metrics);
170+
146171
AWS_EXTERN_C_END
147172

148173
#endif /* AWS_HTTP_CONNECTION_MANAGER_H */

include/aws/http/http2_stream_manager.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct proxy_env_var_settings;
1818
struct aws_http2_setting;
1919
struct aws_http_make_request_options;
2020
struct aws_http_stream;
21+
struct aws_http_manager_metrics;
2122

2223
/**
2324
* Always invoked asynchronously when the stream was created, successfully or not.
@@ -168,5 +169,16 @@ void aws_http2_stream_manager_acquire_stream(
168169
struct aws_http2_stream_manager *http2_stream_manager,
169170
const struct aws_http2_stream_manager_acquire_stream_options *acquire_stream_option);
170171

172+
/**
173+
* Fetch the current metrics from stream manager.
174+
*
175+
* @param http2_stream_manager
176+
* @param out_metrics The metrics to be fetched
177+
*/
178+
AWS_HTTP_API
179+
void aws_http2_stream_manager_fetch_metrics(
180+
const struct aws_http2_stream_manager *http2_stream_manager,
181+
struct aws_http_manager_metrics *out_metrics);
182+
171183
AWS_EXTERN_C_END
172184
#endif /* AWS_HTTP2_STREAM_MANAGER_H */

include/aws/http/private/random_access_set.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,23 @@ int aws_random_access_set_remove(struct aws_random_access_set *set, const void *
6464
* Get the pointer to a random element from the data structure. Fails when the data structure is empty.
6565
*/
6666
AWS_HTTP_API
67-
int aws_random_access_set_random_get_ptr(struct aws_random_access_set *set, void **out);
67+
int aws_random_access_set_random_get_ptr(const struct aws_random_access_set *set, void **out);
6868

6969
AWS_HTTP_API
70-
size_t aws_random_access_set_get_size(struct aws_random_access_set *set);
70+
size_t aws_random_access_set_get_size(const struct aws_random_access_set *set);
7171

7272
/**
7373
* Check the element exist in the data structure or not.
7474
*/
7575
AWS_HTTP_API
76-
int aws_random_access_set_exist(struct aws_random_access_set *set, const void *element, bool *exist);
76+
int aws_random_access_set_exist(const struct aws_random_access_set *set, const void *element, bool *exist);
77+
78+
/**
79+
* Get the pointer to an element that currently stored at that index. It may change if operations like remove and add
80+
* happens. Helpful for debugging and iterating through the whole set.
81+
*/
82+
AWS_HTTP_API
83+
int aws_random_access_set_random_get_ptr_index(const struct aws_random_access_set *set, void **out, size_t index);
7784

7885
AWS_EXTERN_C_END
7986
#endif /* AWS_HTTP_RANDOM_ACCESS_SET_H */

source/connection_manager.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,3 +1538,15 @@ static void s_cull_task(struct aws_task *task, void *arg, enum aws_task_status s
15381538

15391539
s_schedule_connection_culling(manager);
15401540
}
1541+
1542+
void aws_http_connection_manager_fetch_metrics(
1543+
const struct aws_http_connection_manager *manager,
1544+
struct aws_http_manager_metrics *out_metrics) {
1545+
AWS_PRECONDITION(manager);
1546+
AWS_PRECONDITION(out_metrics);
1547+
1548+
AWS_FATAL_ASSERT(aws_mutex_lock((struct aws_mutex *)(void *)&manager->lock) == AWS_OP_SUCCESS);
1549+
out_metrics->available_concurrency = manager->idle_connection_count;
1550+
out_metrics->pending_concurrency_acquires = manager->pending_acquisition_count;
1551+
AWS_FATAL_ASSERT(aws_mutex_unlock((struct aws_mutex *)(void *)&manager->lock) == AWS_OP_SUCCESS);
1552+
}

source/http2_stream_manager.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ static struct aws_h2_sm_connection *s_get_best_sm_connection_from_set(struct aws
142142

143143
/* helper function for building the transaction: Try to assign connection for a pending stream acquisition */
144144
/* *_synced should only be called with LOCK HELD or from another synced function */
145-
static void s_sm_try_assign_connection_to_pending_stream_acquisition(
145+
static void s_sm_try_assign_connection_to_pending_stream_acquisition_synced(
146146
struct aws_http2_stream_manager *stream_manager,
147147
struct aws_h2_sm_pending_stream_acquisition *pending_stream_acquisition) {
148148

@@ -344,7 +344,7 @@ static void s_aws_http2_stream_manager_build_transaction_synced(struct aws_http2
344344
aws_linked_list_pop_front(&stream_manager->synced_data.pending_stream_acquisitions);
345345
struct aws_h2_sm_pending_stream_acquisition *pending_stream_acquisition =
346346
AWS_CONTAINER_OF(node, struct aws_h2_sm_pending_stream_acquisition, node);
347-
s_sm_try_assign_connection_to_pending_stream_acquisition(stream_manager, pending_stream_acquisition);
347+
s_sm_try_assign_connection_to_pending_stream_acquisition_synced(stream_manager, pending_stream_acquisition);
348348
if (pending_stream_acquisition->sm_connection == NULL) {
349349
/* Cannot find any connection, push it back to the front and break the loop */
350350
aws_linked_list_push_front(&stream_manager->synced_data.pending_stream_acquisitions, node);
@@ -994,3 +994,34 @@ void aws_http2_stream_manager_acquire_stream(
994994
} /* END CRITICAL SECTION */
995995
s_aws_http2_stream_manager_execute_transaction(&work);
996996
}
997+
998+
static size_t s_get_available_streams_num_from_connection_set(const struct aws_random_access_set *set) {
999+
size_t all_available_streams_num = 0;
1000+
size_t ideal_connection_num = aws_random_access_set_get_size(set);
1001+
for (size_t i = 0; i < ideal_connection_num; i++) {
1002+
struct aws_h2_sm_connection *sm_connection = NULL;
1003+
AWS_FATAL_ASSERT(aws_random_access_set_random_get_ptr_index(set, (void **)&sm_connection, i) == AWS_OP_SUCCESS);
1004+
uint32_t available_streams = sm_connection->max_concurrent_streams - sm_connection->num_streams_assigned;
1005+
all_available_streams_num += (size_t)available_streams;
1006+
}
1007+
return all_available_streams_num;
1008+
}
1009+
1010+
void aws_http2_stream_manager_fetch_metrics(
1011+
const struct aws_http2_stream_manager *stream_manager,
1012+
struct aws_http_manager_metrics *out_metrics) {
1013+
AWS_PRECONDITION(stream_manager);
1014+
AWS_PRECONDITION(out_metrics);
1015+
{ /* BEGIN CRITICAL SECTION */
1016+
s_lock_synced_data((struct aws_http2_stream_manager *)(void *)stream_manager);
1017+
size_t all_available_streams_num = 0;
1018+
all_available_streams_num +=
1019+
s_get_available_streams_num_from_connection_set(&stream_manager->synced_data.ideal_available_set);
1020+
all_available_streams_num +=
1021+
s_get_available_streams_num_from_connection_set(&stream_manager->synced_data.nonideal_available_set);
1022+
out_metrics->pending_concurrency_acquires =
1023+
stream_manager->synced_data.internal_refcount_stats[AWS_SMCT_PENDING_ACQUISITION];
1024+
out_metrics->available_concurrency = all_available_streams_num;
1025+
s_unlock_synced_data((struct aws_http2_stream_manager *)(void *)stream_manager);
1026+
} /* END CRITICAL SECTION */
1027+
}

source/random_access_set.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ int aws_random_access_set_remove(struct aws_random_access_set *set, const void *
150150
return AWS_OP_SUCCESS;
151151
}
152152

153-
int aws_random_access_set_random_get_ptr(struct aws_random_access_set *set, void **out) {
153+
int aws_random_access_set_random_get_ptr(const struct aws_random_access_set *set, void **out) {
154154
AWS_PRECONDITION(set);
155155
AWS_PRECONDITION(out != NULL);
156156
size_t length = aws_array_list_length(&set->impl->list);
@@ -166,11 +166,11 @@ int aws_random_access_set_random_get_ptr(struct aws_random_access_set *set, void
166166
return aws_array_list_get_at(&set->impl->list, (void *)out, index);
167167
}
168168

169-
size_t aws_random_access_set_get_size(struct aws_random_access_set *set) {
169+
size_t aws_random_access_set_get_size(const struct aws_random_access_set *set) {
170170
return aws_array_list_length(&set->impl->list);
171171
}
172172

173-
int aws_random_access_set_exist(struct aws_random_access_set *set, const void *element, bool *exist) {
173+
int aws_random_access_set_exist(const struct aws_random_access_set *set, const void *element, bool *exist) {
174174
AWS_PRECONDITION(set);
175175
AWS_PRECONDITION(element);
176176
AWS_PRECONDITION(exist);
@@ -179,3 +179,9 @@ int aws_random_access_set_exist(struct aws_random_access_set *set, const void *e
179179
*exist = find != NULL;
180180
return re;
181181
}
182+
183+
int aws_random_access_set_random_get_ptr_index(const struct aws_random_access_set *set, void **out, size_t index) {
184+
AWS_PRECONDITION(set);
185+
AWS_PRECONDITION(out != NULL);
186+
return aws_array_list_get_at(&set->impl->list, (void *)out, index);
187+
}

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ add_net_test_case(h2_sm_mock_multiple_connections)
604604
add_net_test_case(h2_sm_mock_bad_connection_acquired)
605605
add_net_test_case(h2_sm_mock_connections_closed_before_request_made)
606606
add_net_test_case(h2_sm_mock_max_concurrent_streams_remote)
607+
add_net_test_case(h2_sm_mock_fetch_metric)
607608
add_net_test_case(h2_sm_mock_complete_stream)
608609
add_net_test_case(h2_sm_mock_ideal_num_streams)
609610
add_net_test_case(h2_sm_mock_large_ideal_num_streams)

tests/test_stream_manager.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,56 @@ TEST_CASE(h2_sm_mock_max_concurrent_streams_remote) {
823823
return s_tester_clean_up();
824824
}
825825

826+
/* Test that the remote max concurrent streams setting hit */
827+
TEST_CASE(h2_sm_mock_fetch_metric) {
828+
(void)ctx;
829+
struct sm_tester_options options = {
830+
.max_connections = 5,
831+
.alloc = allocator,
832+
};
833+
ASSERT_SUCCESS(s_tester_init(&options));
834+
s_override_cm_connect_function(s_aws_http_connection_manager_create_connection_sync_mock);
835+
/* Set the remote max to be 2 */
836+
s_tester.max_con_stream_remote = 2;
837+
/* Acquire a stream to trigger */
838+
ASSERT_SUCCESS(s_sm_stream_acquiring(1));
839+
/* waiting for one fake connection made */
840+
ASSERT_SUCCESS(s_wait_on_fake_connection_count(1));
841+
s_drain_all_fake_connection_testing_channel();
842+
ASSERT_SUCCESS(s_wait_on_streams_acquired_count(1));
843+
struct aws_http_manager_metrics out_metrics;
844+
AWS_ZERO_STRUCT(out_metrics);
845+
846+
aws_http2_stream_manager_fetch_metrics(s_tester.stream_manager, &out_metrics);
847+
/* Acquired 1 stream, and we hold one connection, the max streams per connection is 2. */
848+
ASSERT_UINT_EQUALS(out_metrics.available_concurrency, 1);
849+
ASSERT_UINT_EQUALS(out_metrics.pending_concurrency_acquires, 0);
850+
851+
ASSERT_SUCCESS(s_sm_stream_acquiring(1));
852+
853+
ASSERT_SUCCESS(s_wait_on_fake_connection_count(1));
854+
s_drain_all_fake_connection_testing_channel();
855+
ASSERT_SUCCESS(s_wait_on_streams_acquired_count(2));
856+
aws_http2_stream_manager_fetch_metrics(s_tester.stream_manager, &out_metrics);
857+
ASSERT_UINT_EQUALS(out_metrics.available_concurrency, 0);
858+
ASSERT_UINT_EQUALS(out_metrics.pending_concurrency_acquires, 0);
859+
860+
ASSERT_SUCCESS(s_sm_stream_acquiring(10));
861+
ASSERT_SUCCESS(s_wait_on_fake_connection_count(5));
862+
s_drain_all_fake_connection_testing_channel();
863+
ASSERT_SUCCESS(s_wait_on_streams_acquired_count(10));
864+
aws_http2_stream_manager_fetch_metrics(s_tester.stream_manager, &out_metrics);
865+
ASSERT_UINT_EQUALS(out_metrics.available_concurrency, 0);
866+
ASSERT_UINT_EQUALS(out_metrics.pending_concurrency_acquires, 2);
867+
868+
ASSERT_SUCCESS(s_complete_all_fake_connection_streams());
869+
/* Still have two more streams that have not been completed */
870+
s_drain_all_fake_connection_testing_channel();
871+
ASSERT_SUCCESS(s_complete_all_fake_connection_streams());
872+
873+
return s_tester_clean_up();
874+
}
875+
826876
/* Test that the stream completed will free the connection for more streams */
827877
TEST_CASE(h2_sm_mock_complete_stream) {
828878
(void)ctx;

0 commit comments

Comments
 (0)