lcb_error_t lcb_bootstrap_common(lcb_t instance, int options) { struct lcb_BOOTSTRAP *bs = instance->bootstrap; hrtime_t now = gethrtime(); if (!bs) { bs = calloc(1, sizeof(*instance->bootstrap)); if (!bs) { return LCB_CLIENT_ENOMEM; } bs->tm = lcbio_timer_new(instance->iotable, bs, initial_timeout); instance->bootstrap = bs; bs->parent = instance; lcb_confmon_add_listener(instance->confmon, &bs->listener); } if (lcb_confmon_is_refreshing(instance->confmon)) { return LCB_SUCCESS; } if (options & LCB_BS_REFRESH_THROTTLE) { /* Refresh throttle requested. This is not true if options == ALWAYS */ hrtime_t next_ts; unsigned errthresh = LCBT_SETTING(instance, weird_things_threshold); if (options & LCB_BS_REFRESH_INCRERR) { bs->errcounter++; } next_ts = bs->last_refresh; next_ts += LCB_US2NS(LCBT_SETTING(instance, weird_things_delay)); if (now < next_ts && bs->errcounter < errthresh) { lcb_log(LOGARGS(instance, INFO), "Not requesting a config refresh because of throttling parameters. Next refresh possible in %ums or %u errors. " "See LCB_CNTL_CONFDELAY_THRESH and LCB_CNTL_CONFERRTHRESH to modify the throttling settings", LCB_NS2US(next_ts-now)/1000, (unsigned)errthresh-bs->errcounter); return LCB_SUCCESS; } } if (options == LCB_BS_REFRESH_INITIAL) { lcb_confmon_prepare(instance->confmon); bs->listener.callback = config_callback; lcbio_timer_set_target(bs->tm, initial_timeout); lcbio_timer_rearm(bs->tm, LCBT_SETTING(instance, config_timeout)); lcb_aspend_add(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); } else { /** No initial timer */ bs->listener.callback = async_step_callback; } /* Reset the counters */ bs->errcounter = 0; if (options != LCB_BS_REFRESH_INITIAL) { bs->last_refresh = now; } return lcb_confmon_start(instance->confmon); }
LIBCOUCHBASE_API lcb_destroy_callback lcb_set_destroy_callback(lcb_t instance, lcb_destroy_callback cb) { lcb_destroy_callback ret = LCBT_SETTING(instance, dtorcb); if (cb) { LCBT_SETTING(instance, dtorcb) = cb; } return ret; }
/** * Optionally decompress an incoming payload. * @param o The instance * @param resp The response received * @param[out] bytes pointer to the final payload * @param[out] nbytes pointer to the size of the final payload * @param[out] freeptr pointer to free. This should be initialized to `NULL`. * If temporary dynamic storage is required this will be set to the allocated * pointer upon return. Otherwise it will be set to NULL. In any case it must */ static void maybe_decompress(lcb_t o, const packet_info *respkt, lcb_RESPGET *rescmd, void **freeptr) { lcb_U8 dtype = 0; if (!PACKET_NVALUE(respkt)) { return; } if (PACKET_DATATYPE(respkt) & PROTOCOL_BINARY_DATATYPE_JSON) { dtype = LCB_VALUE_F_JSON; } if (PACKET_DATATYPE(respkt) & PROTOCOL_BINARY_DATATYPE_COMPRESSED) { if (LCBT_SETTING(o, compressopts) & LCB_COMPRESS_IN) { /* if we inflate, we don't set the flag */ mcreq_inflate_value( PACKET_VALUE(respkt), PACKET_NVALUE(respkt), &rescmd->value, &rescmd->nvalue, freeptr); } else { /* user doesn't want inflation. signal it's compressed */ dtype |= LCB_VALUE_F_SNAPPYCOMP; } } rescmd->datatype = dtype; }
/** * 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; } }
static int can_compress(lcb_t instance, const mc_PIPELINE *pipeline, const lcb_VALBUF *vbuf, lcb_datatype_t datatype) { mc_SERVER *server = (mc_SERVER *)pipeline; int compressopts = LCBT_SETTING(instance, compressopts); if (mcreq_compression_supported() == 0) { return 0; } if (vbuf->vtype != LCB_KV_COPY) { return 0; } if ((compressopts & LCB_COMPRESS_OUT) == 0) { return 0; } if (server->compsupport == 0 && (compressopts & LCB_COMPRESS_FORCE) == 0) { return 0; } if (datatype & LCB_VALUE_F_SNAPPYCOMP) { return 0; } return 1; }
LIBCOUCHBASE_API lcb_error_t lcb_cbflush3(lcb_t instance, const void *cookie, const lcb_CMDBASE *cmd) { lcb_http_request_t htr; lcb_CMDHTTP htcmd = { 0 }; lcb_string urlpath; lcb_error_t rc; (void)cmd; lcb_string_init(&urlpath); lcb_string_appendz(&urlpath, "/pools/default/buckets/"); lcb_string_appendz(&urlpath, LCBT_SETTING(instance, bucket)); lcb_string_appendz(&urlpath, "/controller/doFlush"); htcmd.type = LCB_HTTP_TYPE_MANAGEMENT; htcmd.method = LCB_HTTP_METHOD_POST; htcmd.reqhandle = &htr; LCB_CMD_SET_KEY(&htcmd, urlpath.base, urlpath.nused); rc = lcb_http3(instance, cookie, &htcmd); lcb_string_release(&urlpath); if (rc != LCB_SUCCESS) { return rc; } lcb_htreq_setcb(htr, flush_cb); return LCB_SUCCESS; }
static void on_flush_done(lcbio_CTX *ctx, unsigned expected, unsigned actual) { mc_SERVER *server = lcbio_ctx_data(ctx); lcb_U64 now = 0; if (LCBT_SETTING(server->instance, readj_ts_wait)) { now = gethrtime(); } mcreq_flush_done_ex(&server->pipeline, actual, expected, now); check_closed(server); }
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; }
static lcb_U8 get_poll_meth(lcb_t instance, const lcb_DURABILITYOPTSv0 *options) { /* Need to call this first, so we can actually allocate the appropriate * data for this.. */ lcb_U8 meth = options->pollopts; if (meth == LCB_DURABILITY_MODE_DEFAULT) { meth = LCB_DURABILITY_MODE_CAS; if (LCBT_SETTING(instance, fetch_mutation_tokens) && LCBT_SETTING(instance, dur_mutation_tokens)) { size_t ii; for (ii = 0; ii < LCBT_NSERVERS(instance); ii++) { mc_SERVER *s = LCBT_GET_SERVER(instance, ii); if (s->mutation_tokens) { meth = LCB_DURABILITY_MODE_SEQNO; break; } } } } return meth; }
static void maybe_reset_timeouts(lcb_t instance) { size_t ii; lcb_U64 now; if (!LCBT_SETTING(instance, readj_ts_wait)) { return; } now = lcb_nstime(); for (ii = 0; ii < LCBT_NSERVERS(instance); ++ii) { mc_SERVER *ss = LCBT_GET_SERVER(instance, ii); mcreq_reset_timeouts(&ss->pipeline, now); } lcb_retryq_reset_timeouts(instance->retryq, now); }
static lcb_error_t wrap_return(lcb_t instance, lcb_error_t retval) { if (retval == LCB_SUCCESS) { return retval; } if (instance && LCBT_SETTING(instance, detailed_neterr) == 0) { switch (retval) { case LCB_ECTL_UNKNOWN: return LCB_NOT_SUPPORTED; case LCB_ECTL_UNSUPPMODE: return LCB_NOT_SUPPORTED; case LCB_ECTL_BADARG: return LCB_EINVAL; default: return retval; } } else { return retval; } }
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 */ } }
LIBCOUCHBASE_API lcb_error_t lcb_n1ql_query(lcb_t instance, const void *cookie, const lcb_CMDN1QL *cmd) { lcb_CMDHTTP htcmd = { 0 }; lcb_error_t err; N1QLREQ *req = NULL; if (cmd->query == NULL || cmd->nquery == 0 || cmd->callback == NULL || cmd->content_type == NULL) { return LCB_EINVAL; } htcmd.body = cmd->query; htcmd.nbody = cmd->nquery; htcmd.content_type = cmd->content_type; htcmd.method = LCB_HTTP_METHOD_POST; if (cmd->host) { htcmd.type = LCB_HTTP_TYPE_RAW; LCB_CMD_SET_KEY(&htcmd, QUERY_PATH, strlen(QUERY_PATH)); htcmd.host = cmd->host; htcmd.username = LCBT_SETTING(instance, bucket); htcmd.password = LCBT_SETTING(instance, password); } else { htcmd.type = LCB_HTTP_TYPE_N1QL; } htcmd.cmdflags = LCB_CMDHTTP_F_STREAM; req = calloc(1, sizeof(*req)); if (!req) { err = LCB_CLIENT_ENOMEM; goto GT_DESTROY; } req->callback = cmd->callback; req->cookie = cookie; req->instance = instance; req->parser = lcbjsp_create(LCBJSP_MODE_N1QL); if (!req->parser) { err = LCB_CLIENT_ENOMEM; goto GT_DESTROY; } req->parser->data = req; req->parser->callback = row_callback; htcmd.reqhandle = &req->htreq; err = lcb_http3(instance, req, &htcmd); if (err != LCB_SUCCESS) { goto GT_DESTROY; } lcb_htreq_setcb(req->htreq, chunk_callback); if (cmd->handle) { *cmd->handle = req; } return LCB_SUCCESS; GT_DESTROY: if (req) { req->callback = NULL; destroy_req(req); } return err; }