Exemple #1
0
static lcb_error_t config_nodes(int mode, lcb_t instance, int cmd, void *arg)
{
    const char *node_strs = arg;
    clconfig_provider *target;
    hostlist_t nodes_obj;
    lcb_error_t err;

    if (mode != LCB_CNTL_SET) {
        return LCB_EINVAL;
    }

    nodes_obj = hostlist_create();
    if (!nodes_obj) {
        return LCB_CLIENT_ENOMEM;
    }

    err = hostlist_add_stringz(nodes_obj, node_strs,
                               cmd == LCB_CNTL_CONFIG_HTTP_NODES
                               ? LCB_CONFIG_HTTP_PORT : LCB_CONFIG_MCD_PORT);
    if (err != LCB_SUCCESS) {
        hostlist_destroy(nodes_obj);
        return err;
    }

    if (cmd == LCB_CNTL_CONFIG_HTTP_NODES) {
        target = lcb_confmon_get_provider(instance->confmon, LCB_CLCONFIG_HTTP);
        lcb_clconfig_http_set_nodes(target, nodes_obj);
    } else {
        target = lcb_confmon_get_provider(instance->confmon, LCB_CLCONFIG_CCCP);
        lcb_clconfig_cccp_set_nodes(target, nodes_obj);
    }

    hostlist_destroy(nodes_obj);
    return LCB_SUCCESS;
}
Exemple #2
0
/**
 * This function is where the configuration actually takes place. We ensure
 * in other functions that this is only ever called directly from an event
 * loop stack frame (or one of the small mini functions here) so that we
 * don't accidentally end up destroying resources underneath us.
 */
static void config_callback(clconfig_listener *listener,
                            clconfig_event_t event,
                            clconfig_info *info)
{
    struct lcb_bootstrap_st *bs = (struct lcb_bootstrap_st *)listener;
    lcb_t instance = bs->parent;

    if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) {
        if (event == CLCONFIG_EVENT_PROVIDERS_CYCLED) {
            if (!instance->vbucket_config) {
                initial_bootstrap_error(instance,
                                        LCB_ERROR,
                                        "No more bootstrap providers remain");
            }
        }
        return;
    }

    instance->last_error = LCB_SUCCESS;
    bs->active = 0;
    /** Ensure we're not called directly twice again */
    listener->callback = async_step_callback;

    if (bs->timer) {
        lcb_timer_destroy(instance, bs->timer);
        bs->timer = NULL;
    }

    lcb_log(LOGARGS(instance, DEBUG), "Instance configured!");

    if (instance->type != LCB_TYPE_CLUSTER) {
        lcb_update_vbconfig(instance, info);
    }

    if (!bs->bootstrapped) {
        bs->bootstrapped = 1;
        if (instance->type == LCB_TYPE_BUCKET &&
                instance->dist_type == VBUCKET_DISTRIBUTION_KETAMA) {
            lcb_log(LOGARGS(instance, INFO),
                    "Reverting to HTTP Config for memcached buckets");

            /** Memcached bucket */
            lcb_clconfig_set_http_always_on(
                    lcb_confmon_get_provider(
                            instance->confmon, LCB_CLCONFIG_HTTP));
            lcb_confmon_set_provider_active(instance->confmon,
                                            LCB_CLCONFIG_HTTP, 1);

            lcb_confmon_set_provider_active(instance->confmon,
                                            LCB_CLCONFIG_CCCP, 0);

            lcb_confmon_set_provider_active(instance->confmon,
                                            LCB_CLCONFIG_CCCP, 0);
        }
    }

    lcb_maybe_breakout(instance);
}
Exemple #3
0
/**
 * Invoked when get a NOT_MY_VBUCKET response. If the response contains a JSON
 * payload then we refresh the configuration with it.
 *
 * This function returns 1 if the operation was successfully rescheduled;
 * otherwise it returns 0. If it returns 0 then we give the error back to the
 * user.
 */
static int
handle_nmv(mc_SERVER *oldsrv, packet_info *resinfo, mc_PACKET *oldpkt)
{
    mc_PACKET *newpkt;
    protocol_binary_request_header hdr;
    lcb_error_t err = LCB_ERROR;
    lcb_t instance = oldsrv->instance;
    lcb_U16 vbid;
    clconfig_provider *cccp = lcb_confmon_get_provider(instance->confmon,
        LCB_CLCONFIG_CCCP);

    mcreq_read_hdr(oldpkt, &hdr);
    vbid = ntohs(hdr.request.vbucket);
    lcb_log(LOGARGS(oldsrv, WARN), LOGFMT "NOT_MY_VBUCKET. Packet=%p (S=%u). VBID=%u", LOGID(oldsrv), (void*)oldpkt, oldpkt->opaque, vbid);

    /* Notify of new map */
    lcb_vbguess_remap(instance, vbid, oldsrv->pipeline.index);

    if (PACKET_NBODY(resinfo) && cccp->enabled) {
        lcb_string s;

        lcb_string_init(&s);
        lcb_string_append(&s, PACKET_VALUE(resinfo), PACKET_NVALUE(resinfo));
        err = lcb_cccp_update(cccp, mcserver_get_host(oldsrv), &s);
        lcb_string_release(&s);
    }

    if (err != LCB_SUCCESS) {
        int bs_options;
        if (instance->cur_configinfo->origin == LCB_CLCONFIG_CCCP) {
            /**
             * XXX: Not enough to see if cccp was enabled, since cccp might
             * be requested by a user, but would still not actually be active
             * for clusters < 2.5 If our current config is from CCCP
             * then we can be fairly certain that CCCP is indeed working.
             *
             * For this reason, we don't use if (cccp->enabled) {...}
             */
            bs_options = LCB_BS_REFRESH_THROTTLE;
        } else {
            bs_options = LCB_BS_REFRESH_ALWAYS;
        }
        lcb_bootstrap_common(instance, bs_options);
    }

    if (!lcb_should_retry(oldsrv->settings, oldpkt, LCB_NOT_MY_VBUCKET)) {
        return 0;
    }

    /** Reschedule the packet again .. */
    newpkt = mcreq_renew_packet(oldpkt);
    newpkt->flags &= ~MCREQ_STATE_FLAGS;
    lcb_retryq_nmvadd(instance->retryq, (mc_EXPACKET*)newpkt);
    return 1;
}
/**
 * Invoked when get a NOT_MY_VBUCKET response. If the response contains a JSON
 * payload then we refresh the configuration with it.
 *
 * This function returns 1 if the operation was successfully rescheduled;
 * otherwise it returns 0. If it returns 0 then we give the error back to the
 * user.
 */
static int
handle_nmv(mc_SERVER *oldsrv, packet_info *resinfo, mc_PACKET *oldpkt)
{
    mc_PACKET *newpkt;
    protocol_binary_request_header hdr;
    lcb_error_t err = LCB_ERROR;
    lcb_t instance = oldsrv->instance;
    lcb_U16 vbid;
    int tmpix;
    clconfig_provider *cccp = lcb_confmon_get_provider(instance->confmon,
        LCB_CLCONFIG_CCCP);

    mcreq_read_hdr(oldpkt, &hdr);
    vbid = ntohs(hdr.request.vbucket);
    lcb_log(LOGARGS(oldsrv, WARN), LOGFMT "NOT_MY_VBUCKET. Packet=%p (S=%u). VBID=%u", LOGID(oldsrv), (void*)oldpkt, oldpkt->opaque, vbid);

    /* Notify of new map */
    tmpix = lcb_vbguess_remap(LCBT_VBCONFIG(instance),
        instance->vbguess, vbid, oldsrv->pipeline.index);
    if (tmpix > -1 && tmpix != oldsrv->pipeline.index) {
        lcb_log(LOGARGS(oldsrv, TRACE), LOGFMT "Heuristically set IX=%d as master for VBID=%u", LOGID(oldsrv), tmpix, vbid);
    }

    if (PACKET_NBODY(resinfo) && cccp->enabled) {
        lcb_string s;

        lcb_string_init(&s);
        lcb_string_append(&s, PACKET_VALUE(resinfo), PACKET_NVALUE(resinfo));
        err = lcb_cccp_update(cccp, mcserver_get_host(oldsrv), &s);
        lcb_string_release(&s);
    }

    if (err != LCB_SUCCESS) {
        lcb_bootstrap_common(instance, LCB_BS_REFRESH_ALWAYS);
    }

    if (!lcb_should_retry(oldsrv->settings, oldpkt, LCB_NOT_MY_VBUCKET)) {
        return 0;
    }

    /** Reschedule the packet again .. */
    newpkt = mcreq_renew_packet(oldpkt);
    newpkt->flags &= ~MCREQ_STATE_FLAGS;
    lcb_retryq_add(instance->retryq, (mc_EXPACKET*)newpkt, LCB_NOT_MY_VBUCKET);
    return 1;
}
lcb_error_t lcb_init_providers(lcb_t obj,
                               const struct lcb_create_st2 *e_options)
{
    hostlist_t mc_nodes;
    lcb_error_t err;
    const char *hosts;
    int http_enabled = 1;
    int cccp_enabled = 1;

    clconfig_provider *http =
            lcb_confmon_get_provider(obj->confmon, LCB_CLCONFIG_HTTP);

    clconfig_provider *cccp =
            lcb_confmon_get_provider(obj->confmon, LCB_CLCONFIG_CCCP);


    if (e_options->transports) {
        int cccp_found = 0;
        int http_found = 0;
        const lcb_config_transport_t *cur;

        for (cur = e_options->transports;
                *cur != LCB_CONFIG_TRANSPORT_LIST_END; cur++) {
            if (*cur == LCB_CONFIG_TRANSPORT_CCCP) {
                cccp_found = 1;
            } else if (*cur == LCB_CONFIG_TRANSPORT_HTTP) {
                http_found = 1;
            } else {
                return LCB_EINVAL;
            }
        }

        if (http_found || cccp_found) {
            cccp_enabled = cccp_found;
            http_enabled = http_found;
        }
    }

    if (lcb_getenv_boolean("LCB_NO_CCCP")) {
        cccp_enabled = 0;
    }

    if (lcb_getenv_boolean("LCB_NO_HTTP")) {
        http_enabled = 0;
    }

    /** The only way we can get to here is if one of the vars are set */
    if (cccp_enabled == 0 && http_enabled == 0) {
        return LCB_BAD_ENVIRONMENT;
    }

    if (http_enabled) {
        lcb_clconfig_http_enable(http);
        lcb_clconfig_http_set_nodes(http, obj->usernodes);
    } else {
        lcb_confmon_set_provider_active(obj->confmon, LCB_CLCONFIG_HTTP, 0);
    }

    if (!cccp_enabled) {
        lcb_confmon_set_provider_active(obj->confmon, LCB_CLCONFIG_CCCP, 0);
        return LCB_SUCCESS;
    }

    hosts = get_nonempty_string(e_options->mchosts);
    mc_nodes = hostlist_create();

    if (!mc_nodes) {
        return LCB_CLIENT_ENOMEM;
    }

    if (hosts) {
        err = hostlist_add_stringz(mc_nodes, hosts, LCB_CONFIG_MCD_PORT);
        if (err != LCB_SUCCESS) {
            hostlist_destroy(mc_nodes);
            return err;
        }

    } else {
        lcb_size_t ii;
        for (ii = 0; ii < obj->usernodes->nentries; ii++) {
            lcb_host_t *cur = obj->usernodes->entries + ii;
            hostlist_add_stringz(mc_nodes, cur->host, LCB_CONFIG_MCD_PORT);
        }
    }

    lcb_clconfig_cccp_enable(cccp, obj);
    lcb_clconfig_cccp_set_nodes(cccp, mc_nodes);
    hostlist_destroy(mc_nodes);
    return LCB_SUCCESS;
}
/**
 * Returns 1 if retried, 0 if the command should fail, -1 for an internal
 * error
 */
static int handle_not_my_vbucket(lcb_server_t *c,
                                 packet_info *resinfo,
                                 protocol_binary_request_header *oldreq,
                                 struct lcb_command_data_st *oldct)
{
    int idx;
    char *body;
    lcb_size_t nbody, nr;
    lcb_server_t *new_srv;
    struct lcb_command_data_st ct;
    protocol_binary_request_header req;
    hrtime_t now;
    lcb_string config_string;
    lcb_error_t err = LCB_ERROR;

    lcb_log(LOGARGS(c, WARN),
            "NOT_MY_VBUCKET; Server=%p,ix=%d,real_start=%lu,vb=%d",
            (void *)c, c->index,
            (unsigned long)oldct->real_start,
            (int)ntohs(oldreq->request.vbucket));

    lcb_string_init(&config_string);
    if (PACKET_NBODY(resinfo)) {
        lcb_string_append(&config_string,
                          PACKET_VALUE(resinfo),
                          PACKET_NVALUE(resinfo));

        err = lcb_cccp_update(lcb_confmon_get_provider(c->instance->confmon,
                              LCB_CLCONFIG_CCCP),
                              c->curhost.host,
                              &config_string);
    }

    lcb_string_release(&config_string);

    if (err != LCB_SUCCESS) {
        lcb_bootstrap_refresh(c->instance);
    }

    /* re-schedule command to new server */
    if (!c->instance->settings.vb_noguess) {
        idx = vbucket_found_incorrect_master(c->instance->vbucket_config,
                                             ntohs(oldreq->request.vbucket),
                                             (int)c->index);
    } else {
        idx = c->index;
    }

    if (idx == -1) {
        lcb_log(LOGARGS(c, ERR), "no alternate server");
        return 0;
    }
    lcb_log(LOGARGS(c, INFO), "Mapped key to new server %d -> %d",
            c->index, idx);

    now = gethrtime();

    if (oldct->real_start) {
        hrtime_t min_ok = now - MCSERVER_TIMEOUT(c) * 1000;
        if (oldct->real_start < min_ok) {
            /** Timed out in a 'natural' manner */
            return 0;
        }
    }

    req = *oldreq;

    lcb_assert((lcb_size_t)idx < c->instance->nservers);
    new_srv = c->instance->servers + idx;

    nr = ringbuffer_read(&c->cmd_log, req.bytes, sizeof(req));
    lcb_assert(nr == sizeof(req));

    req.request.opaque = ++c->instance->seqno;
    nbody = ntohl(req.request.bodylen);
    body = malloc(nbody);
    if (body == NULL) {
        lcb_error_handler(c->instance, LCB_CLIENT_ENOMEM, NULL);
        return -1;
    }
    nr = ringbuffer_read(&c->cmd_log, body, nbody);
    lcb_assert(nr == nbody);
    nr = ringbuffer_read(&c->output_cookies, &ct, sizeof(ct));
    lcb_assert(nr == sizeof(ct));

    /* Preserve the cookie and reset timestamp for the command. This
     * means that the library will retry the command until it will
     * get code different from LCB_NOT_MY_VBUCKET */
    if (!ct.real_start) {
        ct.real_start = ct.start;
    }
    ct.start = now;

    lcb_server_retry_packet(new_srv, &ct, &req, sizeof(req));
    /* FIXME dtrace instrumentation */
    lcb_server_write_packet(new_srv, body, nbody);
    lcb_server_end_packet(new_srv);
    lcb_server_send_packets(new_srv);
    free(body);

    return 1;
}