Beispiel #1
0
/**
 * Schedules us to be notified with the given state within a particular amount
 * of time. This is used both for the timeout and for the interval
 */
void
lcbdur_switch_state(lcb_DURSET *dset, unsigned int state)
{
    lcb_U32 delay = 0;
    lcbio_TABLE* io = dset->instance->iotable;
    hrtime_t now = gethrtime();

    if (state == LCBDUR_STATE_TIMEOUT) {
        if (dset->ns_timeout && now < dset->ns_timeout) {
            delay = LCB_NS2US(dset->ns_timeout - now);
        } else {
            delay = 0;
        }
    } else if (state == LCBDUR_STATE_OBSPOLL) {
        if (now + LCB_US2NS(DSET_OPTFLD(dset, interval)) < dset->ns_timeout) {
            delay = DSET_OPTFLD(dset, interval);
        } else {
            delay = 0;
            state = LCBDUR_STATE_TIMEOUT;
        }
    } else if (state == LCBDUR_STATE_INIT) {
        delay = 0;
    }

    dset->next_state = state;
    io->timer.cancel(io->p, dset->timer);
    io->timer.schedule(io->p, dset->timer, delay, dset, timer_callback);
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}
Beispiel #4
0
static int
server_criteria_satisfied(const lcb_DURITEM *item,
    const lcbdur_SERVINFO *info, int is_master)
{
    const lcb_DURSET *dset = item->parent;
    if (!info->exists) {
        return 0;
    }
    if (info->persisted) {
        return 1;
    }
    if (DSET_OPTFLD(dset, persist_to) == 0) {
        return 1;
    }
    if (DSET_OPTFLD(dset, persist_to) == 1 && !is_master) {
        return 1;
    }
    return 0;
}
/**
 * Called when the last (primitive) OBSERVE response is received for the entry.
 */
static void dset_done_waiting(lcb_durability_set_t *dset)
{
    lcb_assert(dset->waiting || ("Got NULL callback twice!" && 0));

    dset->waiting = 0;

    if (dset->nremaining > 0) {
        timer_schedule(dset, DSET_OPTFLD(dset, interval), STATE_OBSPOLL);
    }
    dset_unref(dset);
}
Beispiel #6
0
static lcb_error_t
dset_ctx_schedule(lcb_MULTICMD_CTX *mctx, const void *cookie)
{
    size_t ii;
    lcb_error_t err;
    lcb_DURSET *dset = CTX_FROM_MULTI(mctx);
    char *kptr = dset->kvbufs.base;

    if (!DSET_COUNT(dset)) {
        lcbdur_destroy(dset);
        return LCB_EINVAL;
    }

    for (ii = 0; ii < DSET_COUNT(dset); ii++) {
        lcb_DURITEM *ent = DSET_ENTRIES(dset) + ii;
        RESFLD(ent, key) = kptr;
        kptr += RESFLD(ent, nkey);
    }

    if (DSET_PROCS(dset)->schedule) {
        err = DSET_PROCS(dset)->schedule(dset);
        if (err != LCB_SUCCESS) {
            lcbdur_destroy(dset);
            return err;
        }
    }

    lcbdur_ref(dset);
    dset->cookie = cookie;
    dset->nremaining = DSET_COUNT(dset);
    dset->ns_timeout = gethrtime() + LCB_US2NS(DSET_OPTFLD(dset, timeout));

    lcb_aspend_add(&dset->instance->pendops, LCB_PENDTYPE_DURABILITY, dset);
    lcbdur_switch_state(dset, LCBDUR_STATE_INIT);
    return LCB_SUCCESS;
}
Beispiel #7
0
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
lcb_error_t lcb_durability_poll(lcb_t instance,
                                const void *cookie,
                                const lcb_durability_opts_t *options,
                                lcb_size_t ncmds,
                                const lcb_durability_cmd_t *const *cmds)
{
    hrtime_t now = gethrtime();
    lcb_durability_set_t *dset;
    lcb_size_t ii;
    lcb_io_opt_t io = instance->settings.io;

    if (!ncmds) {
        return LCB_EINVAL;
    }

    dset = calloc(1, sizeof(*dset));
    if (!dset) {
        return LCB_CLIENT_ENOMEM;
    }

    dset->opts = *options;
    dset->instance = instance;

    if (!DSET_OPTFLD(dset, timeout)) {
        DSET_OPTFLD(dset, timeout) = instance->settings.durability_timeout;
    }

    if (-1 == verify_critera(instance, dset)) {
        free(dset);
        return LCB_DURABILITY_ETOOMANY;
    }

    /* set our timeouts now */
    dset->us_timeout = (lcb_uint32_t)(now / 1000) + DSET_OPTFLD(dset, timeout);
    dset->timer = io->v.v0.create_timer(io);
    dset->cookie = cookie;
    dset->nentries = ncmds;
    dset->nremaining = ncmds;

    /** Get the timings */
    if (!DSET_OPTFLD(dset, interval)) {
        DSET_OPTFLD(dset, interval) = LCB_DEFAULT_DURABILITY_INTERVAL;
    }

    /* list of observe commands to schedule */
    if (dset->nentries == 1) {
        dset->entries = &dset->single.ent;
        dset->valid_entries = &dset->single.entp;

    } else {
        dset->ht = lcb_hashtable_nc_new(dset->nentries);
        dset->entries = calloc(dset->nentries, sizeof(*dset->entries));
        dset->valid_entries = malloc(dset->nentries * sizeof(*dset->valid_entries));
        if (dset->entries == NULL || dset->valid_entries == NULL) {
            lcb_durability_dset_destroy(dset);
            return LCB_CLIENT_ENOMEM;
        }
    }

    /* set up the observe commands */
    for (ii = 0; ii < dset->nentries; ii++) {
        lcb_durability_entry_t *ent = dset->entries + ii;
        ent_init(cmds[ii], ent);
        ent->parent = dset;

        if (dset->nentries > 1) {
            int mt = genhash_update(dset->ht,
                                    REQFLD(ent, key),
                                    REQFLD(ent, nkey),
                                    ent, 0);
            if (mt != NEW) {
                lcb_durability_dset_destroy(dset);
                return LCB_DUPLICATE_COMMANDS;
            }
        }
    }

    /**
     * Increase the refcount by one. This will be decremented
     * when the remaining_total count hits 0
     */

    dset_ref(dset);
    hashset_add(instance->durability_polls, dset);
    timer_schedule(dset, 0, STATE_OBSPOLL);
    return lcb_synchandler_return(instance, LCB_SUCCESS);
}
/**
 * Observe callback. Called internally by libcouchbase's observe handlers
 */
void lcb_durability_dset_update(lcb_t instance,
                                lcb_durability_set_t *dset,
                                lcb_error_t err,
                                const lcb_observe_resp_t *resp)
{
    lcb_durability_entry_t *ent;

    /**
     * So we have two counters to decrement. One is the global 'done' counter
     * and the other is the iteration counter.
     *
     * The iteration counter is only decremented when we receive a NULL signal
     * in the callback, whereas the global counter is decremented once, whenever
     * the entry's criteria have been satisfied
     */

    if (resp->v.v0.key == NULL) {
        dset_done_waiting(dset);
        return;
    }

    if (dset->nentries == 1) {
        ent = &dset->single.ent;
    } else {
        ent = genhash_find(dset->ht, resp->v.v0.key, resp->v.v0.nkey);
    }

    if (ent->done) {
        /* ignore subsequent errors */
        return;
    }

    if (err != LCB_SUCCESS) {
        RESFLD(ent, err) = err;
        return;
    }

    RESFLD(ent, nresponses)++;

    if (resp->v.v0.cas && resp->v.v0.from_master) {

        RESFLD(ent, cas) = resp->v.v0.cas;

        if (REQFLD(ent, cas) && REQFLD(ent, cas) != resp->v.v0.cas) {
            RESFLD(ent, err) = LCB_KEY_EEXISTS;
            ent_set_resdone(ent);
            return;
        }
    }

    if (DSET_OPTFLD(ent->parent, check_delete)) {
        check_negative_durability(ent, resp);

    } else {
        check_positive_durability(ent, resp);
    }

    if (ent_is_complete(ent)) {
        /* clear any transient errors */
        RESFLD(ent, err) = LCB_SUCCESS;
        ent_set_resdone(ent);
    }

    (void)instance;

}
Beispiel #10
0
/**
 * Observe callback. Called internally by libcouchbase's observe handlers
 */
void
lcb_durability_dset_update(lcb_t instance,
    lcb_DURSET *dset, lcb_error_t err, const lcb_RESPOBSERVE *resp)
{
    lcb_DURITEM *ent;

    /**
     * So we have two counters to decrement. One is the global 'done' counter
     * and the other is the iteration counter.
     *
     * The iteration counter is only decremented when we receive a NULL signal
     * in the callback, whereas the global counter is decremented once, whenever
     * the entry's criteria have been satisfied
     */

    if (resp->key == NULL) {
        dset_done_waiting(dset);
        return;
    }

    if (dset->nentries == 1) {
        ent = &dset->single.ent;
    } else {
        ent = genhash_find(dset->ht, resp->key, resp->nkey);
    }

    if (ent->done) {
        /* ignore subsequent errors */
        return;
    }

    if (err != LCB_SUCCESS) {
        RESFLD(ent, rc) = err;
        /* If it's a non-scheduling error then the item will be retried in the
         * next iteration */
        if (err == LCB_SCHEDFAIL_INTERNAL) {
            ent_set_resdone(ent);
        }
        return;
    }

    RESFLD(ent, nresponses)++;
    if (resp->cas && resp->ismaster) {
        RESFLD(ent, cas) = resp->cas;

        if (ent->reqcas && ent->reqcas != resp->cas) {
            RESFLD(ent, rc) = LCB_KEY_EEXISTS;
            ent_set_resdone(ent);
            return;
        }
    }

    if (DSET_OPTFLD(ent->parent, check_delete)) {
        check_negative_durability(ent, resp);
    } else {
        check_positive_durability(ent, resp);
    }

    if (ent_is_complete(ent)) {
        /* clear any transient errors */
        RESFLD(ent, rc) = LCB_SUCCESS;
        ent_set_resdone(ent);
    }

    (void)instance;

}