/** * Handles the propagation and population of the 'synctoken' information. * @param mc_resp The response packet * @param req The request packet (used to get the vBucket) * @param tgt Pointer to synctoken which should be populated. */ static void handle_synctoken(lcb_t instance, const packet_info *mc_resp, const mc_PACKET *req, lcb_SYNCTOKEN *stok) { const char *sbuf; uint16_t vbid; if (PACKET_EXTLEN(mc_resp) == 0) { return; /* No extras */ } if (!instance->dcpinfo && LCBT_SETTING(instance, dur_synctokens)) { size_t nvb = LCBT_VBCONFIG(instance)->nvb; if (nvb) { instance->dcpinfo = calloc(nvb, sizeof(*instance->dcpinfo)); } } sbuf = PACKET_BODY(mc_resp); vbid = mcreq_get_vbucket(req); stok->vbid_ = vbid; memcpy(&stok->uuid_, sbuf, 8); memcpy(&stok->seqno_, sbuf + 8, 8); stok->uuid_ = lcb_ntohll(stok->uuid_); stok->seqno_ = lcb_ntohll(stok->seqno_); if (instance->dcpinfo) { instance->dcpinfo[vbid] = *stok; } }
void lcbdur_prepare_item(lcb_DURITEM *ent, lcb_U16 *ixarray, size_t *nitems) { size_t ii, oix = 0, maxix = 0; lcb_DURSET *dset = ent->parent; lcb_t instance = dset->instance; lcbvb_CONFIG *vbc = LCBT_VBCONFIG(instance); RESFLD(ent, persisted_master) = 0; RESFLD(ent, exists_master) = 0; RESFLD(ent, npersisted) = 0; RESFLD(ent, nreplicated) = 0; RESFLD(ent, cas) = 0; RESFLD(ent, rc) = LCB_SUCCESS; if (DSET_OPTFLD(dset, persist_to) == 1 && DSET_OPTFLD(dset, replicate_to) == 0) { maxix = 1; /* Only master! */ } else { maxix = LCBT_NREPLICAS(instance) + 1; } for (ii = 0; ii < maxix; ii++) { int cur_ix; lcbdur_SERVINFO *info = &ent->sinfo[ii]; const mc_SERVER *s_exp; cur_ix = lcbvb_vbserver(vbc, ent->vbid, ii); if (cur_ix < 0) { memset(info, 0, sizeof(*info)); continue; } s_exp = LCBT_GET_SERVER(instance, cur_ix); if (s_exp != info->server) { memset(info, 0, sizeof(*info)); } else if (server_criteria_satisfied(ent, info, ii==0)) { /* Update counters as required */ if (ii == 0) { RESFLD(ent, exists_master) = 1; } else { RESFLD(ent, nreplicated)++; } if (info->persisted) { RESFLD(ent, npersisted)++; if (ii == 0) { RESFLD(ent, persisted_master) = 1; } } continue; } /* Otherwise, write the expected server out */ ixarray[oix++] = s_exp->pipeline.index; } *nitems = oix; }
LIBCOUCHBASE_API lcb_int32_t lcb_get_num_nodes(lcb_t instance) { if (LCBT_VBCONFIG(instance)) { return LCBT_NSERVERS(instance); } else { return -1; } }
LIBCOUCHBASE_API lcb_int32_t lcb_get_num_replicas(lcb_t instance) { if (LCBT_VBCONFIG(instance)) { return LCBT_NREPLICAS(instance); } else { return -1; } }
LIBCOUCHBASE_API lcb_MULTICMD_CTX * lcb_endure3_ctxnew(lcb_t instance, const lcb_durability_opts_t *options, lcb_error_t *errp) { lcb_DURSET *dset; lcb_error_t err_s; hrtime_t now; lcbio_pTABLE io = instance->iotable; if (!errp) { errp = &err_s; } if (!LCBT_VBCONFIG(instance)) { *errp = LCB_CLIENT_ETMPFAIL; return NULL; } now = gethrtime(); dset = calloc(1, sizeof(*dset)); if (!dset) { *errp = LCB_CLIENT_ENOMEM; return NULL; } dset->opts = *options; dset->instance = instance; dset->mctx.addcmd = dset_ctx_add; dset->mctx.done = dset_ctx_schedule; dset->mctx.fail = dset_ctx_fail; if (!DSET_OPTFLD(dset, timeout)) { DSET_OPTFLD(dset, timeout) = LCBT_SETTING(instance, durability_timeout); } if (!DSET_OPTFLD(dset, interval)) { DSET_OPTFLD(dset, interval) = LCB_DEFAULT_DURABILITY_INTERVAL; } if (-1 == verify_critera(instance, dset)) { free(dset); *errp = LCB_DURABILITY_ETOOMANY; return NULL; } dset->us_timeout = (lcb_U32)(now / 1000) + DSET_OPTFLD(dset, timeout); dset->timer = io->timer.create(io->p); lcb_string_init(&dset->kvbufs); return &dset->mctx; }
lcbdur_SERVINFO * lcbdur_ent_getinfo(lcb_DURITEM *item, int srvix) { size_t ii; lcb_t instance = item->parent->instance; for (ii = 0; ii < LCBT_NREPLICAS(instance)+1; ii++) { int ix = lcbvb_vbserver(LCBT_VBCONFIG(instance), item->vbid, ii); if (ix > -1 && ix == srvix) { return &item->sinfo[ii]; } } return NULL; }
/** * 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; }
void lcbdur_update_item(lcb_DURITEM *item, int flags, int srvix) { lcbdur_SERVINFO *info; lcb_t instance; int is_master; const mc_SERVER *server; if (!flags || item->done) { return; } info = lcbdur_ent_getinfo(item, srvix); if (!info) { lcb_log(LOGARGS(item->parent, DEBUG), "Ignoring response from server %d. Not a master or replica for vBucket %d", srvix, item->vbid); return; } instance = item->parent->instance; is_master = lcbvb_vbmaster(LCBT_VBCONFIG(instance), item->vbid) == srvix; server = LCBT_GET_SERVER(instance, srvix); memset(info, 0, sizeof(*info)); info->server = server; if (flags & LCBDUR_UPDATE_PERSISTED) { info->persisted = 1; RESFLD(item, npersisted)++; if (is_master) { RESFLD(item, persisted_master) = 1; } } if (flags & LCBDUR_UPDATE_REPLICATED) { info->exists = 1; if (is_master) { RESFLD(item, exists_master) = 1; } else { RESFLD(item, nreplicated)++; } } if (lcbdur_ent_check_done(item)) { RESFLD(item, rc) = LCB_SUCCESS; lcbdur_ent_finish(item); } }
/** * Run the event loop until we've got a response for all of our spooled * commands. You should not call this function from within your callbacks. * * @param instance the instance to run the event loop for. * * @author Trond Norbye */ LIBCOUCHBASE_API lcb_error_t lcb_wait(lcb_t instance) { if (instance->wait != 0) { return instance->last_error; } if (!has_pending(instance)) { return LCB_SUCCESS; } maybe_reset_timeouts(instance); instance->last_error = LCB_SUCCESS; instance->wait = 1; IOT_START(instance->iotable); instance->wait = 0; if (LCBT_VBCONFIG(instance)) { return LCB_SUCCESS; } return instance->last_error; }
static lcb_error_t obs_ctxadd(lcb_MULTICMD_CTX *mctx, const lcb_CMDOBSERVE *cmd) { const void *hk; lcb_SIZE nhk; int vbid; unsigned maxix; int ii; OBSERVECTX *ctx = CTX_FROM_MULTI(mctx); lcb_t instance = ctx->instance; mc_CMDQUEUE *cq = &instance->cmdq; if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { return LCB_EMPTY_KEY; } if (cq->config == NULL) { return LCB_CLIENT_ETMPFAIL; } if (LCBVB_DISTTYPE(LCBT_VBCONFIG(instance)) != LCBVB_DIST_VBUCKET) { return LCB_NOT_SUPPORTED; } mcreq_extract_hashkey(&cmd->key, &cmd->_hashkey, 24, &hk, &nhk); vbid = lcbvb_k2vb(cq->config, hk, nhk); maxix = LCBVB_NREPLICAS(cq->config); for (ii = -1; ii < (int)maxix; ++ii) { struct observe_st *rr; lcb_U16 vb16, klen16; int ix; if (ii == -1) { ix = lcbvb_vbmaster(cq->config, vbid); if (ix < 0) { return LCB_NO_MATCHING_SERVER; } } else { ix = lcbvb_vbreplica(cq->config, vbid, ii); if (ix < 0) { continue; } } lcb_assert(ix < (int)ctx->nrequests); rr = ctx->requests + ix; if (!rr->allocated) { if (!init_request(rr)) { return LCB_CLIENT_ENOMEM; } } vb16 = htons((lcb_U16)vbid); klen16 = htons((lcb_U16)cmd->key.contig.nbytes); lcb_string_append(&rr->body, &vb16, sizeof vb16); lcb_string_append(&rr->body, &klen16, sizeof klen16); lcb_string_append(&rr->body, cmd->key.contig.bytes, cmd->key.contig.nbytes); ctx->remaining++; if (cmd->cmdflags & LCB_CMDOBSERVE_F_MASTER_ONLY) { break; } } return LCB_SUCCESS; }
LIBCOUCHBASE_API lcb_MULTICMD_CTX * lcb_endure3_ctxnew(lcb_t instance, const lcb_durability_opts_t *options, lcb_error_t *errp) { lcb_DURSET *dset; lcb_error_t err_s; lcbio_pTABLE io = instance->iotable; const lcb_DURABILITYOPTSv0 *opts_in = &options->v.v0; if (!errp) { errp = &err_s; } *errp = LCB_SUCCESS; if (!LCBT_VBCONFIG(instance)) { *errp = LCB_CLIENT_ETMPFAIL; return NULL; } dset = calloc(1, sizeof(*dset)); if (!dset) { *errp = LCB_CLIENT_ENOMEM; return NULL; } /* Ensure we don't clobber options from older versions */ dset->opts.cap_max = opts_in->cap_max; dset->opts.check_delete = opts_in->check_delete; dset->opts.interval = opts_in->interval; dset->opts.persist_to = opts_in->persist_to; dset->opts.replicate_to = opts_in->replicate_to; dset->opts.timeout = opts_in->timeout; if (options->version > 0) { dset->opts.pollopts = opts_in->pollopts; } dset->opts.pollopts = get_poll_meth(instance, &dset->opts); dset->instance = instance; dset->mctx.addcmd = dset_ctx_add; dset->mctx.done = dset_ctx_schedule; dset->mctx.fail = dset_ctx_fail; if (!DSET_OPTFLD(dset, timeout)) { DSET_OPTFLD(dset, timeout) = LCBT_SETTING(instance, durability_timeout); } if (!DSET_OPTFLD(dset, interval)) { DSET_OPTFLD(dset, interval) = LCBT_SETTING(instance, durability_interval); } *errp = lcb_durability_validate(instance, &dset->opts.persist_to, &dset->opts.replicate_to, dset->opts.cap_max ? LCB_DURABILITY_VALIDATE_CAPMAX : 0); if (*errp != LCB_SUCCESS) { free(dset); return NULL; } dset->timer = io->timer.create(io->p); lcb_string_init(&dset->kvbufs); return &dset->mctx; }
LIBCOUCHBASE_API const char * lcb_get_node(lcb_t instance, lcb_GETNODETYPE type, unsigned ix) { if (type & LCB_NODE_HTCONFIG) { if (type & LCB_NODE_CONNECTED) { const lcb_host_t *host = lcb_confmon_get_rest_host(instance->confmon); if (host) { return mk_scratch_host(instance, host); } else { return return_badhost(type); } } else { /* Retrieve one from the vbucket configuration */ lcbvb_CONFIG *vbc = LCBT_VBCONFIG(instance); lcbvb_SVCMODE mode; const char *hp = NULL; if (LCBT_SETTING(instance, sslopts) & LCB_SSL_ENABLED) { mode = LCBVB_SVCMODE_SSL; } else { mode = LCBVB_SVCMODE_PLAIN; } if (instance->type == LCB_TYPE_BUCKET) { if (vbc) { ix %= LCBVB_NSERVERS(vbc); hp = lcbvb_get_hostport(vbc, ix, LCBVB_SVCTYPE_MGMT, mode); } else if ((type & LCB_NODE_NEVERNULL) == 0) { return NULL; } } if (hp == NULL && instance->ht_nodes && instance->ht_nodes->nentries) { ix %= instance->ht_nodes->nentries; hostlist_ensure_strlist(instance->ht_nodes); hp = instance->ht_nodes->slentries[ix]; } if (!hp) { if ((hp = return_badhost(type)) == NULL) { return NULL; } } if (!ensure_scratch(instance, strlen(hp)+1)) { return NULL; } lcb_string_appendz(instance->scratch, hp); return instance->scratch->base; } } else if (type & (LCB_NODE_DATA|LCB_NODE_VIEWS)) { const mc_SERVER *server; ix %= LCBT_NSERVERS(instance); server = LCBT_GET_SERVER(instance, ix); if ((type & LCB_NODE_CONNECTED) && server->connctx == NULL) { return return_badhost(type); } if (server->curhost == NULL) { return return_badhost(type); } /* otherwise, return the actual host:port of the server */ if (type & LCB_NODE_DATA) { return mk_scratch_host(instance, server->curhost); } else { return server->viewshost; } } else { return NULL; /* don't know the type */ } }
static lcb_error_t obs_ctxadd(lcb_MULTICMD_CTX *mctx, const lcb_CMDBASE *cmdbase) { int vbid, srvix_dummy; unsigned ii; const lcb_CMDOBSERVE *cmd = (const lcb_CMDOBSERVE *)cmdbase; OBSERVECTX *ctx = CTX_FROM_MULTI(mctx); lcb_t instance = ctx->instance; mc_CMDQUEUE *cq = &instance->cmdq; lcb_U16 servers_s[4]; const lcb_U16 *servers; size_t nservers; if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { return LCB_EMPTY_KEY; } if (cq->config == NULL) { return LCB_CLIENT_ETMPFAIL; } if (LCBVB_DISTTYPE(LCBT_VBCONFIG(instance)) != LCBVB_DIST_VBUCKET) { return LCB_NOT_SUPPORTED; } mcreq_map_key(cq, &cmd->key, &cmd->_hashkey, 24, &vbid, &srvix_dummy); if (cmd->servers_) { servers = cmd->servers_; nservers = cmd->nservers_; } else { nservers = 0; servers = servers_s; /* Replicas are always < 4 */ for (ii = 0; ii < LCBVB_NREPLICAS(cq->config) + 1; ii++) { int ix = lcbvb_vbserver(cq->config, vbid, ii); if (ix < 0) { if (ii == 0) { return LCB_NO_MATCHING_SERVER; } else { continue; } } servers_s[nservers++] = ix; if (cmd->cmdflags & LCB_CMDOBSERVE_F_MASTER_ONLY) { break; /* Only a single server! */ } } } if (nservers == 0) { return LCB_NO_MATCHING_SERVER; } for (ii = 0; ii < nservers; ii++) { lcb_string *rr; lcb_U16 vb16, klen16; lcb_U16 ix = servers[ii]; lcb_assert(ix < ctx->nrequests); rr = ctx->requests + ix; if (0 != lcb_string_reserve(rr, 4 + cmd->key.contig.nbytes)) { return LCB_CLIENT_ENOMEM; } vb16 = htons((lcb_U16)vbid); klen16 = htons((lcb_U16)cmd->key.contig.nbytes); lcb_string_append(rr, &vb16, sizeof vb16); lcb_string_append(rr, &klen16, sizeof klen16); lcb_string_append(rr, cmd->key.contig.bytes, cmd->key.contig.nbytes); ctx->remaining++; } 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 *bs = (struct lcb_BOOTSTRAP *)listener; lcb_t instance = bs->parent; if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) { if (event == CLCONFIG_EVENT_PROVIDERS_CYCLED) { if (!LCBT_VBCONFIG(instance)) { initial_bootstrap_error( instance, LCB_ERROR, "No more bootstrap providers remain"); } } return; } instance->last_error = LCB_SUCCESS; /** Ensure we're not called directly twice again */ listener->callback = async_step_callback; lcbio_timer_disarm(bs->tm); lcb_log(LOGARGS(instance, DEBUG), "Instance configured!"); if (info->origin != LCB_CLCONFIG_FILE) { /* Set the timestamp for the current config to control throttling, * but only if it's not an initial file-based config. See CCBC-482 */ bs->last_refresh = gethrtime(); bs->errcounter = 0; } if (info->origin == LCB_CLCONFIG_CCCP) { /* Disable HTTP provider if we've received something via CCCP */ if (instance->cur_configinfo == NULL || instance->cur_configinfo->origin != LCB_CLCONFIG_HTTP) { /* Never disable HTTP if it's still being used */ lcb_confmon_set_provider_active( instance->confmon, LCB_CLCONFIG_HTTP, 0); } } if (instance->type != LCB_TYPE_CLUSTER) { lcb_update_vbconfig(instance, info); } if (!bs->bootstrapped) { bs->bootstrapped = 1; lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); if (instance->type == LCB_TYPE_BUCKET && LCBVB_DISTTYPE(LCBT_VBCONFIG(instance)) == LCBVB_DIST_KETAMA && instance->cur_configinfo->origin != LCB_CLCONFIG_MCRAW) { lcb_log(LOGARGS(instance, INFO), "Reverting to HTTP Config for memcached buckets"); instance->settings->bc_http_stream_time = -1; lcb_confmon_set_provider_active( instance->confmon, LCB_CLCONFIG_HTTP, 1); lcb_confmon_set_provider_active( instance->confmon, LCB_CLCONFIG_CCCP, 0); } instance->callbacks.bootstrap(instance, LCB_SUCCESS); } lcb_maybe_breakout(instance); }