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; }
/** * 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); }
/** * 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; }