diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index 6b380bf87e5..28db2db0b48 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -163,9 +163,11 @@ typedef struct { /** Maximum message length that the channel can send. Int valued, bytes. -1 means unlimited. */ #define GRPC_ARG_MAX_SEND_MESSAGE_LENGTH "grpc.max_send_message_length" - +/** Maximum time that a channel may exist. Int valued, seconds. INT_MAX means + unlimited. */ #define GPRC_ARG_MAX_CONNECION_AGE_S "grpc.max_connection_age" - +/** Grace period after the chennel reaches its max age. Int valued, seconds. + INT_MAX means unlimited. */ #define GPRC_ARG_MAX_CONNECION_AGE_GRACE_S "grpc.max_connection_age_grace" /** Initial sequence number for http2 transports. Int valued. */ #define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \ diff --git a/src/core/lib/channel/max_age_filter.c b/src/core/lib/channel/max_age_filter.c index f840483c722..364e01705d2 100644 --- a/src/core/lib/channel/max_age_filter.c +++ b/src/core/lib/channel/max_age_filter.c @@ -41,6 +41,7 @@ #include "src/core/lib/transport/service_config.h" #define DEFAULT_MAX_CONNECTION_AGE_S INT_MAX +#define DEFAULT_MAX_CONNECTION_AGE_GRACE_S INT_MAX typedef struct channel_data { // We take a reference to the channel stack for the timer callback @@ -49,19 +50,31 @@ typedef struct channel_data { gpr_mu max_age_timer_mu; // True if the max_age timer callback is currently pending bool max_age_timer_pending; + // True if the max_age timer callback is currently pending + bool max_age_grace_timer_pending; // The timer for checking if the channel has reached its max age grpc_timer max_age_timer; + // The timer for checking if the channel has reached its max age + grpc_timer max_age_grace_timer; // Allowed max time a channel may exist gpr_timespec max_connection_age; + // Allowed grace period after the channel reaches its max age + gpr_timespec max_connection_age_grace; // Closure to run when the channel reaches its max age and should be closed + // gracefully grpc_closure close_max_age_channel; - // Closure to run when the init fo channel stack is done and the timer should - // be started - grpc_closure start_timer_after_init; + // Closure to run the channel uses up its max age grace time and should be + // closed forcibly + grpc_closure force_close_max_age_channel; + // Closure to run when the init fo channel stack is done and the max_age timer + // should be started + grpc_closure start_max_age_timer_after_init; + // Closure to run when the goaway op is finished and the max_age_timer + grpc_closure start_max_age_grace_timer_after_goaway_op; } channel_data; -static void start_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { +static void start_max_age_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { channel_data* chand = arg; gpr_mu_lock(&chand->max_age_timer_mu); chand->max_age_timer_pending = true; @@ -71,7 +84,23 @@ static void start_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, &chand->close_max_age_channel, gpr_now(GPR_CLOCK_MONOTONIC)); gpr_mu_unlock(&chand->max_age_timer_mu); GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, - "max_age start_timer_after_init"); + "max_age start_max_age_timer_after_init"); +} + +static void start_max_age_grace_timer_after_goaway_op(grpc_exec_ctx* exec_ctx, + void* arg, + grpc_error* error) { + channel_data* chand = arg; + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_grace_timer_pending = true; + grpc_timer_init(exec_ctx, &chand->max_age_grace_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + chand->max_connection_age_grace), + &chand->force_close_max_age_channel, + gpr_now(GPR_CLOCK_MONOTONIC)); + gpr_mu_unlock(&chand->max_age_timer_mu); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age start_max_age_grace_timer_after_goaway_op"); } static void close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, @@ -81,7 +110,10 @@ static void close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, chand->max_age_timer_pending = false; gpr_mu_unlock(&chand->max_age_timer_mu); if (error == GRPC_ERROR_NONE) { - grpc_transport_op* op = grpc_make_transport_op(NULL); + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_age_grace_timer_after_goaway_op"); + grpc_transport_op* op = grpc_make_transport_op( + &chand->start_max_age_grace_timer_after_goaway_op); op->goaway_error = grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_age"), GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); @@ -93,6 +125,24 @@ static void close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, } } +static void force_close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = arg; + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_grace_timer_pending = false; + gpr_mu_unlock(&chand->max_age_timer_mu); + if (error == GRPC_ERROR_NONE) { + grpc_transport_op* op = grpc_make_transport_op(NULL); + op->disconnect_with_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel reaches max age"); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(exec_ctx, elem, op); + } else if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("force_close_max_age_channel", error); + } +} + // Constructor for call_data. static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, @@ -115,11 +165,17 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, channel_data* chand = elem->channel_data; gpr_mu_init(&chand->max_age_timer_mu); chand->max_age_timer_pending = false; + chand->max_age_grace_timer_pending = false; chand->channel_stack = args->channel_stack; chand->max_connection_age = DEFAULT_MAX_CONNECTION_AGE_S == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_seconds(DEFAULT_MAX_CONNECTION_AGE_S, GPR_TIMESPAN); + chand->max_connection_age = + DEFAULT_MAX_CONNECTION_AGE_GRACE_S == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_seconds(DEFAULT_MAX_CONNECTION_AGE_GRACE_S, + GPR_TIMESPAN); for (size_t i = 0; i < args->channel_args->num_args; ++i) { if (0 == strcmp(args->channel_args->args[i].key, GPRC_ARG_MAX_CONNECION_AGE_S)) { @@ -129,10 +185,28 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, chand->max_connection_age = value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_seconds(value, GPR_TIMESPAN); + } else if (0 == strcmp(args->channel_args->args[i].key, + GPRC_ARG_MAX_CONNECION_AGE_GRACE_S)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], + (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_GRACE_S, 1, + INT_MAX}); + chand->max_connection_age_grace = + value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_seconds(value, GPR_TIMESPAN); } } grpc_closure_init(&chand->close_max_age_channel, close_max_age_channel, chand, grpc_schedule_on_exec_ctx); + grpc_closure_init(&chand->force_close_max_age_channel, + force_close_max_age_channel, chand, + grpc_schedule_on_exec_ctx); + grpc_closure_init(&chand->start_max_age_timer_after_init, + start_max_age_timer_after_init, chand, + grpc_schedule_on_exec_ctx); + grpc_closure_init(&chand->start_max_age_grace_timer_after_goaway_op, + start_max_age_grace_timer_after_goaway_op, chand, + grpc_schedule_on_exec_ctx); if (gpr_time_cmp(chand->max_connection_age, gpr_inf_future(GPR_TIMESPAN)) != 0) { @@ -144,10 +218,8 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, // timer, and we schedule that closure to be run after call stack // initialization is done. GRPC_CHANNEL_STACK_REF(chand->channel_stack, - "max_age start_timer_after_init"); - grpc_closure_init(&chand->start_timer_after_init, start_timer_after_init, - chand, grpc_schedule_on_exec_ctx); - grpc_closure_sched(exec_ctx, &chand->start_timer_after_init, + "max_age start_max_age_timer_after_init"); + grpc_closure_sched(exec_ctx, &chand->start_max_age_timer_after_init, GRPC_ERROR_NONE); } @@ -162,6 +234,9 @@ static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, if (chand->max_age_timer_pending) { grpc_timer_cancel(exec_ctx, &chand->max_age_timer); } + if (chand->max_age_grace_timer_pending) { + grpc_timer_cancel(exec_ctx, &chand->max_age_grace_timer); + } gpr_mu_unlock(&chand->max_age_timer_mu); }