EV-Etcd

 view release on metacpan or  search on metacpan

etcd_common.c  view on Meta::CPAN

        case GRPC_STATUS_DEADLINE_EXCEEDED:
            return 1;
        default:
            return 0;
    }
}

/* Statuses a reconnect cannot fix; streams retry on any other end */
int is_permanent_status(grpc_status_code code) {
    switch (code) {
        case GRPC_STATUS_INVALID_ARGUMENT:
        case GRPC_STATUS_NOT_FOUND:
        case GRPC_STATUS_ALREADY_EXISTS:
        case GRPC_STATUS_PERMISSION_DENIED:
        case GRPC_STATUS_FAILED_PRECONDITION:
        case GRPC_STATUS_OUT_OF_RANGE:
        case GRPC_STATUS_UNIMPLEMENTED:
        case GRPC_STATUS_UNAUTHENTICATED:
            return 1;
        default:
            return 0;
    }
}

int etcd_is_no_leader(grpc_status_code status, grpc_slice status_details) {
    return status == GRPC_STATUS_UNAVAILABLE
        && grpc_slice_eq(status_details, grpc_slice_from_static_string("etcdserver: no leader"));
}

static void set_int_arg(grpc_arg *arg, const char *key, int value) {
    arg->type = GRPC_ARG_INTEGER;
    arg->key = (char *)key;
    arg->value.integer = value;
}

channel_ref_t *etcd_create_channel(ev_etcd_t *client, const char *target) {
    grpc_arg arg[9];
    grpc_channel_args args = { 0, arg };
    /* gRPC caps messages at 4 MiB; a watch replay or a range can be larger */
    set_int_arg(&arg[args.num_args++], "grpc.max_receive_message_length", -1);
    /* gRPC's default is 120 s, shared by every channel to the target */
    set_int_arg(&arg[args.num_args++], "grpc.max_reconnect_backoff_ms", 5000);
    if (client->tls_server_name) {
        arg[args.num_args].type = GRPC_ARG_STRING;
        arg[args.num_args].key = (char *)"grpc.ssl_target_name_override";
        arg[args.num_args++].value.string = client->tls_server_name;
    }
    if (client->keepalive_ms) {
        /* etcd GOAWAYs pings without calls; gRPC's own throttles would hide a dead
         * idle watch, and newer gRPC times pings out by ping_timeout_ms */
        set_int_arg(&arg[args.num_args++], "grpc.keepalive_time_ms", client->keepalive_ms);
        set_int_arg(&arg[args.num_args++], "grpc.keepalive_timeout_ms", client->keepalive_timeout_ms);
        set_int_arg(&arg[args.num_args++], "grpc.keepalive_permit_without_calls", 0);
        set_int_arg(&arg[args.num_args++], "grpc.http2.max_pings_without_data", 0);
        set_int_arg(&arg[args.num_args++], "grpc.http2.min_time_between_pings_ms", client->keepalive_ms);
        set_int_arg(&arg[args.num_args++], "grpc.http2.ping_timeout_ms", client->keepalive_timeout_ms);
    }
    /* grpc_channel_create arrived in gRPC 1.45 */
    grpc_channel *channel;
#ifdef HAVE_GRPC_NEW_CHANNEL_API
    grpc_channel_credentials *creds = client->creds
        ? client->creds : grpc_insecure_credentials_create();
    WITH_SIGNALS_BLOCKED(channel = grpc_channel_create(target, creds, &args));
    if (!client->creds) grpc_channel_credentials_release(creds);
#else
    WITH_SIGNALS_BLOCKED(channel = client->creds
        ? grpc_secure_channel_create(client->creds, target, &args, NULL)
        : grpc_insecure_channel_create(target, &args, NULL));
#endif
    if (!channel) return NULL;

    channel_ref_t *ref;
    Newx(ref, 1, channel_ref_t);
    ref->channel = channel;
    ref->refs = 1;
    return ref;
}

void etcd_channel_release(channel_ref_t *ref, int destroy) {
    if (ref && !--ref->refs) {
        /* A forked child discards only our wrapper, without touching gRPC. */
        if (destroy) grpc_channel_destroy(ref->channel);
        Safefree(ref);
    }
}

static void release_send_metadata(call_base_t *base) {
    if (base->owns_auth_value) {
        grpc_slice_unref(base->send_md[0].value);
        base->owns_auth_value = 0;
    }
}

void etcd_call_release(call_base_t *base) {
    etcd_channel_release(base->channel_ref, 1);
    base->channel_ref = NULL;
    release_send_metadata(base);
}

static void cancel_blocking_calls(ev_etcd_t *client, unsigned channel_gen, const char *message) {
    for (pending_call_t *pc = client->pending_calls; pc; pc = pc->next) {
        if (pc->call && pc->base.channel_gen == channel_gen
            && (pc->base.type == CALL_TYPE_LOCK || pc->base.type == CALL_TYPE_ELECTION_CAMPAIGN))
            grpc_call_cancel_with_status(pc->call, GRPC_STATUS_UNAVAILABLE, message, NULL);
    }
}

static void check_leader_failure(ev_etcd_t *client, unsigned channel_gen,
                                grpc_status_code status, grpc_slice status_details) {
    if (etcd_is_no_leader(status, status_details)) {
        /* Retired channels too: another failure may have moved the client already */
        cancel_blocking_calls(client, channel_gen, "etcdserver: no leader");
    }
}

void etcd_rotate_endpoint(ev_etcd_t *client) {
    int next = client->current_endpoint;
    if (client->endpoint_count > 1)
        next = (next + 1) % client->endpoint_count;

    channel_ref_t *ref = etcd_create_channel(client, client->endpoints[next]);
    if (!ref) return;

    channel_ref_t *old = client->channel_ref;



( run in 0.963 second using v1.01-cache-2.11-cpan-3f962061493 )