Exemplo n.º 1
0
/**
 * 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);
}
Exemplo n.º 2
0
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);
}
Exemplo n.º 3
0
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);
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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);
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
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);
}
Exemplo n.º 8
0
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);
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
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);
}
Exemplo n.º 11
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);
}
Exemplo n.º 12
0
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);
}
Exemplo n.º 13
0
/**
 * 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);
}
Exemplo n.º 14
0
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);
}
Exemplo n.º 15
0
/**
 * 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;
}
Exemplo n.º 16
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 *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);
}