aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/ext/filters/client_channel/retry_throttle.cc
diff options
context:
space:
mode:
authorGravatar Yash Tibrewal <yashkt@google.com>2017-09-21 18:56:08 -0700
committerGravatar Yash Tibrewal <yashkt@google.com>2017-10-02 16:29:41 -0700
commit83062842c3601faeddcae8f901c515e3c78f3661 (patch)
tree58536b25350a5f2b3123161ab4ca9b8ce01a43a5 /src/core/ext/filters/client_channel/retry_throttle.cc
parent1c9b584a153ff592c92b6cf6baa620d8602a37cd (diff)
Changes for C to C++. Adding extern C to header files for compatibility.
Also converting to .cc
Diffstat (limited to 'src/core/ext/filters/client_channel/retry_throttle.cc')
-rw-r--r--src/core/ext/filters/client_channel/retry_throttle.cc202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/core/ext/filters/client_channel/retry_throttle.cc b/src/core/ext/filters/client_channel/retry_throttle.cc
new file mode 100644
index 0000000000..09dcade089
--- /dev/null
+++ b/src/core/ext/filters/client_channel/retry_throttle.cc
@@ -0,0 +1,202 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "src/core/ext/filters/client_channel/retry_throttle.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/avl.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+//
+// server_retry_throttle_data
+//
+
+struct grpc_server_retry_throttle_data {
+ gpr_refcount refs;
+ int max_milli_tokens;
+ int milli_token_ratio;
+ gpr_atm milli_tokens;
+ // A pointer to the replacement for this grpc_server_retry_throttle_data
+ // entry. If non-NULL, then this entry is stale and must not be used.
+ // We hold a reference to the replacement.
+ gpr_atm replacement;
+};
+
+static void get_replacement_throttle_data_if_needed(
+ grpc_server_retry_throttle_data** throttle_data) {
+ while (true) {
+ grpc_server_retry_throttle_data* new_throttle_data =
+ (grpc_server_retry_throttle_data*)gpr_atm_acq_load(
+ &(*throttle_data)->replacement);
+ if (new_throttle_data == NULL) return;
+ *throttle_data = new_throttle_data;
+ }
+}
+
+bool grpc_server_retry_throttle_data_record_failure(
+ grpc_server_retry_throttle_data* throttle_data) {
+ // First, check if we are stale and need to be replaced.
+ get_replacement_throttle_data_if_needed(&throttle_data);
+ // We decrement milli_tokens by 1000 (1 token) for each failure.
+ const int new_value = (int)gpr_atm_no_barrier_clamped_add(
+ &throttle_data->milli_tokens, (gpr_atm)-1000, (gpr_atm)0,
+ (gpr_atm)throttle_data->max_milli_tokens);
+ // Retries are allowed as long as the new value is above the threshold
+ // (max_milli_tokens / 2).
+ return new_value > throttle_data->max_milli_tokens / 2;
+}
+
+void grpc_server_retry_throttle_data_record_success(
+ grpc_server_retry_throttle_data* throttle_data) {
+ // First, check if we are stale and need to be replaced.
+ get_replacement_throttle_data_if_needed(&throttle_data);
+ // We increment milli_tokens by milli_token_ratio for each success.
+ gpr_atm_no_barrier_clamped_add(
+ &throttle_data->milli_tokens, (gpr_atm)throttle_data->milli_token_ratio,
+ (gpr_atm)0, (gpr_atm)throttle_data->max_milli_tokens);
+}
+
+grpc_server_retry_throttle_data* grpc_server_retry_throttle_data_ref(
+ grpc_server_retry_throttle_data* throttle_data) {
+ gpr_ref(&throttle_data->refs);
+ return throttle_data;
+}
+
+void grpc_server_retry_throttle_data_unref(
+ grpc_server_retry_throttle_data* throttle_data) {
+ if (gpr_unref(&throttle_data->refs)) {
+ grpc_server_retry_throttle_data* replacement =
+ (grpc_server_retry_throttle_data*)gpr_atm_acq_load(
+ &throttle_data->replacement);
+ if (replacement != NULL) {
+ grpc_server_retry_throttle_data_unref(replacement);
+ }
+ gpr_free(throttle_data);
+ }
+}
+
+static grpc_server_retry_throttle_data* grpc_server_retry_throttle_data_create(
+ int max_milli_tokens, int milli_token_ratio,
+ grpc_server_retry_throttle_data* old_throttle_data) {
+ grpc_server_retry_throttle_data* throttle_data =
+ (grpc_server_retry_throttle_data*)gpr_malloc(sizeof(*throttle_data));
+ memset(throttle_data, 0, sizeof(*throttle_data));
+ gpr_ref_init(&throttle_data->refs, 1);
+ throttle_data->max_milli_tokens = max_milli_tokens;
+ throttle_data->milli_token_ratio = milli_token_ratio;
+ int initial_milli_tokens = max_milli_tokens;
+ // If there was a pre-existing entry for this server name, initialize
+ // the token count by scaling proportionately to the old data. This
+ // ensures that if we're already throttling retries on the old scale,
+ // we will start out doing the same thing on the new one.
+ if (old_throttle_data != NULL) {
+ double token_fraction =
+ (int)gpr_atm_acq_load(&old_throttle_data->milli_tokens) /
+ (double)old_throttle_data->max_milli_tokens;
+ initial_milli_tokens = (int)(token_fraction * max_milli_tokens);
+ }
+ gpr_atm_rel_store(&throttle_data->milli_tokens,
+ (gpr_atm)initial_milli_tokens);
+ // If there was a pre-existing entry, mark it as stale and give it a
+ // pointer to the new entry, which is its replacement.
+ if (old_throttle_data != NULL) {
+ grpc_server_retry_throttle_data_ref(throttle_data);
+ gpr_atm_rel_store(&old_throttle_data->replacement, (gpr_atm)throttle_data);
+ }
+ return throttle_data;
+}
+
+//
+// avl vtable for string -> server_retry_throttle_data map
+//
+
+static void* copy_server_name(void* key, void* unused) {
+ return gpr_strdup((const char*)key);
+}
+
+static long compare_server_name(void* key1, void* key2, void* unused) {
+ return strcmp((const char*)key1, (const char*)key2);
+}
+
+static void destroy_server_retry_throttle_data(void* value, void* unused) {
+ grpc_server_retry_throttle_data* throttle_data =
+ (grpc_server_retry_throttle_data*)value;
+ grpc_server_retry_throttle_data_unref(throttle_data);
+}
+
+static void* copy_server_retry_throttle_data(void* value, void* unused) {
+ grpc_server_retry_throttle_data* throttle_data =
+ (grpc_server_retry_throttle_data*)value;
+ return grpc_server_retry_throttle_data_ref(throttle_data);
+}
+
+static void destroy_server_name(void* key, void* unused) { gpr_free(key); }
+
+static const gpr_avl_vtable avl_vtable = {
+ destroy_server_name, copy_server_name, compare_server_name,
+ destroy_server_retry_throttle_data, copy_server_retry_throttle_data};
+
+//
+// server_retry_throttle_map
+//
+
+static gpr_mu g_mu;
+static gpr_avl g_avl;
+
+void grpc_retry_throttle_map_init() {
+ gpr_mu_init(&g_mu);
+ g_avl = gpr_avl_create(&avl_vtable);
+}
+
+void grpc_retry_throttle_map_shutdown() {
+ gpr_mu_destroy(&g_mu);
+ gpr_avl_unref(g_avl, NULL);
+}
+
+grpc_server_retry_throttle_data* grpc_retry_throttle_map_get_data_for_server(
+ const char* server_name, int max_milli_tokens, int milli_token_ratio) {
+ gpr_mu_lock(&g_mu);
+ grpc_server_retry_throttle_data* throttle_data =
+ (grpc_server_retry_throttle_data*)gpr_avl_get(g_avl, (char*)server_name,
+ NULL);
+ if (throttle_data == NULL) {
+ // Entry not found. Create a new one.
+ throttle_data = grpc_server_retry_throttle_data_create(
+ max_milli_tokens, milli_token_ratio, NULL);
+ g_avl = gpr_avl_add(g_avl, (char*)server_name, throttle_data, NULL);
+ } else {
+ if (throttle_data->max_milli_tokens != max_milli_tokens ||
+ throttle_data->milli_token_ratio != milli_token_ratio) {
+ // Entry found but with old parameters. Create a new one based on
+ // the original one.
+ throttle_data = grpc_server_retry_throttle_data_create(
+ max_milli_tokens, milli_token_ratio, throttle_data);
+ g_avl = gpr_avl_add(g_avl, (char*)server_name, throttle_data, NULL);
+ } else {
+ // Entry found. Increase refcount.
+ grpc_server_retry_throttle_data_ref(throttle_data);
+ }
+ }
+ gpr_mu_unlock(&g_mu);
+ return throttle_data;
+}