diff options
-rw-r--r-- | CMakeLists.txt | 69 | ||||
-rw-r--r-- | Makefile | 84 | ||||
-rw-r--r-- | build.yaml | 22 | ||||
-rw-r--r-- | doc/connection-backoff.md | 5 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc | 26 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc | 23 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc | 23 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/subchannel.cc | 103 | ||||
-rw-r--r-- | src/core/lib/backoff/backoff.cc | 81 | ||||
-rw-r--r-- | src/core/lib/backoff/backoff.h | 112 | ||||
-rw-r--r-- | test/core/backoff/BUILD | 7 | ||||
-rw-r--r-- | test/core/backoff/backoff_test.cc | 196 | ||||
-rw-r--r-- | tools/run_tests/generated/sources_and_headers.json | 34 | ||||
-rw-r--r-- | tools/run_tests/generated/tests.json | 48 |
14 files changed, 433 insertions, 400 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 22a1ab79af..7af707ef53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -380,7 +380,6 @@ add_dependencies(buildtests_c algorithm_test) add_dependencies(buildtests_c alloc_test) add_dependencies(buildtests_c alpn_test) add_dependencies(buildtests_c arena_test) -add_dependencies(buildtests_c backoff_test) add_dependencies(buildtests_c bad_server_response_test) add_dependencies(buildtests_c bin_decoder_test) add_dependencies(buildtests_c bin_encoder_test) @@ -633,6 +632,7 @@ add_custom_target(buildtests_cxx) add_dependencies(buildtests_cxx alarm_cpp_test) add_dependencies(buildtests_cxx async_end2end_test) add_dependencies(buildtests_cxx auth_property_iterator_test) +add_dependencies(buildtests_cxx backoff_test) add_dependencies(buildtests_cxx bdp_estimator_test) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_cxx bm_arena) @@ -5195,35 +5195,6 @@ target_link_libraries(arena_test endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) -add_executable(backoff_test - test/core/backoff/backoff_test.cc -) - - -target_include_directories(backoff_test - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include - PRIVATE ${BORINGSSL_ROOT_DIR}/include - PRIVATE ${PROTOBUF_ROOT_DIR}/src - PRIVATE ${BENCHMARK_ROOT_DIR}/include - PRIVATE ${ZLIB_ROOT_DIR} - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib - PRIVATE ${CARES_INCLUDE_DIR} - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include -) - -target_link_libraries(backoff_test - ${_gRPC_ALLTARGETS_LIBRARIES} - grpc_test_util - grpc - gpr_test_util - gpr -) - -endif (gRPC_BUILD_TESTS) -if (gRPC_BUILD_TESTS) - add_executable(bad_server_response_test test/core/end2end/bad_server_response_test.cc ) @@ -9133,6 +9104,44 @@ target_link_libraries(auth_property_iterator_test endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) +add_executable(backoff_test + test/core/backoff/backoff_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + + +target_include_directories(backoff_test + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${BORINGSSL_ROOT_DIR}/include + PRIVATE ${PROTOBUF_ROOT_DIR}/src + PRIVATE ${BENCHMARK_ROOT_DIR}/include + PRIVATE ${ZLIB_ROOT_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib + PRIVATE ${CARES_INCLUDE_DIR} + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares + PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include + PRIVATE third_party/googletest/googletest/include + PRIVATE third_party/googletest/googletest + PRIVATE third_party/googletest/googlemock/include + PRIVATE third_party/googletest/googlemock + PRIVATE ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(backoff_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc + gpr_test_util + gpr + ${_gRPC_GFLAGS_LIBRARIES} +) + +endif (gRPC_BUILD_TESTS) +if (gRPC_BUILD_TESTS) + add_executable(bdp_estimator_test test/core/transport/bdp_estimator_test.cc third_party/googletest/googletest/src/gtest-all.cc @@ -951,7 +951,6 @@ alloc_test: $(BINDIR)/$(CONFIG)/alloc_test alpn_test: $(BINDIR)/$(CONFIG)/alpn_test api_fuzzer: $(BINDIR)/$(CONFIG)/api_fuzzer arena_test: $(BINDIR)/$(CONFIG)/arena_test -backoff_test: $(BINDIR)/$(CONFIG)/backoff_test bad_server_response_test: $(BINDIR)/$(CONFIG)/bad_server_response_test bin_decoder_test: $(BINDIR)/$(CONFIG)/bin_decoder_test bin_encoder_test: $(BINDIR)/$(CONFIG)/bin_encoder_test @@ -1097,6 +1096,7 @@ wakeup_fd_cv_test: $(BINDIR)/$(CONFIG)/wakeup_fd_cv_test alarm_cpp_test: $(BINDIR)/$(CONFIG)/alarm_cpp_test async_end2end_test: $(BINDIR)/$(CONFIG)/async_end2end_test auth_property_iterator_test: $(BINDIR)/$(CONFIG)/auth_property_iterator_test +backoff_test: $(BINDIR)/$(CONFIG)/backoff_test bdp_estimator_test: $(BINDIR)/$(CONFIG)/bdp_estimator_test bm_arena: $(BINDIR)/$(CONFIG)/bm_arena bm_call_create: $(BINDIR)/$(CONFIG)/bm_call_create @@ -1352,7 +1352,6 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/alloc_test \ $(BINDIR)/$(CONFIG)/alpn_test \ $(BINDIR)/$(CONFIG)/arena_test \ - $(BINDIR)/$(CONFIG)/backoff_test \ $(BINDIR)/$(CONFIG)/bad_server_response_test \ $(BINDIR)/$(CONFIG)/bin_decoder_test \ $(BINDIR)/$(CONFIG)/bin_encoder_test \ @@ -1541,6 +1540,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/alarm_cpp_test \ $(BINDIR)/$(CONFIG)/async_end2end_test \ $(BINDIR)/$(CONFIG)/auth_property_iterator_test \ + $(BINDIR)/$(CONFIG)/backoff_test \ $(BINDIR)/$(CONFIG)/bdp_estimator_test \ $(BINDIR)/$(CONFIG)/bm_arena \ $(BINDIR)/$(CONFIG)/bm_call_create \ @@ -1669,6 +1669,7 @@ buildtests_cxx: privatelibs_cxx \ $(BINDIR)/$(CONFIG)/alarm_cpp_test \ $(BINDIR)/$(CONFIG)/async_end2end_test \ $(BINDIR)/$(CONFIG)/auth_property_iterator_test \ + $(BINDIR)/$(CONFIG)/backoff_test \ $(BINDIR)/$(CONFIG)/bdp_estimator_test \ $(BINDIR)/$(CONFIG)/bm_arena \ $(BINDIR)/$(CONFIG)/bm_call_create \ @@ -1772,8 +1773,6 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/alpn_test || ( echo test alpn_test failed ; exit 1 ) $(E) "[RUN] Testing arena_test" $(Q) $(BINDIR)/$(CONFIG)/arena_test || ( echo test arena_test failed ; exit 1 ) - $(E) "[RUN] Testing backoff_test" - $(Q) $(BINDIR)/$(CONFIG)/backoff_test || ( echo test backoff_test failed ; exit 1 ) $(E) "[RUN] Testing bad_server_response_test" $(Q) $(BINDIR)/$(CONFIG)/bad_server_response_test || ( echo test bad_server_response_test failed ; exit 1 ) $(E) "[RUN] Testing bin_decoder_test" @@ -2038,6 +2037,8 @@ test_cxx: buildtests_cxx $(Q) $(BINDIR)/$(CONFIG)/async_end2end_test || ( echo test async_end2end_test failed ; exit 1 ) $(E) "[RUN] Testing auth_property_iterator_test" $(Q) $(BINDIR)/$(CONFIG)/auth_property_iterator_test || ( echo test auth_property_iterator_test failed ; exit 1 ) + $(E) "[RUN] Testing backoff_test" + $(Q) $(BINDIR)/$(CONFIG)/backoff_test || ( echo test backoff_test failed ; exit 1 ) $(E) "[RUN] Testing bdp_estimator_test" $(Q) $(BINDIR)/$(CONFIG)/bdp_estimator_test || ( echo test bdp_estimator_test failed ; exit 1 ) $(E) "[RUN] Testing bm_arena" @@ -8912,38 +8913,6 @@ endif endif -BACKOFF_TEST_SRC = \ - test/core/backoff/backoff_test.cc \ - -BACKOFF_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BACKOFF_TEST_SRC)))) -ifeq ($(NO_SECURE),true) - -# You can't build secure targets if you don't have OpenSSL. - -$(BINDIR)/$(CONFIG)/backoff_test: openssl_dep_error - -else - - - -$(BINDIR)/$(CONFIG)/backoff_test: $(BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a - $(E) "[LD] Linking $@" - $(Q) mkdir -p `dirname $@` - $(Q) $(LD) $(LDFLAGS) $(BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/backoff_test - -endif - -$(OBJDIR)/$(CONFIG)/test/core/backoff/backoff_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a - -deps_backoff_test: $(BACKOFF_TEST_OBJS:.o=.dep) - -ifneq ($(NO_SECURE),true) -ifneq ($(NO_DEPS),true) --include $(BACKOFF_TEST_OBJS:.o=.dep) -endif -endif - - BAD_SERVER_RESPONSE_TEST_SRC = \ test/core/end2end/bad_server_response_test.cc \ @@ -13629,6 +13598,49 @@ endif endif +BACKOFF_TEST_SRC = \ + test/core/backoff/backoff_test.cc \ + +BACKOFF_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(BACKOFF_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/backoff_test: openssl_dep_error + +else + + + + +ifeq ($(NO_PROTOBUF),true) + +# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+. + +$(BINDIR)/$(CONFIG)/backoff_test: protobuf_dep_error + +else + +$(BINDIR)/$(CONFIG)/backoff_test: $(PROTOBUF_DEP) $(BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LDXX) $(LDFLAGS) $(BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/backoff_test + +endif + +endif + +$(OBJDIR)/$(CONFIG)/test/core/backoff/backoff_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_backoff_test: $(BACKOFF_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(BACKOFF_TEST_OBJS:.o=.dep) +endif +endif + + BDP_ESTIMATOR_TEST_SRC = \ test/core/transport/bdp_estimator_test.cc \ diff --git a/build.yaml b/build.yaml index 6158b466ea..9e9dcbc0b4 100644 --- a/build.yaml +++ b/build.yaml @@ -1769,17 +1769,6 @@ targets: - gpr_test_util - gpr uses_polling: false -- name: backoff_test - build: test - language: c - src: - - test/core/backoff/backoff_test.cc - deps: - - grpc_test_util - - grpc - - gpr_test_util - - gpr - uses_polling: false - name: bad_server_response_test build: test language: c @@ -3479,6 +3468,17 @@ targets: - gpr_test_util - gpr uses_polling: false +- name: backoff_test + build: test + language: c++ + src: + - test/core/backoff/backoff_test.cc + deps: + - grpc_test_util + - grpc + - gpr_test_util + - gpr + uses_polling: false - name: bdp_estimator_test build: test language: c++ diff --git a/doc/connection-backoff.md b/doc/connection-backoff.md index 0e83d9b97f..85cd5e258e 100644 --- a/doc/connection-backoff.md +++ b/doc/connection-backoff.md @@ -26,9 +26,8 @@ ConnectWithBackoff() != SUCCESS) SleepUntil(current_deadline) current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF) - current_deadline = now() + current_backoff + - UniformRandom(-JITTER * current_backoff, JITTER * current_backoff) - + jitter = UniformRandom(-JITTER * current_backoff, JITTER * current_backoff) + current_deadline = now() + Min(current_backoff + jitter, MAX_BACKOFF) ``` With specific parameters of diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc index db06fc20b6..d8137636b8 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -113,13 +113,13 @@ #include "src/core/lib/slice/slice_hash_table.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/manual_constructor.h" #include "src/core/lib/support/string.h" #include "src/core/lib/surface/call.h" #include "src/core/lib/surface/channel.h" #include "src/core/lib/surface/channel_init.h" #include "src/core/lib/transport/static_metadata.h" -#define GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS 20 #define GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS 1 #define GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER 1.6 #define GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS 120 @@ -397,7 +397,7 @@ typedef struct glb_lb_policy { grpc_slice lb_call_status_details; /** LB call retry backoff state */ - grpc_backoff lb_call_backoff_state; + grpc_core::ManualConstructor<grpc_core::BackOff> lb_call_backoff; /** LB call retry timer */ grpc_timer lb_call_retry_timer; @@ -1156,7 +1156,7 @@ static void start_picking_locked(grpc_exec_ctx* exec_ctx, } glb_policy->started_picking = true; - grpc_backoff_reset(&glb_policy->lb_call_backoff_state); + glb_policy->lb_call_backoff->Reset(); query_for_backends_locked(exec_ctx, glb_policy); } @@ -1296,9 +1296,7 @@ static void maybe_restart_lb_call(grpc_exec_ctx* exec_ctx, glb_policy->updating_lb_call = false; } else if (!glb_policy->shutting_down) { /* if we aren't shutting down, restart the LB client call after some time */ - grpc_millis next_try = - grpc_backoff_step(exec_ctx, &glb_policy->lb_call_backoff_state) - .next_attempt_start_time; + grpc_millis next_try = glb_policy->lb_call_backoff->Step(exec_ctx); if (grpc_lb_glb_trace.enabled()) { gpr_log(GPR_DEBUG, "[grpclb %p] Connection to LB server lost...", glb_policy); @@ -1467,12 +1465,14 @@ static void lb_call_init_locked(grpc_exec_ctx* exec_ctx, lb_on_response_received_locked, glb_policy, grpc_combiner_scheduler(glb_policy->base.combiner)); - grpc_backoff_init(&glb_policy->lb_call_backoff_state, - GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS * 1000, - GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER, - GRPC_GRPCLB_RECONNECT_JITTER, - GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000, - GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + grpc_core::BackOff::Options backoff_options; + backoff_options + .set_initial_backoff(GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS * 1000) + .set_multiplier(GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(GRPC_GRPCLB_RECONNECT_JITTER) + .set_max_backoff(GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + + glb_policy->lb_call_backoff.Init(backoff_options); glb_policy->seen_initial_response = false; glb_policy->last_client_load_report_counters_were_zero = false; @@ -1579,7 +1579,7 @@ static void lb_on_response_received_locked(grpc_exec_ctx* exec_ctx, void* arg, memset(ops, 0, sizeof(ops)); grpc_op* op = ops; if (glb_policy->lb_response_payload != nullptr) { - grpc_backoff_reset(&glb_policy->lb_call_backoff_state); + glb_policy->lb_call_backoff->Reset(); /* Received data from the LB server. Look inside * glb_policy->lb_response_payload, for a serverlist. */ grpc_byte_buffer_reader bbr; diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc index 3a16b3492d..543f5cf343 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc @@ -40,10 +40,10 @@ #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/json/json.h" #include "src/core/lib/support/env.h" +#include "src/core/lib/support/manual_constructor.h" #include "src/core/lib/support/string.h" #include "src/core/lib/transport/service_config.h" -#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 #define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 #define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 #define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 @@ -89,7 +89,7 @@ typedef struct { bool have_retry_timer; grpc_timer retry_timer; /** retry backoff state */ - grpc_backoff backoff_state; + grpc_core::ManualConstructor<grpc_core::BackOff> backoff; /** currently resolving addresses */ grpc_lb_addresses* lb_addresses; @@ -137,7 +137,7 @@ static void dns_ares_channel_saw_error_locked(grpc_exec_ctx* exec_ctx, grpc_resolver* resolver) { ares_dns_resolver* r = (ares_dns_resolver*)resolver; if (!r->resolving) { - grpc_backoff_reset(&r->backoff_state); + r->backoff->Reset(); dns_ares_start_resolving_locked(exec_ctx, r); } } @@ -272,8 +272,7 @@ static void dns_ares_on_resolved_locked(grpc_exec_ctx* exec_ctx, void* arg, } else { const char* msg = grpc_error_string(error); gpr_log(GPR_DEBUG, "dns resolution failed: %s", msg); - grpc_millis next_try = - grpc_backoff_step(exec_ctx, &r->backoff_state).next_attempt_start_time; + grpc_millis next_try = r->backoff->Step(exec_ctx); grpc_millis timeout = next_try - grpc_exec_ctx_now(exec_ctx); gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", grpc_error_string(error)); @@ -307,7 +306,7 @@ static void dns_ares_next_locked(grpc_exec_ctx* exec_ctx, r->next_completion = on_complete; r->target_result = target_result; if (r->resolved_version == 0 && !r->resolving) { - grpc_backoff_reset(&r->backoff_state); + r->backoff->Reset(); dns_ares_start_resolving_locked(exec_ctx, r); } else { dns_ares_maybe_finish_next_locked(exec_ctx, r); @@ -381,11 +380,13 @@ static grpc_resolver* dns_ares_create(grpc_exec_ctx* exec_ctx, grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, args->pollset_set); } - grpc_backoff_init( - &r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000, - GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER, GRPC_DNS_RECONNECT_JITTER, - GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000, - GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + grpc_core::BackOff::Options backoff_options; + backoff_options + .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000) + .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(GRPC_DNS_RECONNECT_JITTER) + .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + r->backoff.Init(grpc_core::BackOff(backoff_options)); GRPC_CLOSURE_INIT(&r->dns_ares_on_retry_timer_locked, dns_ares_on_retry_timer_locked, r, grpc_combiner_scheduler(r->base.combiner)); diff --git a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc index fc40ce6966..03a7b446f6 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc @@ -33,9 +33,9 @@ #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/support/env.h" +#include "src/core/lib/support/manual_constructor.h" #include "src/core/lib/support/string.h" -#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 #define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 #define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 #define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 @@ -70,7 +70,7 @@ typedef struct { grpc_timer retry_timer; grpc_closure on_retry; /** retry backoff state */ - grpc_backoff backoff_state; + grpc_core::ManualConstructor<grpc_core::BackOff> backoff; /** currently resolving addresses */ grpc_resolved_addresses* addresses; @@ -113,7 +113,7 @@ static void dns_channel_saw_error_locked(grpc_exec_ctx* exec_ctx, grpc_resolver* resolver) { dns_resolver* r = (dns_resolver*)resolver; if (!r->resolving) { - grpc_backoff_reset(&r->backoff_state); + r->backoff->Reset(); dns_start_resolving_locked(exec_ctx, r); } } @@ -126,7 +126,7 @@ static void dns_next_locked(grpc_exec_ctx* exec_ctx, grpc_resolver* resolver, r->next_completion = on_complete; r->target_result = target_result; if (r->resolved_version == 0 && !r->resolving) { - grpc_backoff_reset(&r->backoff_state); + r->backoff->Reset(); dns_start_resolving_locked(exec_ctx, r); } else { dns_maybe_finish_next_locked(exec_ctx, r); @@ -170,8 +170,7 @@ static void dns_on_resolved_locked(grpc_exec_ctx* exec_ctx, void* arg, grpc_resolved_addresses_destroy(r->addresses); grpc_lb_addresses_destroy(exec_ctx, addresses); } else { - grpc_millis next_try = - grpc_backoff_step(exec_ctx, &r->backoff_state).next_attempt_start_time; + grpc_millis next_try = r->backoff->Step(exec_ctx); grpc_millis timeout = next_try - grpc_exec_ctx_now(exec_ctx); gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", grpc_error_string(error)); @@ -257,11 +256,13 @@ static grpc_resolver* dns_create(grpc_exec_ctx* exec_ctx, grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, args->pollset_set); } - grpc_backoff_init( - &r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000, - GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER, GRPC_DNS_RECONNECT_JITTER, - GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000, - GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + grpc_core::BackOff::Options backoff_options; + backoff_options + .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * 1000) + .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(GRPC_DNS_RECONNECT_JITTER) + .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + r->backoff.Init(grpc_core::BackOff(backoff_options)); return &r->base; } diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc index 58e294d597..136a4de2ee 100644 --- a/src/core/ext/filters/client_channel/subchannel.cc +++ b/src/core/ext/filters/client_channel/subchannel.cc @@ -20,7 +20,9 @@ #include <inttypes.h> #include <limits.h> -#include <string.h> + +#include <algorithm> +#include <cstring> #include <grpc/support/alloc.h> #include <grpc/support/avl.h> @@ -39,6 +41,7 @@ #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/manual_constructor.h" #include "src/core/lib/surface/channel.h" #include "src/core/lib/surface/channel_init.h" #include "src/core/lib/transport/connectivity_state.h" @@ -48,7 +51,7 @@ #define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1 #define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6 -#define GRPC_SUBCHANNEL_RECONNECT_MIN_BACKOFF_SECONDS 20 +#define GRPC_SUBCHANNEL_RECONNECT_MIN_TIMEOUT_SECONDS 20 #define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120 #define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2 @@ -118,8 +121,8 @@ struct grpc_subchannel { external_state_watcher root_external_state_watcher; /** backoff state */ - grpc_backoff backoff_state; - grpc_backoff_result backoff_result; + grpc_core::ManualConstructor<grpc_core::BackOff> backoff; + grpc_millis next_attempt_deadline; /** do we have an active alarm? */ bool have_alarm; @@ -283,6 +286,44 @@ void grpc_subchannel_weak_unref(grpc_exec_ctx* exec_ctx, } } +static grpc_core::BackOff::Options extract_backoff_options( + const grpc_channel_args* args) { + int initial_backoff_ms = + GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000; + int max_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; + bool fixed_reconnect_backoff = false; + if (args != nullptr) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, + "grpc.testing.fixed_reconnect_backoff_ms")) { + fixed_reconnect_backoff = true; + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_MIN_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + max_backoff_ms = grpc_channel_arg_get_integer( + &args->args[i], {max_backoff_ms, 100, INT_MAX}); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + initial_backoff_ms = grpc_channel_arg_get_integer( + &args->args[i], {initial_backoff_ms, 100, INT_MAX}); + } + } + } + grpc_core::BackOff::Options backoff_options; + backoff_options.set_initial_backoff(initial_backoff_ms) + .set_multiplier(fixed_reconnect_backoff + ? 1.0 + : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(fixed_reconnect_backoff ? 0.0 + : GRPC_SUBCHANNEL_RECONNECT_JITTER) + .set_max_backoff(max_backoff_ms); + return backoff_options; +} + grpc_subchannel* grpc_subchannel_create(grpc_exec_ctx* exec_ctx, grpc_connector* connector, const grpc_subchannel_args* args) { @@ -334,43 +375,7 @@ grpc_subchannel* grpc_subchannel_create(grpc_exec_ctx* exec_ctx, grpc_schedule_on_exec_ctx); grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, "subchannel"); - int initial_backoff_ms = - GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000; - int min_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MIN_BACKOFF_SECONDS * 1000; - int max_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; - bool fixed_reconnect_backoff = false; - if (c->args) { - for (size_t i = 0; i < c->args->num_args; i++) { - if (0 == strcmp(c->args->args[i].key, - "grpc.testing.fixed_reconnect_backoff_ms")) { - fixed_reconnect_backoff = true; - initial_backoff_ms = min_backoff_ms = max_backoff_ms = - grpc_channel_arg_get_integer(&c->args->args[i], - {initial_backoff_ms, 100, INT_MAX}); - } else if (0 == strcmp(c->args->args[i].key, - GRPC_ARG_MIN_RECONNECT_BACKOFF_MS)) { - fixed_reconnect_backoff = false; - min_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], {min_backoff_ms, 100, INT_MAX}); - } else if (0 == strcmp(c->args->args[i].key, - GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) { - fixed_reconnect_backoff = false; - max_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], {max_backoff_ms, 100, INT_MAX}); - } else if (0 == strcmp(c->args->args[i].key, - GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) { - fixed_reconnect_backoff = false; - initial_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], {initial_backoff_ms, 100, INT_MAX}); - } - } - } - grpc_backoff_init( - &c->backoff_state, initial_backoff_ms, - fixed_reconnect_backoff ? 1.0 - : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER, - fixed_reconnect_backoff ? 0.0 : GRPC_SUBCHANNEL_RECONNECT_JITTER, - min_backoff_ms, max_backoff_ms); + c->backoff.Init(extract_backoff_options(args->args)); gpr_mu_init(&c->mu); return grpc_subchannel_index_register(exec_ctx, key, c); @@ -381,7 +386,7 @@ static void continue_connect_locked(grpc_exec_ctx* exec_ctx, grpc_connect_in_args args; args.interested_parties = c->pollset_set; - args.deadline = c->backoff_result.current_deadline; + args.deadline = c->next_attempt_deadline; args.channel_args = c->args; grpc_connectivity_state_set(exec_ctx, &c->state_tracker, @@ -429,7 +434,11 @@ static void on_alarm(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { } if (error == GRPC_ERROR_NONE) { gpr_log(GPR_INFO, "Failed to connect to channel, retrying"); - c->backoff_result = grpc_backoff_step(exec_ctx, &c->backoff_state); + const grpc_millis min_deadline = + (GRPC_SUBCHANNEL_RECONNECT_MIN_TIMEOUT_SECONDS * 1000) + + grpc_exec_ctx_now(exec_ctx); + c->next_attempt_deadline = + std::max(c->backoff->Step(exec_ctx), min_deadline); continue_connect_locked(exec_ctx, c); gpr_mu_unlock(&c->mu); } else { @@ -466,21 +475,21 @@ static void maybe_start_connecting_locked(grpc_exec_ctx* exec_ctx, if (!c->backoff_begun) { c->backoff_begun = true; - c->backoff_result = grpc_backoff_begin(exec_ctx, &c->backoff_state); + c->next_attempt_deadline = c->backoff->Begin(exec_ctx); continue_connect_locked(exec_ctx, c); } else { GPR_ASSERT(!c->have_alarm); c->have_alarm = true; const grpc_millis time_til_next = - c->backoff_result.next_attempt_start_time - grpc_exec_ctx_now(exec_ctx); + c->next_attempt_deadline - grpc_exec_ctx_now(exec_ctx); if (time_til_next <= 0) { gpr_log(GPR_INFO, "Retry immediately"); } else { gpr_log(GPR_INFO, "Retry in %" PRIdPTR " milliseconds", time_til_next); } GRPC_CLOSURE_INIT(&c->on_alarm, on_alarm, c, grpc_schedule_on_exec_ctx); - grpc_timer_init(exec_ctx, &c->alarm, - c->backoff_result.next_attempt_start_time, &c->on_alarm); + grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt_deadline, + &c->on_alarm); } } diff --git a/src/core/lib/backoff/backoff.cc b/src/core/lib/backoff/backoff.cc index dc754ddd82..5553e6b3f3 100644 --- a/src/core/lib/backoff/backoff.cc +++ b/src/core/lib/backoff/backoff.cc @@ -18,63 +18,54 @@ #include "src/core/lib/backoff/backoff.h" +#include <algorithm> + #include <grpc/support/useful.h> -void grpc_backoff_init(grpc_backoff* backoff, grpc_millis initial_backoff, - double multiplier, double jitter, - grpc_millis min_connect_timeout, - grpc_millis max_backoff) { - backoff->initial_backoff = initial_backoff; - backoff->multiplier = multiplier; - backoff->jitter = jitter; - backoff->min_connect_timeout = min_connect_timeout; - backoff->max_backoff = max_backoff; - backoff->rng_state = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec; -} +namespace grpc_core { -grpc_backoff_result grpc_backoff_begin(grpc_exec_ctx* exec_ctx, - grpc_backoff* backoff) { - backoff->current_backoff = backoff->initial_backoff; - const grpc_millis initial_timeout = - GPR_MAX(backoff->initial_backoff, backoff->min_connect_timeout); - const grpc_millis now = grpc_exec_ctx_now(exec_ctx); - const grpc_backoff_result result = {now + initial_timeout, - now + backoff->current_backoff}; - return result; -} +namespace { -/* Generate a random number between 0 and 1. */ -static double generate_uniform_random_number(uint32_t* rng_state) { - *rng_state = (1103515245 * *rng_state + 12345) % ((uint32_t)1 << 31); - return *rng_state / (double)((uint32_t)1 << 31); +/* Generate a random number between 0 and 1. We roll our own RNG because seeding + * rand() modifies a global variable we have no control over. */ +double generate_uniform_random_number(uint32_t* rng_state) { + constexpr uint32_t two_raise_31 = uint32_t(1) << 31; + *rng_state = (1103515245 * *rng_state + 12345) % two_raise_31; + return *rng_state / static_cast<double>(two_raise_31); } -static double generate_uniform_random_number_between(uint32_t* rng_state, - double a, double b) { +double generate_uniform_random_number_between(uint32_t* rng_state, double a, + double b) { if (a == b) return a; if (a > b) GPR_SWAP(double, a, b); // make sure a < b const double range = b - a; return a + generate_uniform_random_number(rng_state) * range; } +} // namespace -grpc_backoff_result grpc_backoff_step(grpc_exec_ctx* exec_ctx, - grpc_backoff* backoff) { - backoff->current_backoff = (grpc_millis)(GPR_MIN( - backoff->current_backoff * backoff->multiplier, backoff->max_backoff)); - const double jitter = generate_uniform_random_number_between( - &backoff->rng_state, -backoff->jitter * backoff->current_backoff, - backoff->jitter * backoff->current_backoff); - const grpc_millis current_timeout = - GPR_MAX((grpc_millis)(backoff->current_backoff + jitter), - backoff->min_connect_timeout); - const grpc_millis next_timeout = GPR_MIN( - (grpc_millis)(backoff->current_backoff + jitter), backoff->max_backoff); - const grpc_millis now = grpc_exec_ctx_now(exec_ctx); - const grpc_backoff_result result = {now + current_timeout, - now + next_timeout}; - return result; +BackOff::BackOff(const Options& options) : options_(options) { + rng_state_ = static_cast<uint32_t>(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); +} + +grpc_millis BackOff::Begin(grpc_exec_ctx* exec_ctx) { + current_backoff_ = options_.initial_backoff(); + return current_backoff_ + grpc_exec_ctx_now(exec_ctx); } -void grpc_backoff_reset(grpc_backoff* backoff) { - backoff->current_backoff = backoff->initial_backoff; +grpc_millis BackOff::Step(grpc_exec_ctx* exec_ctx) { + current_backoff_ = + (grpc_millis)(std::min(current_backoff_ * options_.multiplier(), + (double)options_.max_backoff())); + const double jitter = generate_uniform_random_number_between( + &rng_state_, -options_.jitter() * current_backoff_, + options_.jitter() * current_backoff_); + const grpc_millis next_timeout = std::min( + (grpc_millis)(current_backoff_ + jitter), options_.max_backoff()); + return next_timeout + grpc_exec_ctx_now(exec_ctx); } + +void BackOff::Reset() { current_backoff_ = options_.initial_backoff(); } + +void BackOff::SetRandomSeed(uint32_t seed) { rng_state_ = seed; } + +} // namespace grpc_core diff --git a/src/core/lib/backoff/backoff.h b/src/core/lib/backoff/backoff.h index 0da9082e70..6f2c30d42f 100644 --- a/src/core/lib/backoff/backoff.h +++ b/src/core/lib/backoff/backoff.h @@ -21,55 +21,69 @@ #include "src/core/lib/iomgr/exec_ctx.h" -typedef struct { - /// const: how long to wait after the first failure before retrying - grpc_millis initial_backoff; - - /// const: factor with which to multiply backoff after a failed retry - double multiplier; - - /// const: amount to randomize backoffs - double jitter; - - /// const: minimum time between retries - grpc_millis min_connect_timeout; - - /// const: maximum time between retries - grpc_millis max_backoff; - +namespace grpc_core { + +/// Implementation of the backoff mechanism described in +/// doc/connection-backoff.md +class BackOff { + public: + class Options; + + /// Initialize backoff machinery - does not need to be destroyed + explicit BackOff(const Options& options); + + /// Begin retry loop: returns the deadline to be used for the next attempt, + /// following the backoff strategy. + grpc_millis Begin(grpc_exec_ctx* exec_ctx); + /// Step a retry loop: returns the deadline to be used for the next attempt, + /// following the backoff strategy. + grpc_millis Step(grpc_exec_ctx* exec_ctx); + /// Reset the backoff, so the next grpc_backoff_step will be a + /// grpc_backoff_begin. + void Reset(); + + void SetRandomSeed(unsigned int seed); + + class Options { + public: + Options& set_initial_backoff(grpc_millis initial_backoff) { + initial_backoff_ = initial_backoff; + return *this; + } + Options& set_multiplier(double multiplier) { + multiplier_ = multiplier; + return *this; + } + Options& set_jitter(double jitter) { + jitter_ = jitter; + return *this; + } + Options& set_max_backoff(grpc_millis max_backoff) { + max_backoff_ = max_backoff; + return *this; + } + /// how long to wait after the first failure before retrying + grpc_millis initial_backoff() const { return initial_backoff_; } + /// factor with which to multiply backoff after a failed retry + double multiplier() const { return multiplier_; } + /// amount to randomize backoffs + double jitter() const { return jitter_; } + /// maximum time between retries + grpc_millis max_backoff() const { return max_backoff_; } + + private: + grpc_millis initial_backoff_; + double multiplier_; + double jitter_; + grpc_millis max_backoff_; + }; // class Options + + private: + const Options options_; /// current delay before retries - grpc_millis current_backoff; - - /// random number generator - uint32_t rng_state; -} grpc_backoff; - -typedef struct { - /// Deadline to be used for the current attempt. - grpc_millis current_deadline; - - /// Deadline to be used for the next attempt, following the backoff strategy. - grpc_millis next_attempt_start_time; -} grpc_backoff_result; - -/// Initialize backoff machinery - does not need to be destroyed -void grpc_backoff_init(grpc_backoff* backoff, grpc_millis initial_backoff, - double multiplier, double jitter, - grpc_millis min_connect_timeout, - grpc_millis max_backoff); - -/// Begin retry loop: returns the deadlines to be used for the current attempt -/// and the subsequent retry, if any. -grpc_backoff_result grpc_backoff_begin(grpc_exec_ctx* exec_ctx, - grpc_backoff* backoff); - -/// Step a retry loop: returns the deadlines to be used for the current attempt -/// and the subsequent retry, if any. -grpc_backoff_result grpc_backoff_step(grpc_exec_ctx* exec_ctx, - grpc_backoff* backoff); - -/// Reset the backoff, so the next grpc_backoff_step will be a -/// grpc_backoff_begin. -void grpc_backoff_reset(grpc_backoff* backoff); + grpc_millis current_backoff_; + uint32_t rng_state_; +}; +} // namespace grpc_core #endif /* GRPC_CORE_LIB_BACKOFF_BACKOFF_H */ diff --git a/test/core/backoff/BUILD b/test/core/backoff/BUILD index 4cd7acf066..6fbd6542d4 100644 --- a/test/core/backoff/BUILD +++ b/test/core/backoff/BUILD @@ -26,11 +26,14 @@ package( grpc_cc_test( name = "backoff_test", srcs = ["backoff_test.cc"], + external_deps = [ + "gtest", + ], language = "C++", deps = [ - "//:grpc", - "//test/core/util:grpc_test_util", "//:gpr", + "//:grpc", "//test/core/util:gpr_test_util", + "//test/core/util:grpc_test_util", ], ) diff --git a/test/core/backoff/backoff_test.cc b/test/core/backoff/backoff_test.cc index ef2de8d638..07e61bc11e 100644 --- a/test/core/backoff/backoff_test.cc +++ b/test/core/backoff/backoff_test.cc @@ -18,137 +18,135 @@ #include "src/core/lib/backoff/backoff.h" +#include <algorithm> + #include <grpc/support/log.h> #include <grpc/support/useful.h> +#include <gtest/gtest.h> #include "test/core/util/test_config.h" -static void test_constant_backoff(void) { - grpc_backoff backoff; +namespace grpc { +namespace testing { +namespace { + +using grpc_core::BackOff; + +TEST(BackOffTest, ConstantBackOff) { const grpc_millis initial_backoff = 200; const double multiplier = 1.0; const double jitter = 0.0; - const grpc_millis min_connect_timeout = 100; const grpc_millis max_backoff = 1000; - grpc_backoff_init(&backoff, initial_backoff, multiplier, jitter, - min_connect_timeout, max_backoff); + BackOff::Options options; + options.set_initial_backoff(initial_backoff) + .set_multiplier(multiplier) + .set_jitter(jitter) + .set_max_backoff(max_backoff); + BackOff backoff(options); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_backoff_result next_deadlines = grpc_backoff_begin(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline - grpc_exec_ctx_now(&exec_ctx) == - initial_backoff); - GPR_ASSERT(next_deadlines.next_attempt_start_time - - grpc_exec_ctx_now(&exec_ctx) == - initial_backoff); + grpc_millis next_attempt_start_time = backoff.Begin(&exec_ctx); + EXPECT_EQ(next_attempt_start_time - grpc_exec_ctx_now(&exec_ctx), + initial_backoff); for (int i = 0; i < 10000; i++) { - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline - grpc_exec_ctx_now(&exec_ctx) == - initial_backoff); - GPR_ASSERT(next_deadlines.next_attempt_start_time - - grpc_exec_ctx_now(&exec_ctx) == - initial_backoff); - exec_ctx.now = next_deadlines.current_deadline; + next_attempt_start_time = backoff.Step(&exec_ctx); + EXPECT_EQ(next_attempt_start_time - grpc_exec_ctx_now(&exec_ctx), + initial_backoff); } grpc_exec_ctx_finish(&exec_ctx); } -static void test_min_connect(void) { - grpc_backoff backoff; +TEST(BackOffTest, MinConnect) { const grpc_millis initial_backoff = 100; const double multiplier = 1.0; const double jitter = 0.0; - const grpc_millis min_connect_timeout = 200; const grpc_millis max_backoff = 1000; - grpc_backoff_init(&backoff, initial_backoff, multiplier, jitter, - min_connect_timeout, max_backoff); + BackOff::Options options; + options.set_initial_backoff(initial_backoff) + .set_multiplier(multiplier) + .set_jitter(jitter) + .set_max_backoff(max_backoff); + BackOff backoff(options); grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_backoff_result next = grpc_backoff_begin(&exec_ctx, &backoff); - // Because the min_connect_timeout > initial_backoff, current_deadline is used - // as the deadline for the current attempt. - GPR_ASSERT(next.current_deadline - grpc_exec_ctx_now(&exec_ctx) == - min_connect_timeout); - // ... while, if the current attempt fails, the next one will happen after - // initial_backoff. - GPR_ASSERT(next.next_attempt_start_time - grpc_exec_ctx_now(&exec_ctx) == - initial_backoff); + grpc_millis next = backoff.Begin(&exec_ctx); + EXPECT_EQ(next - grpc_exec_ctx_now(&exec_ctx), initial_backoff); grpc_exec_ctx_finish(&exec_ctx); } -static void test_no_jitter_backoff(void) { - grpc_backoff backoff; +TEST(BackOffTest, NoJitterBackOff) { const grpc_millis initial_backoff = 2; const double multiplier = 2.0; const double jitter = 0.0; - const grpc_millis min_connect_timeout = 1; const grpc_millis max_backoff = 513; - grpc_backoff_init(&backoff, initial_backoff, multiplier, jitter, - min_connect_timeout, max_backoff); + BackOff::Options options; + options.set_initial_backoff(initial_backoff) + .set_multiplier(multiplier) + .set_jitter(jitter) + .set_max_backoff(max_backoff); + BackOff backoff(options); // x_1 = 2 // x_n = 2**i + x_{i-1} ( = 2**(n+1) - 2 ) grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; exec_ctx.now = 0; exec_ctx.now_is_valid = true; - grpc_backoff_result next_deadlines = grpc_backoff_begin(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == - next_deadlines.next_attempt_start_time); - GPR_ASSERT(next_deadlines.current_deadline == 2); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 6); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 14); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 30); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 62); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 126); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 254); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 510); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 1022); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); + grpc_millis next = backoff.Begin(&exec_ctx); + EXPECT_EQ(next, 2); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 6); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 14); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 30); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 62); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 126); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 254); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 510); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 1022); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); // Hit the maximum timeout. From this point onwards, retries will increase // only by max timeout. - GPR_ASSERT(next_deadlines.current_deadline == 1535); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 2048); - exec_ctx.now = next_deadlines.current_deadline; - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline == 2561); + EXPECT_EQ(next, 1535); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 2048); + exec_ctx.now = next; + next = backoff.Step(&exec_ctx); + EXPECT_EQ(next, 2561); grpc_exec_ctx_finish(&exec_ctx); } -static void test_jitter_backoff(void) { +TEST(BackOffTest, JitterBackOff) { const grpc_millis initial_backoff = 500; grpc_millis current_backoff = initial_backoff; const grpc_millis max_backoff = 1000; - const grpc_millis min_connect_timeout = 100; const double multiplier = 1.0; const double jitter = 0.1; - grpc_backoff backoff; - grpc_backoff_init(&backoff, initial_backoff, multiplier, jitter, - min_connect_timeout, max_backoff); + BackOff::Options options; + options.set_initial_backoff(initial_backoff) + .set_multiplier(multiplier) + .set_jitter(jitter) + .set_max_backoff(max_backoff); + BackOff backoff(options); - backoff.rng_state = 0; // force consistent PRNG + backoff.SetRandomSeed(0); // force consistent PRNG grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_backoff_result next_deadlines = grpc_backoff_begin(&exec_ctx, &backoff); - GPR_ASSERT(next_deadlines.current_deadline - grpc_exec_ctx_now(&exec_ctx) == - initial_backoff); - GPR_ASSERT(next_deadlines.next_attempt_start_time - - grpc_exec_ctx_now(&exec_ctx) == - initial_backoff); + grpc_millis next = backoff.Begin(&exec_ctx); + EXPECT_EQ(next - grpc_exec_ctx_now(&exec_ctx), initial_backoff); grpc_millis expected_next_lower_bound = (grpc_millis)((double)current_backoff * (1 - jitter)); @@ -156,32 +154,28 @@ static void test_jitter_backoff(void) { (grpc_millis)((double)current_backoff * (1 + jitter)); for (int i = 0; i < 10000; i++) { - next_deadlines = grpc_backoff_step(&exec_ctx, &backoff); + next = backoff.Step(&exec_ctx); // next-now must be within (jitter*100)% of the current backoff (which // increases by * multiplier up to max_backoff). - const grpc_millis timeout_millis = - next_deadlines.current_deadline - grpc_exec_ctx_now(&exec_ctx); - GPR_ASSERT(timeout_millis >= expected_next_lower_bound); - GPR_ASSERT(timeout_millis <= expected_next_upper_bound); - current_backoff = GPR_MIN( + const grpc_millis timeout_millis = next - grpc_exec_ctx_now(&exec_ctx); + EXPECT_GE(timeout_millis, expected_next_lower_bound); + EXPECT_LE(timeout_millis, expected_next_upper_bound); + current_backoff = std::min( (grpc_millis)((double)current_backoff * multiplier), max_backoff); expected_next_lower_bound = (grpc_millis)((double)current_backoff * (1 - jitter)); expected_next_upper_bound = (grpc_millis)((double)current_backoff * (1 + jitter)); - exec_ctx.now = next_deadlines.current_deadline; } grpc_exec_ctx_finish(&exec_ctx); } +} // namespace +} // namespace testing +} // namespace grpc + int main(int argc, char** argv) { grpc_test_init(argc, argv); - gpr_time_init(); - - test_constant_backoff(); - test_min_connect(); - test_no_jitter_backoff(); - test_jitter_backoff(); - - return 0; + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index 3958d8e9e4..e934df3a18 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -104,23 +104,6 @@ "gpr", "gpr_test_util", "grpc", - "grpc_test_util" - ], - "headers": [], - "is_filegroup": false, - "language": "c", - "name": "backoff_test", - "src": [ - "test/core/backoff/backoff_test.cc" - ], - "third_party": false, - "type": "target" - }, - { - "deps": [ - "gpr", - "gpr_test_util", - "grpc", "grpc_test_util", "test_tcp_server" ], @@ -2541,6 +2524,23 @@ "gpr", "gpr_test_util", "grpc", + "grpc_test_util" + ], + "headers": [], + "is_filegroup": false, + "language": "c++", + "name": "backoff_test", + "src": [ + "test/core/backoff/backoff_test.cc" + ], + "third_party": false, + "type": "target" + }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", "grpc++", "grpc++_test_util", "grpc_test_util" diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 6ffc7f412d..908bcec2ad 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -132,30 +132,6 @@ ], "cpu_cost": 1.0, "exclude_configs": [], - "exclude_iomgrs": [], - "flaky": false, - "gtest": false, - "language": "c", - "name": "backoff_test", - "platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "uses_polling": false - }, - { - "args": [], - "benchmark": false, - "ci_platforms": [ - "linux", - "mac", - "posix", - "windows" - ], - "cpu_cost": 1.0, - "exclude_configs": [], "exclude_iomgrs": [ "uv" ], @@ -2974,6 +2950,30 @@ "flaky": false, "gtest": false, "language": "c++", + "name": "backoff_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": false + }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c++", "name": "bdp_estimator_test", "platforms": [ "linux", |