/** * Actually free the resources allocated by the dset (and all its entries). * Called by some other functions in libcouchbase */ void lcb_durability_dset_destroy(lcb_durability_set_t *dset) { lcb_size_t ii; lcb_t instance = dset->instance; if (dset->timer) { lcb_io_opt_t io = instance->settings.io; io->v.v0.delete_timer(io, dset->timer); io->v.v0.destroy_timer(io, dset->timer); dset->timer = NULL; } for (ii = 0; ii < dset->nentries; ii++) { lcb_durability_entry_t *ent = dset->entries + ii; free((void *)REQFLD(ent, key)); } hashset_remove(dset->instance->durability_polls, dset); if (dset->nentries > 1) { if (dset->ht) { genhash_free(dset->ht); } free(dset->entries); free(dset->valid_entries); } free(dset); lcb_maybe_breakout(instance); }
static void event_complete_common(lcb_server_t *c, lcb_error_t rc) { lcb_t instance = c->instance; if (rc != LCB_SUCCESS) { lcb_failout_server(c, rc); } else { if (instance->bootstrap.type == LCB_CONFIG_TRANSPORT_HTTP && c->is_config_node) { c->instance->bootstrap.via.http.weird_things = 0; } lcb_sockrw_apply_want(&c->connection); c->inside_handler = 0; } if (instance->compat.type == LCB_CACHED_CONFIG && instance->compat.value.cached.needs_update) { lcb_refresh_config_cache(instance); } else if (instance->bootstrap.type == LCB_CONFIG_TRANSPORT_CCCP && instance->bootstrap.via.cccp.next_config) { lcb_update_vbconfig(instance, instance->bootstrap.via.cccp.next_config); instance->bootstrap.via.cccp.next_config = NULL; } lcb_maybe_breakout(instance); lcb_error_handler(instance, rc, NULL); }
void lcb_timeout_server(lcb_server_t *server) { hrtime_t now, min_valid, next_ns = 0; lcb_uint32_t next_us; LOG(server, ERR, "Server timed out"); lcb_bootstrap_errcount_incr(server->instance); if (!server->connection_ready) { lcb_failout_server(server, LCB_ETIMEDOUT); return; } now = gethrtime(); /** The oldest valid command timestamp */ min_valid = now - ((hrtime_t)MCSERVER_TIMEOUT(server)) * 1000; purge_single_server(server, LCB_ETIMEDOUT, min_valid, &next_ns); if (next_ns) { next_us = (lcb_uint32_t) (next_ns / 1000); } else { next_us = MCSERVER_TIMEOUT(server); } lcb_log(LOGARGS(server, INFO), "%p, Scheduling next timeout for %d ms", server, next_us / 1000); lcb_timer_rearm(server->io_timer, next_us); lcb_maybe_breakout(server->instance); }
static void timer_callback(lcb_socket_t sock, short which, void *arg) { lcb_timer_t timer = arg; lcb_t instance = timer->instance; lcb_assert(TMR_IS_ARMED(timer)); lcb_assert(!TMR_IS_DESTROYED(timer)); timer->state |= LCB_TIMER_S_ENTERED; lcb_timer_disarm(timer); timer->callback(timer, instance, timer->cookie); if (TMR_IS_DESTROYED(timer) == 0 && TMR_IS_PERIODIC(timer) != 0) { lcb_timer_rearm(timer, timer->usec_); return; } if (! TMR_IS_STANDALONE(timer)) { lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_TIMER, timer); lcb_maybe_breakout(instance); } if (TMR_IS_DESTROYED(timer)) { destroy_timer(timer); } else { timer->state &= ~LCB_TIMER_S_ENTERED; } (void)sock; (void)which; }
void lcb_update_vbconfig(lcb_t instance, clconfig_info *config) { lcb_size_t ii; int change_status; clconfig_info *old_config; mc_CMDQUEUE *q = &instance->cmdq; old_config = instance->cur_configinfo; instance->cur_configinfo = config; instance->dist_type = VB_DISTTYPE(config->vbc); lcb_clconfig_incref(config); instance->nreplicas = (lcb_uint16_t)VB_NREPLICAS(config->vbc); q->config = instance->cur_configinfo->vbc; q->instance = instance; if (old_config) { if (is_new_config(instance, old_config->vbc, config->vbc)) { change_status = replace_config(instance, config); if (change_status == -1) { LOG(instance, ERR, "Couldn't replace config"); return; } lcb_clconfig_decref(old_config); } else { change_status = LCB_CONFIGURATION_UNCHANGED; } } else { unsigned nservers; mc_PIPELINE **servers; nservers = VB_NSERVERS(config->vbc); if ((servers = malloc(sizeof(*servers) * nservers)) == NULL) { abort(); } for (ii = 0; ii < nservers; ii++) { mc_SERVER *srv; if ((srv = mcserver_alloc(instance, ii)) == NULL) { abort(); } servers[ii] = &srv->pipeline; } mcreq_queue_add_pipelines(q, servers, nservers, config->vbc); change_status = LCB_CONFIGURATION_NEW; } /* Notify anyone interested in this event... */ if (change_status != LCB_CONFIGURATION_UNCHANGED) { if (instance->vbucket_state_listener != NULL) { for (ii = 0; ii < q->npipelines; ii++) { lcb_server_t *server = (lcb_server_t *)q->pipelines[ii]; instance->vbucket_state_listener(server); } } } instance->callbacks.configuration(instance, change_status); lcb_maybe_breakout(instance); }
static void server_timeout_handler(lcb_connection_t conn, lcb_error_t err) { lcb_server_t *server = (lcb_server_t *)conn->data; lcb_timeout_server(server); lcb_maybe_breakout(server->instance); (void)err; }
/** * 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); }
static void initial_bootstrap_error(lcb_t instance, lcb_error_t err, const char *errinfo) { struct lcb_BOOTSTRAP *bs = instance->bootstrap; instance->last_error = lcb_confmon_last_error(instance->confmon); if (instance->last_error == LCB_SUCCESS) { instance->last_error = err; } instance->callbacks.error(instance, instance->last_error, errinfo); lcb_log(LOGARGS(instance, ERR), "Failed to bootstrap client=%p. Code=0x%x, Message=%s", (void *)instance, err, errinfo); lcbio_timer_disarm(bs->tm); instance->callbacks.bootstrap(instance, instance->last_error); lcb_aspend_del(&instance->pendops, LCB_PENDTYPE_COUNTER, NULL); lcb_maybe_breakout(instance); }
static void event_complete_common(lcb_server_t *c, lcb_error_t rc) { lcb_t instance = c->instance; if (rc != LCB_SUCCESS) { lcb_failout_server(c, rc); } else { if (c->is_config_node) { c->instance->weird_things = 0; } lcb_sockrw_apply_want(&c->connection); c->inside_handler = 0; } if (instance->compat.type == LCB_CACHED_CONFIG && instance->compat.value.cached.needs_update) { lcb_refresh_config_cache(instance); } lcb_maybe_breakout(instance); lcb_error_handler(instance, rc, NULL); }
static void timeout_handler(lcb_connection_t conn, lcb_error_t err) { lcb_t instance = (lcb_t)conn->data; const char *msg = "Configuration update timed out"; lcb_assert(instance->config.state != LCB_CONFSTATE_CONFIGURED); if (instance->config.state == LCB_CONFSTATE_UNINIT) { /** * If lcb_connect was called explicitly then it means there are no * pending operations and we should just break out because we have * no valid configuration. */ lcb_error_handler(instance, LCB_CONNECT_ERROR, "Could not connect to server within allotted time"); lcb_maybe_breakout(instance); return; } lcb_bootstrap_error(instance, err, msg, 0); }
/** * Actually free the resources allocated by the dset (and all its entries). * Called by some other functions in libcouchbase */ void lcbdur_destroy(lcb_DURSET *dset) { lcb_t instance = dset->instance; if (DSET_PROCS(dset)->clean) { DSET_PROCS(dset)->clean(dset); } if (dset->timer) { lcbio_TABLE *io = instance->iotable; io->timer.cancel(io->p, dset->timer); io->timer.destroy(io->p, dset->timer); dset->timer = NULL; } lcb_aspend_del(&dset->instance->pendops, LCB_PENDTYPE_DURABILITY, dset); LCB_SSOBUF_CLEAN(&dset->entries_); lcb_string_release(&dset->kvbufs); free(dset); lcb_maybe_breakout(instance); }
static void initial_bootstrap_error(lcb_t instance, lcb_error_t err, const char *errinfo) { instance->last_error = lcb_confmon_last_error(instance->confmon); if (instance->last_error == LCB_SUCCESS) { instance->last_error = err; } instance->bootstrap->active = 0 ; lcb_error_handler(instance, instance->last_error, errinfo); lcb_log(LOGARGS(instance, ERR), "Failed to bootstrap client=%p. Code=0x%x, Message=%s", (void *)instance, err, errinfo); if (instance->bootstrap->timer) { lcb_timer_destroy(instance, instance->bootstrap->timer); instance->bootstrap->timer = NULL; } lcb_maybe_breakout(instance); }
/** * Actually free the resources allocated by the dset (and all its entries). * Called by some other functions in libcouchbase */ void lcb_durability_dset_destroy(lcb_DURSET *dset) { lcb_t instance = dset->instance; if (dset->timer) { lcbio_TABLE *io = instance->iotable; io->timer.cancel(io->p, dset->timer); io->timer.destroy(io->p, dset->timer); dset->timer = NULL; } lcb_aspend_del(&dset->instance->pendops, LCB_PENDTYPE_DURABILITY, dset); if (dset->nentries > 1) { if (dset->ht) { genhash_free(dset->ht); } free(dset->entries); } lcb_string_release(&dset->kvbufs); free(dset); lcb_maybe_breakout(instance); }
static void purge_single_server(lcb_server_t *server, lcb_error_t error, hrtime_t min_nonstale, hrtime_t *tmo_next) { protocol_binary_request_header req; struct lcb_command_data_st ct; lcb_size_t nr; char *packet; lcb_size_t packetsize; char *keyptr; ringbuffer_t rest; ringbuffer_t *stream = &server->cmd_log; ringbuffer_t *cookies; ringbuffer_t *mirror = NULL; /* mirror buffer should be purged with main stream */ lcb_connection_t conn = &server->connection; lcb_size_t send_size = 0; lcb_size_t stream_size = ringbuffer_get_nbytes(stream); hrtime_t now = gethrtime(); if (server->connection_ready) { cookies = &server->output_cookies; } else { cookies = &server->pending_cookies; mirror = &server->pending; } if (conn->output) { /* This will usually be false for v1 */ send_size = ringbuffer_get_nbytes(conn->output); } lcb_assert(ringbuffer_initialize(&rest, 1024)); do { int allocated = 0; lcb_uint32_t headersize; lcb_uint16_t nkey; nr = ringbuffer_peek(cookies, &ct, sizeof(ct)); if (nr != sizeof(ct)) { break; } nr = ringbuffer_peek(stream, req.bytes, sizeof(req)); if (nr != sizeof(req)) { break; } packetsize = (lcb_uint32_t)sizeof(req) + ntohl(req.request.bodylen); if (stream->nbytes < packetsize) { break; } if (min_nonstale && ct.start >= min_nonstale) { lcb_log(LOGARGS(server, INFO), "Still have %d ms remaining for command", (ct.start - min_nonstale) / 1000000); if (tmo_next) { *tmo_next = (ct.start - min_nonstale) + 1; } break; } lcb_log(LOGARGS(server, INFO), "Command with cookie=%p timed out from server %s:%s", ct.cookie, server->curhost.host, server->curhost.port); ringbuffer_consumed(cookies, sizeof(ct)); lcb_assert(nr == sizeof(req)); packet = stream->read_head; if (server->instance->histogram) { lcb_record_metrics(server->instance, now - ct.start, req.request.opcode); } if (server->connection_ready && stream_size > send_size && (stream_size - packetsize) < send_size) { /* Copy the rest of the current packet into the temporary stream */ /* I do believe I have some IOV functions to do that? */ lcb_size_t nbytes = packetsize - (stream_size - send_size); lcb_assert(ringbuffer_memcpy(&rest, conn->output, nbytes) == 0); ringbuffer_consumed(conn->output, nbytes); send_size -= nbytes; } stream_size -= packetsize; headersize = (lcb_uint32_t)sizeof(req) + req.request.extlen + htons(req.request.keylen); if (!ringbuffer_is_continous(stream, RINGBUFFER_READ, headersize)) { packet = malloc(headersize); if (packet == NULL) { lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); abort(); } nr = ringbuffer_peek(stream, packet, headersize); if (nr != headersize) { lcb_error_handler(server->instance, LCB_EINTERNAL, NULL); free(packet); abort(); } allocated = 1; } keyptr = packet + sizeof(req) + req.request.extlen; nkey = ntohs(req.request.keylen); failout_single_request(server, &req, &ct, error, keyptr, nkey, packet); if (allocated) { free(packet); } ringbuffer_consumed(stream, packetsize); if (mirror) { ringbuffer_consumed(mirror, packetsize); } } while (1); /* CONSTCOND */ if (server->connection_ready && conn->output) { /* Preserve the rest of the stream */ lcb_size_t nbytes = ringbuffer_get_nbytes(stream); send_size = ringbuffer_get_nbytes(conn->output); if (send_size >= nbytes) { ringbuffer_consumed(conn->output, send_size - nbytes); lcb_assert(ringbuffer_memcpy(&rest, conn->output, nbytes) == 0); } ringbuffer_reset(conn->output); ringbuffer_append(&rest, conn->output); } ringbuffer_destruct(&rest); lcb_maybe_breakout(server->instance); }
/** * Common function to handle parsing the event loop for both v0 and v1 io * implementations. */ static lcb_error_t handle_vbstream_read(lcb_t instance) { lcb_error_t err; int can_retry = 0; int old_gen = instance->config.generation; struct lcb_http_bootstrap_st *http = &instance->bootstrap.via.http; lcb_connection_t conn = &instance->bootstrap.via.http.connection; err = lcb_parse_vbucket_stream(instance); if (err == LCB_SUCCESS) { if (instance->type == LCB_TYPE_BUCKET) { lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); } if (old_gen != instance->config.generation || instance->type == LCB_TYPE_CLUSTER) { lcb_connection_cancel_timer(conn); conn->timeout.usec = 0; lcb_maybe_breakout(instance); } return LCB_SUCCESS; } else if (err != LCB_BUSY) { /** * XXX: We only want to retry on some errors. Things which signify an * obvious user error should be left out here; we only care about * actual "network" errors */ switch (err) { case LCB_ENOMEM: case LCB_AUTH_ERROR: case LCB_PROTOCOL_ERROR: case LCB_BUCKET_ENOENT: can_retry = 0; break; default: can_retry = 1; } if (http->bummer && (err == LCB_BUCKET_ENOENT || err == LCB_AUTH_ERROR)) { can_retry = 1; } if (can_retry) { const char *msg = "Failed to get configuration"; lcb_bootstrap_error(instance, err, msg, LCB_CONFERR_NO_BREAKOUT); return err; } else { lcb_maybe_breakout(instance); return lcb_error_handler(instance, err, ""); } } lcb_assert(err == LCB_BUSY); lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); if (old_gen != instance->config.generation) { lcb_connection_cancel_timer(conn); conn->timeout.usec = 0; lcb_maybe_breakout(instance); } return LCB_BUSY; }
/** * 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); }