|
|
|
@ -62,8 +62,14 @@ typedef struct servers_fixture { |
|
|
|
|
grpc_metadata_array *request_metadata_recv; |
|
|
|
|
} servers_fixture; |
|
|
|
|
|
|
|
|
|
typedef struct request_sequences { |
|
|
|
|
size_t n; |
|
|
|
|
int *connections; |
|
|
|
|
int *connectivity_states; |
|
|
|
|
} request_sequences; |
|
|
|
|
|
|
|
|
|
typedef void (*verifier_fn)(const servers_fixture *, grpc_channel *, |
|
|
|
|
const int *, const size_t); |
|
|
|
|
const request_sequences *, const size_t); |
|
|
|
|
|
|
|
|
|
typedef struct test_spec { |
|
|
|
|
size_t num_iters; |
|
|
|
@ -227,9 +233,24 @@ static void teardown_servers(servers_fixture *f) { |
|
|
|
|
gpr_free(f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static request_sequences request_sequences_create(size_t n) { |
|
|
|
|
request_sequences res; |
|
|
|
|
res.n = n; |
|
|
|
|
res.connections = gpr_malloc(sizeof(*res.connections) * n); |
|
|
|
|
res.connectivity_states = gpr_malloc(sizeof(*res.connectivity_states) * n); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void request_sequences_destroy(const request_sequences *rseqs) { |
|
|
|
|
gpr_free(rseqs->connections); |
|
|
|
|
gpr_free(rseqs->connectivity_states); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Returns connection sequence (server indices), which must be freed */ |
|
|
|
|
static int *perform_request(servers_fixture *f, grpc_channel *client, |
|
|
|
|
request_data *rdata, const test_spec *spec) { |
|
|
|
|
static request_sequences perform_request(servers_fixture *f, |
|
|
|
|
grpc_channel *client, |
|
|
|
|
request_data *rdata, |
|
|
|
|
const test_spec *spec) { |
|
|
|
|
grpc_call *c; |
|
|
|
|
int s_idx; |
|
|
|
|
int *s_valid; |
|
|
|
@ -239,11 +260,10 @@ static int *perform_request(servers_fixture *f, grpc_channel *client, |
|
|
|
|
size_t i, iter_num; |
|
|
|
|
grpc_event ev; |
|
|
|
|
int read_tag; |
|
|
|
|
int *connection_sequence; |
|
|
|
|
int completed_client; |
|
|
|
|
const request_sequences sequences = request_sequences_create(spec->num_iters); |
|
|
|
|
|
|
|
|
|
s_valid = gpr_malloc(sizeof(int) * f->num_servers); |
|
|
|
|
connection_sequence = gpr_malloc(sizeof(int) * spec->num_iters); |
|
|
|
|
|
|
|
|
|
for (iter_num = 0; iter_num < spec->num_iters; iter_num++) { |
|
|
|
|
cq_verifier *cqv = cq_verifier_create(f->cq); |
|
|
|
@ -260,7 +280,7 @@ static int *perform_request(servers_fixture *f, grpc_channel *client, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
connection_sequence[iter_num] = -1; |
|
|
|
|
sequences.connections[iter_num] = -1; |
|
|
|
|
grpc_metadata_array_init(&rdata->initial_metadata_recv); |
|
|
|
|
grpc_metadata_array_init(&rdata->trailing_metadata_recv); |
|
|
|
|
|
|
|
|
@ -305,12 +325,14 @@ static int *perform_request(servers_fixture *f, grpc_channel *client, |
|
|
|
|
grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL)); |
|
|
|
|
|
|
|
|
|
s_idx = -1; |
|
|
|
|
while ( |
|
|
|
|
(ev = grpc_completion_queue_next( |
|
|
|
|
f->cq, GRPC_TIMEOUT_MILLIS_TO_DEADLINE(10 * RETRY_TIMEOUT), NULL)) |
|
|
|
|
.type != GRPC_QUEUE_TIMEOUT) { |
|
|
|
|
while ((ev = grpc_completion_queue_next( |
|
|
|
|
f->cq, GRPC_TIMEOUT_MILLIS_TO_DEADLINE(RETRY_TIMEOUT), NULL)) |
|
|
|
|
.type != GRPC_QUEUE_TIMEOUT) { |
|
|
|
|
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE); |
|
|
|
|
read_tag = ((int)(intptr_t)ev.tag); |
|
|
|
|
const grpc_connectivity_state conn_state = |
|
|
|
|
grpc_channel_check_connectivity_state(client, 0); |
|
|
|
|
sequences.connectivity_states[iter_num] = conn_state; |
|
|
|
|
gpr_log(GPR_DEBUG, "EVENT: success:%d, type:%d, tag:%d iter:%" PRIuPTR, |
|
|
|
|
ev.success, ev.type, read_tag, iter_num); |
|
|
|
|
if (ev.success && read_tag >= 1000) { |
|
|
|
@ -318,7 +340,7 @@ static int *perform_request(servers_fixture *f, grpc_channel *client, |
|
|
|
|
/* only server notifications for non-shutdown events */ |
|
|
|
|
s_idx = read_tag - 1000; |
|
|
|
|
s_valid[s_idx] = 1; |
|
|
|
|
connection_sequence[iter_num] = s_idx; |
|
|
|
|
sequences.connections[iter_num] = s_idx; |
|
|
|
|
break; |
|
|
|
|
} else if (read_tag == 1) { |
|
|
|
|
gpr_log(GPR_DEBUG, "client timed out"); |
|
|
|
@ -381,10 +403,9 @@ static int *perform_request(servers_fixture *f, grpc_channel *client, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
GPR_ASSERT( |
|
|
|
|
grpc_completion_queue_next( |
|
|
|
|
f->cq, GRPC_TIMEOUT_MILLIS_TO_DEADLINE(2 * RETRY_TIMEOUT), NULL) |
|
|
|
|
.type == GRPC_QUEUE_TIMEOUT); |
|
|
|
|
GPR_ASSERT(grpc_completion_queue_next( |
|
|
|
|
f->cq, GRPC_TIMEOUT_MILLIS_TO_DEADLINE(RETRY_TIMEOUT), NULL) |
|
|
|
|
.type == GRPC_QUEUE_TIMEOUT); |
|
|
|
|
|
|
|
|
|
grpc_metadata_array_destroy(&rdata->initial_metadata_recv); |
|
|
|
|
grpc_metadata_array_destroy(&rdata->trailing_metadata_recv); |
|
|
|
@ -401,7 +422,7 @@ static int *perform_request(servers_fixture *f, grpc_channel *client, |
|
|
|
|
|
|
|
|
|
gpr_free(s_valid); |
|
|
|
|
|
|
|
|
|
return connection_sequence; |
|
|
|
|
return sequences; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static grpc_call **perform_multirequest(servers_fixture *f, |
|
|
|
@ -441,62 +462,10 @@ static grpc_call **perform_multirequest(servers_fixture *f, |
|
|
|
|
return calls; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void assert_channel_connectivity(grpc_channel *ch, |
|
|
|
|
size_t num_accepted_conn_states, |
|
|
|
|
int accepted_conn_state, ...) { |
|
|
|
|
size_t i; |
|
|
|
|
grpc_channel_stack *client_stack; |
|
|
|
|
grpc_channel_element *client_channel_filter; |
|
|
|
|
grpc_connectivity_state actual_conn_state; |
|
|
|
|
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; |
|
|
|
|
va_list ap; |
|
|
|
|
|
|
|
|
|
client_stack = grpc_channel_get_channel_stack(ch); |
|
|
|
|
client_channel_filter = grpc_channel_stack_last_element(client_stack); |
|
|
|
|
|
|
|
|
|
actual_conn_state = grpc_client_channel_check_connectivity_state( |
|
|
|
|
&exec_ctx, client_channel_filter, 0 /* don't try to connect */); |
|
|
|
|
grpc_exec_ctx_finish(&exec_ctx); |
|
|
|
|
va_start(ap, accepted_conn_state); |
|
|
|
|
for (i = 0; i < num_accepted_conn_states; i++) { |
|
|
|
|
if ((int)actual_conn_state == accepted_conn_state) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
accepted_conn_state = va_arg(ap, grpc_connectivity_state); |
|
|
|
|
} |
|
|
|
|
va_end(ap); |
|
|
|
|
if (i == num_accepted_conn_states) { |
|
|
|
|
char **accepted_strs = |
|
|
|
|
gpr_malloc(sizeof(char *) * num_accepted_conn_states); |
|
|
|
|
char *accepted_str_joined; |
|
|
|
|
va_start(ap, accepted_conn_state); |
|
|
|
|
for (i = 0; i < num_accepted_conn_states; i++) { |
|
|
|
|
GPR_ASSERT(gpr_asprintf(&accepted_strs[i], "%d", accepted_conn_state) > |
|
|
|
|
0); |
|
|
|
|
accepted_conn_state = va_arg(ap, grpc_connectivity_state); |
|
|
|
|
} |
|
|
|
|
va_end(ap); |
|
|
|
|
accepted_str_joined = gpr_strjoin_sep((const char **)accepted_strs, |
|
|
|
|
num_accepted_conn_states, ", ", NULL); |
|
|
|
|
gpr_log( |
|
|
|
|
GPR_ERROR, |
|
|
|
|
"Channel connectivity assertion failed: expected <one of [%s]>, got %d", |
|
|
|
|
accepted_str_joined, actual_conn_state); |
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_accepted_conn_states; i++) { |
|
|
|
|
gpr_free(accepted_strs[i]); |
|
|
|
|
} |
|
|
|
|
gpr_free(accepted_strs); |
|
|
|
|
gpr_free(accepted_str_joined); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void run_spec(const test_spec *spec) { |
|
|
|
|
grpc_channel *client; |
|
|
|
|
char *client_hostport; |
|
|
|
|
char *servers_hostports_str; |
|
|
|
|
int *actual_connection_sequence; |
|
|
|
|
request_data rdata; |
|
|
|
|
servers_fixture *f; |
|
|
|
|
grpc_channel_args args; |
|
|
|
@ -524,14 +493,14 @@ void run_spec(const test_spec *spec) { |
|
|
|
|
gpr_log(GPR_INFO, "Testing '%s' with servers=%s client=%s", spec->description, |
|
|
|
|
servers_hostports_str, client_hostport); |
|
|
|
|
|
|
|
|
|
actual_connection_sequence = perform_request(f, client, &rdata, spec); |
|
|
|
|
const request_sequences sequences = perform_request(f, client, &rdata, spec); |
|
|
|
|
|
|
|
|
|
spec->verifier(f, client, actual_connection_sequence, spec->num_iters); |
|
|
|
|
spec->verifier(f, client, &sequences, spec->num_iters); |
|
|
|
|
|
|
|
|
|
gpr_free(client_hostport); |
|
|
|
|
gpr_free(servers_hostports_str); |
|
|
|
|
gpr_free(actual_connection_sequence); |
|
|
|
|
gpr_free(rdata.call_details); |
|
|
|
|
request_sequences_destroy(&sequences); |
|
|
|
|
|
|
|
|
|
grpc_channel_destroy(client); /* calls the LB's shutdown func */ |
|
|
|
|
teardown_servers(f); |
|
|
|
@ -684,29 +653,43 @@ static void print_failed_expectations(const int *expected_connection_sequence, |
|
|
|
|
|
|
|
|
|
static void verify_vanilla_round_robin(const servers_fixture *f, |
|
|
|
|
grpc_channel *client, |
|
|
|
|
const int *actual_connection_sequence, |
|
|
|
|
const request_sequences *sequences, |
|
|
|
|
const size_t num_iters) { |
|
|
|
|
int *expected_connection_sequence; |
|
|
|
|
size_t i; |
|
|
|
|
const size_t expected_seq_length = f->num_servers; |
|
|
|
|
|
|
|
|
|
/* verify conn. seq. expectation */ |
|
|
|
|
/* get the first sequence of "num_servers" elements */ |
|
|
|
|
expected_connection_sequence = gpr_malloc(sizeof(int) * expected_seq_length); |
|
|
|
|
memcpy(expected_connection_sequence, actual_connection_sequence, |
|
|
|
|
int *expected_connection_sequence = |
|
|
|
|
gpr_malloc(sizeof(int) * expected_seq_length); |
|
|
|
|
memcpy(expected_connection_sequence, sequences->connections, |
|
|
|
|
sizeof(int) * expected_seq_length); |
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_iters; i++) { |
|
|
|
|
const int actual = actual_connection_sequence[i]; |
|
|
|
|
for (size_t i = 0; i < num_iters; i++) { |
|
|
|
|
const int actual = sequences->connections[i]; |
|
|
|
|
const int expected = expected_connection_sequence[i % expected_seq_length]; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
print_failed_expectations(expected_connection_sequence, |
|
|
|
|
actual_connection_sequence, expected_seq_length, |
|
|
|
|
num_iters); |
|
|
|
|
gpr_log( |
|
|
|
|
GPR_ERROR, |
|
|
|
|
"CONNECTION SEQUENCE FAILURE: expected %d, got %d at iteration #%d", |
|
|
|
|
expected, actual, (int)i); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* All servers are available, therefore all client subchannels are READY, even
|
|
|
|
|
* when we only need one for the client channel state to be READY */ |
|
|
|
|
for (size_t i = 0; i < sequences->n; i++) { |
|
|
|
|
const grpc_connectivity_state actual = sequences->connectivity_states[i]; |
|
|
|
|
const grpc_connectivity_state expected = GRPC_CHANNEL_READY; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' " |
|
|
|
|
"at iteration #%d", |
|
|
|
|
grpc_connectivity_state_name(expected), |
|
|
|
|
grpc_connectivity_state_name(actual), (int)i); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
assert_channel_connectivity(client, 1, GRPC_CHANNEL_READY); |
|
|
|
|
|
|
|
|
|
gpr_free(expected_connection_sequence); |
|
|
|
|
} |
|
|
|
@ -715,7 +698,7 @@ static void verify_vanilla_round_robin(const servers_fixture *f, |
|
|
|
|
* given in "f") are killed */ |
|
|
|
|
static void verify_vanishing_floor_round_robin( |
|
|
|
|
const servers_fixture *f, grpc_channel *client, |
|
|
|
|
const int *actual_connection_sequence, const size_t num_iters) { |
|
|
|
|
const request_sequences *sequences, const size_t num_iters) { |
|
|
|
|
int *expected_connection_sequence; |
|
|
|
|
const size_t expected_seq_length = 2; |
|
|
|
|
size_t i; |
|
|
|
@ -723,57 +706,83 @@ static void verify_vanishing_floor_round_robin( |
|
|
|
|
/* verify conn. seq. expectation */ |
|
|
|
|
/* copy the first full sequence (without -1s) */ |
|
|
|
|
expected_connection_sequence = gpr_malloc(sizeof(int) * expected_seq_length); |
|
|
|
|
memcpy(expected_connection_sequence, actual_connection_sequence + 2, |
|
|
|
|
memcpy(expected_connection_sequence, sequences->connections + 2, |
|
|
|
|
expected_seq_length * sizeof(int)); |
|
|
|
|
|
|
|
|
|
/* first two elements of the sequence should be [0 (1st server), -1 (failure)]
|
|
|
|
|
*/ |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[0] == 0); |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[1] == -1); |
|
|
|
|
GPR_ASSERT(sequences->connections[0] == 0); |
|
|
|
|
GPR_ASSERT(sequences->connections[1] == -1); |
|
|
|
|
|
|
|
|
|
/* the next two element must be [3, 0], repeating from that point: the 3 is
|
|
|
|
|
* brought forth by servers 1 and 2 disappearing after the intial pick of 0 */ |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[2] == 3); |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[3] == 0); |
|
|
|
|
GPR_ASSERT(sequences->connections[2] == 3); |
|
|
|
|
GPR_ASSERT(sequences->connections[3] == 0); |
|
|
|
|
|
|
|
|
|
/* make sure that the expectation obliges */ |
|
|
|
|
for (i = 2; i < num_iters; i++) { |
|
|
|
|
const int actual = actual_connection_sequence[i]; |
|
|
|
|
const int actual = sequences->connections[i]; |
|
|
|
|
const int expected = expected_connection_sequence[i % expected_seq_length]; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
print_failed_expectations(expected_connection_sequence, |
|
|
|
|
actual_connection_sequence, expected_seq_length, |
|
|
|
|
sequences->connections, expected_seq_length, |
|
|
|
|
num_iters); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* There's always at least one subchannel READY (connected), therefore the
|
|
|
|
|
* overall state of the client channel is READY at all times. */ |
|
|
|
|
for (i = 0; i < sequences->n; i++) { |
|
|
|
|
const grpc_connectivity_state actual = sequences->connectivity_states[i]; |
|
|
|
|
const grpc_connectivity_state expected = GRPC_CHANNEL_READY; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' " |
|
|
|
|
"at iteration #%d", |
|
|
|
|
grpc_connectivity_state_name(expected), |
|
|
|
|
grpc_connectivity_state_name(actual), (int)i); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gpr_free(expected_connection_sequence); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void verify_total_carnage_round_robin( |
|
|
|
|
const servers_fixture *f, grpc_channel *client, |
|
|
|
|
const int *actual_connection_sequence, const size_t num_iters) { |
|
|
|
|
size_t i; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_iters; i++) { |
|
|
|
|
const int actual = actual_connection_sequence[i]; |
|
|
|
|
static void verify_total_carnage_round_robin(const servers_fixture *f, |
|
|
|
|
grpc_channel *client, |
|
|
|
|
const request_sequences *sequences, |
|
|
|
|
const size_t num_iters) { |
|
|
|
|
for (size_t i = 0; i < num_iters; i++) { |
|
|
|
|
const int actual = sequences->connections[i]; |
|
|
|
|
const int expected = -1; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, "FAILURE: expected %d, actual %d at iter %" PRIuPTR, |
|
|
|
|
expected, actual, i); |
|
|
|
|
gpr_log( |
|
|
|
|
GPR_ERROR, |
|
|
|
|
"CONNECTION SEQUENCE FAILURE: expected %d, got %d at iteration #%d", |
|
|
|
|
expected, actual, (int)i); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* even though we know all the servers are dead, the client is still trying
|
|
|
|
|
* retrying, believing it's in a transient failure situation */ |
|
|
|
|
assert_channel_connectivity(client, 2, GRPC_CHANNEL_TRANSIENT_FAILURE, |
|
|
|
|
GRPC_CHANNEL_CONNECTING); |
|
|
|
|
/* no server is ever available. The persistent state is TRANSIENT_FAILURE */ |
|
|
|
|
for (size_t i = 0; i < sequences->n; i++) { |
|
|
|
|
const grpc_connectivity_state actual = sequences->connectivity_states[i]; |
|
|
|
|
const grpc_connectivity_state expected = GRPC_CHANNEL_TRANSIENT_FAILURE; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' " |
|
|
|
|
"at iteration #%d", |
|
|
|
|
grpc_connectivity_state_name(expected), |
|
|
|
|
grpc_connectivity_state_name(actual), (int)i); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void verify_partial_carnage_round_robin( |
|
|
|
|
const servers_fixture *f, grpc_channel *client, |
|
|
|
|
const int *actual_connection_sequence, const size_t num_iters) { |
|
|
|
|
const request_sequences *sequences, const size_t num_iters) { |
|
|
|
|
int *expected_connection_sequence; |
|
|
|
|
size_t i; |
|
|
|
|
const size_t expected_seq_length = f->num_servers; |
|
|
|
@ -781,15 +790,15 @@ static void verify_partial_carnage_round_robin( |
|
|
|
|
/* verify conn. seq. expectation */ |
|
|
|
|
/* get the first sequence of "num_servers" elements */ |
|
|
|
|
expected_connection_sequence = gpr_malloc(sizeof(int) * expected_seq_length); |
|
|
|
|
memcpy(expected_connection_sequence, actual_connection_sequence, |
|
|
|
|
memcpy(expected_connection_sequence, sequences->connections, |
|
|
|
|
sizeof(int) * expected_seq_length); |
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_iters / 2; i++) { |
|
|
|
|
const int actual = actual_connection_sequence[i]; |
|
|
|
|
const int actual = sequences->connections[i]; |
|
|
|
|
const int expected = expected_connection_sequence[i % expected_seq_length]; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
print_failed_expectations(expected_connection_sequence, |
|
|
|
|
actual_connection_sequence, expected_seq_length, |
|
|
|
|
sequences->connections, expected_seq_length, |
|
|
|
|
num_iters); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
@ -797,13 +806,34 @@ static void verify_partial_carnage_round_robin( |
|
|
|
|
|
|
|
|
|
/* second half of the iterations go without response */ |
|
|
|
|
for (; i < num_iters; i++) { |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[i] == -1); |
|
|
|
|
GPR_ASSERT(sequences->connections[i] == -1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* We can assert that the first client channel state should be READY, when all
|
|
|
|
|
* servers were available; and that the last one should be TRANSIENT_FAILURE, |
|
|
|
|
* after all servers are gone. */ |
|
|
|
|
grpc_connectivity_state actual = sequences->connectivity_states[0]; |
|
|
|
|
grpc_connectivity_state expected = GRPC_CHANNEL_READY; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' " |
|
|
|
|
"at iteration #%d", |
|
|
|
|
grpc_connectivity_state_name(expected), |
|
|
|
|
grpc_connectivity_state_name(actual), 0); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
actual = sequences->connectivity_states[num_iters - 1]; |
|
|
|
|
expected = GRPC_CHANNEL_TRANSIENT_FAILURE; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' " |
|
|
|
|
"at iteration #%d", |
|
|
|
|
grpc_connectivity_state_name(expected), |
|
|
|
|
grpc_connectivity_state_name(actual), (int)num_iters - 1); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* even though we know all the servers are dead, the client is still trying
|
|
|
|
|
* retrying, believing it's in a transient failure situation */ |
|
|
|
|
assert_channel_connectivity(client, 2, GRPC_CHANNEL_TRANSIENT_FAILURE, |
|
|
|
|
GRPC_CHANNEL_CONNECTING); |
|
|
|
|
gpr_free(expected_connection_sequence); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -826,15 +856,14 @@ static void dump_array(const char *desc, const int *data, const size_t count) { |
|
|
|
|
|
|
|
|
|
static void verify_rebirth_round_robin(const servers_fixture *f, |
|
|
|
|
grpc_channel *client, |
|
|
|
|
const int *actual_connection_sequence, |
|
|
|
|
const request_sequences *sequences, |
|
|
|
|
const size_t num_iters) { |
|
|
|
|
int *expected_connection_sequence; |
|
|
|
|
size_t i, j, unique_seq_last_idx, unique_seq_first_idx; |
|
|
|
|
const size_t expected_seq_length = f->num_servers; |
|
|
|
|
int *seen_elements; |
|
|
|
|
|
|
|
|
|
dump_array("actual_connection_sequence", actual_connection_sequence, |
|
|
|
|
num_iters); |
|
|
|
|
dump_array("actual_connection_sequence", sequences->connections, num_iters); |
|
|
|
|
|
|
|
|
|
/* verify conn. seq. expectation */ |
|
|
|
|
/* get the first unique run of length "num_servers". */ |
|
|
|
@ -845,13 +874,13 @@ static void verify_rebirth_round_robin(const servers_fixture *f, |
|
|
|
|
|
|
|
|
|
memset(seen_elements, 0, sizeof(int) * expected_seq_length); |
|
|
|
|
for (i = 0; i < num_iters; i++) { |
|
|
|
|
if (actual_connection_sequence[i] < 0 || |
|
|
|
|
seen_elements[actual_connection_sequence[i]] != 0) { |
|
|
|
|
if (sequences->connections[i] < 0 || |
|
|
|
|
seen_elements[sequences->connections[i]] != 0) { |
|
|
|
|
/* if anything breaks the uniqueness of the run, back to square zero */ |
|
|
|
|
memset(seen_elements, 0, sizeof(int) * expected_seq_length); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
seen_elements[actual_connection_sequence[i]] = 1; |
|
|
|
|
seen_elements[sequences->connections[i]] = 1; |
|
|
|
|
for (j = 0; j < expected_seq_length; j++) { |
|
|
|
|
if (seen_elements[j] == 0) break; |
|
|
|
|
} |
|
|
|
@ -870,30 +899,72 @@ static void verify_rebirth_round_robin(const servers_fixture *f, |
|
|
|
|
|
|
|
|
|
unique_seq_first_idx = (unique_seq_last_idx - expected_seq_length + 1); |
|
|
|
|
memcpy(expected_connection_sequence, |
|
|
|
|
actual_connection_sequence + unique_seq_first_idx, |
|
|
|
|
sequences->connections + unique_seq_first_idx, |
|
|
|
|
sizeof(int) * expected_seq_length); |
|
|
|
|
|
|
|
|
|
/* first iteration succeeds */ |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[0] != -1); |
|
|
|
|
GPR_ASSERT(sequences->connections[0] != -1); |
|
|
|
|
/* then we fail for a while... */ |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[1] == -1); |
|
|
|
|
GPR_ASSERT(sequences->connections[1] == -1); |
|
|
|
|
/* ... but should be up at "unique_seq_first_idx" */ |
|
|
|
|
GPR_ASSERT(actual_connection_sequence[unique_seq_first_idx] != -1); |
|
|
|
|
GPR_ASSERT(sequences->connections[unique_seq_first_idx] != -1); |
|
|
|
|
|
|
|
|
|
for (j = 0, i = unique_seq_first_idx; i < num_iters; i++) { |
|
|
|
|
const int actual = actual_connection_sequence[i]; |
|
|
|
|
const int actual = sequences->connections[i]; |
|
|
|
|
const int expected = |
|
|
|
|
expected_connection_sequence[j++ % expected_seq_length]; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
print_failed_expectations(expected_connection_sequence, |
|
|
|
|
actual_connection_sequence, expected_seq_length, |
|
|
|
|
sequences->connections, expected_seq_length, |
|
|
|
|
num_iters); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* things are fine once the servers are brought back up */ |
|
|
|
|
assert_channel_connectivity(client, 1, GRPC_CHANNEL_READY); |
|
|
|
|
/* We can assert that the first client channel state should be READY, when all
|
|
|
|
|
* servers were available; same thing for the last one. In the middle |
|
|
|
|
* somewhere there must exist at least one TRANSIENT_FAILURE */ |
|
|
|
|
grpc_connectivity_state actual = sequences->connectivity_states[0]; |
|
|
|
|
grpc_connectivity_state expected = GRPC_CHANNEL_READY; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' " |
|
|
|
|
"at iteration #%d", |
|
|
|
|
grpc_connectivity_state_name(expected), |
|
|
|
|
grpc_connectivity_state_name(actual), 0); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
actual = sequences->connectivity_states[num_iters - 1]; |
|
|
|
|
expected = GRPC_CHANNEL_READY; |
|
|
|
|
if (actual != expected) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: expected '%s', got '%s' " |
|
|
|
|
"at iteration #%d", |
|
|
|
|
grpc_connectivity_state_name(expected), |
|
|
|
|
grpc_connectivity_state_name(actual), (int)num_iters - 1); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool found_failure_status = false; |
|
|
|
|
for (i = 1; i < sequences->n - 1; i++) { |
|
|
|
|
if (sequences->connectivity_states[i] == GRPC_CHANNEL_TRANSIENT_FAILURE) { |
|
|
|
|
found_failure_status = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!found_failure_status) { |
|
|
|
|
gpr_log( |
|
|
|
|
GPR_ERROR, |
|
|
|
|
"CONNECTIVITY STATUS SEQUENCE FAILURE: " |
|
|
|
|
"GRPC_CHANNEL_TRANSIENT_FAILURE status not found. Got the following " |
|
|
|
|
"instead:"); |
|
|
|
|
for (i = 0; i < num_iters; i++) { |
|
|
|
|
gpr_log(GPR_ERROR, "[%d]: %s", (int)i, |
|
|
|
|
grpc_connectivity_state_name(sequences->connectivity_states[i])); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gpr_free(expected_connection_sequence); |
|
|
|
|
gpr_free(seen_elements); |
|
|
|
|
} |
|
|
|
@ -934,7 +1005,7 @@ int main(int argc, char **argv) { |
|
|
|
|
* This should knock down the server bound to be selected next */ |
|
|
|
|
test_spec_reset(spec); |
|
|
|
|
spec->verifier = verify_vanishing_floor_round_robin; |
|
|
|
|
spec->description = "test_kill_all_server_at_2nd_iteration"; |
|
|
|
|
spec->description = "test_kill_middle_servers_at_2nd_iteration"; |
|
|
|
|
for (i = 1; i < NUM_SERVERS - 1; i++) { |
|
|
|
|
spec->kill_at[1][i] = 1; |
|
|
|
|
} |
|
|
|
|