Example #1
0
/*
 * adds a delta value to a numeric item.
 *
 * c     connection requesting the operation
 * it    item to adjust
 * incr  true to increment value, false to decrement
 * delta amount to adjust value by
 * @param ritem The resulting item after adding the delta. Only valid if
 *              ENGINE_SUCCESS is returned. Caller is responsible for calling
 *              do_item_release() on this when finished with it.
 *
 * returns a response code to send back to the client.
 */
static ENGINE_ERROR_CODE do_add_delta(struct default_engine *engine,
                                      hash_item *it, const bool incr,
                                      const int64_t delta, item** ritem,
                                      uint64_t *result, const void *cookie) {
    const char *ptr;
    uint64_t value;
    char buf[80];
    int res;

    if (it->nbytes >= (sizeof(buf) - 1)) {
        return ENGINE_EINVAL;
    }

    ptr = item_get_data(it);
    memcpy(buf, ptr, it->nbytes);
    buf[it->nbytes] = '\0';

    if (!safe_strtoull(buf, &value)) {
        return ENGINE_EINVAL;
    }

    if (incr) {
        value += delta;
    } else {
        if ((uint64_t)delta > value) {
            value = 0;
        } else {
            value -= delta;
        }
    }

    *result = value;

    res = snprintf(buf, sizeof(buf), "%" PRIu64, value);
    if (res < 0 || res >= sizeof(buf)) {
        return ENGINE_EINVAL;
    }

    if (it->refcount == 1 && res <= (int)it->nbytes) {
        /* we can do inline replacement */
        memcpy(item_get_data(it), buf, res);
        memset(item_get_data(it) + res, ' ', it->nbytes - res);
        item_set_cas(NULL, NULL, it, get_cas_id());
        *ritem = it;
    } else {
        hash_item *new_it = do_item_alloc(engine, item_get_key(it),
                                          it->flags,
                                          it->exptime, res,
                                          cookie, it->datatype);
        if (new_it == NULL) {
            do_item_unlink(engine, it);
            return ENGINE_ENOMEM;
        }
        memcpy(item_get_data(new_it), buf, res);
        do_item_replace(engine, it, new_it);
        *ritem = new_it;
    }

    return ENGINE_SUCCESS;
}
Example #2
0
File: item.c Project: huayl/pelikan
void
item_update(struct item *it, const struct bstring *val)
{
    ASSERT(item_slabid(it->klen, val->len, it->olen) == it->id);

    it->vlen = val->len;
    cc_memcpy(item_data(it), val->data, val->len);
    item_set_cas(it);

    log_verb("update it %p of id %"PRIu8, it, it->id);
}
Example #3
0
File: item.c Project: huayl/pelikan
/* TODO(yao): move this to memcache-specific location */
static void
_item_define(struct item *it, const struct bstring *key, const struct bstring
        *val, uint8_t olen, proc_time_i expire_at)
{
    proc_time_i expire_cap = time_delta2proc_sec(max_ttl);

    it->create_at = time_proc_sec();
    it->expire_at = expire_at < expire_cap ? expire_at : expire_cap;
    item_set_cas(it);
    it->olen = olen;
    cc_memcpy(item_key(it), key->data, key->len);
    it->klen = key->len;
    if (val != NULL) {
        cc_memcpy(item_data(it), val->data, val->len);
    }
    it->vlen = (val == NULL) ? 0 : val->len;
}
Example #4
0
int do_item_link(struct default_engine *engine, hash_item *it) {
    MEMCACHED_ITEM_LINK(item_get_key(it), it->nkey, it->nbytes);
    assert((it->iflag & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    assert(it->nbytes < (1024 * 1024));  /* 1MB max size */
    it->iflag |= ITEM_LINKED;
    it->time = engine->server.core->get_current_time();
    assoc_insert(engine, engine->server.core->hash(item_get_key(it),
                                                        it->nkey, 0),
                 it);

    pthread_mutex_lock(&engine->stats.lock);
    engine->stats.curr_bytes += ITEM_ntotal(engine, it);
    engine->stats.curr_items += 1;
    engine->stats.total_items += 1;
    pthread_mutex_unlock(&engine->stats.lock);

    /* Allocate a new CAS ID on link. */
    item_set_cas(NULL, NULL, it, get_cas_id());

    item_link_q(engine, it);

    return 1;
}
Example #5
0
int do_item_link(struct default_engine *engine, hash_item *it) {
    const hash_key* key = item_get_key(it);
    MEMCACHED_ITEM_LINK(hash_key_get_client_key(key), hash_key_get_client_key_len(key), it->nbytes);
    cb_assert((it->iflag & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    it->iflag |= ITEM_LINKED;
    it->time = engine->server.core->get_current_time();

    assoc_insert(engine, crc32c(hash_key_get_key(key),
                                hash_key_get_key_len(key), 0),
                 it);

    cb_mutex_enter(&engine->stats.lock);
    engine->stats.curr_bytes += ITEM_ntotal(engine, it);
    engine->stats.curr_items += 1;
    engine->stats.total_items += 1;
    cb_mutex_exit(&engine->stats.lock);

    /* Allocate a new CAS ID on link. */
    item_set_cas(NULL, NULL, it, get_cas_id());

    item_link_q(engine, it);

    return 1;
}
Example #6
0
static void
asc_process_update(struct conn *c, struct token *token, int ntoken)
{
    char *key;
    size_t nkey;
    uint32_t flags, vlen;
    int32_t exptime_int;
    time_t exptime;
    uint64_t req_cas_id = 0;
    struct item *it;
    bool handle_cas;
    req_type_t type;
    uint8_t id;

    asc_set_noreply_maybe(c, token, ntoken);

    if (!asc_ntoken_valid(c, ntoken)) {
        log_hexdump(LOG_NOTICE, c->req, c->req_len, "client error on c %d for "
                    "req of type %d with %d invalid tokens", c->sd,
                    c->req_type, ntoken);

        asc_write_client_error(c);
        return;
    }

    type = c->req_type;
    handle_cas = (type == REQ_CAS) ? true : false;
    key = token[TOKEN_KEY].val;
    nkey = token[TOKEN_KEY].len;

    if (nkey > KEY_MAX_LEN) {
        log_debug(LOG_NOTICE, "client error on c %d for req of type %d and %d "
                  "length key", c->sd, c->req_type, nkey);

        asc_write_client_error(c);
        return;
    }

    if (!mc_strtoul(token[TOKEN_FLAGS].val, &flags)) {
        log_debug(LOG_NOTICE, "client error on c %d for req of type %d and "
                  "invalid flags '%.*s'", c->sd, c->req_type,
                  token[TOKEN_FLAGS].len, token[TOKEN_FLAGS].val);

        asc_write_client_error(c);
        return;
    }

    if (!mc_strtol(token[TOKEN_EXPIRY].val, &exptime_int)) {
        log_debug(LOG_NOTICE, "client error on c %d for req of type %d and "
                  "invalid expiry '%.*s'", c->sd, c->req_type,
                  token[TOKEN_EXPIRY].len, token[TOKEN_EXPIRY].val);

        asc_write_client_error(c);
        return;
    }

    if (!mc_strtoul(token[TOKEN_VLEN].val, &vlen)) {
        log_debug(LOG_NOTICE, "client error on c %d for req of type %d and "
                  "invalid vlen '%.*s'", c->sd, c->req_type,
                  token[TOKEN_VLEN].len, token[TOKEN_VLEN].val);

        asc_write_client_error(c);
        return;
    }

    id = item_slabid(nkey, vlen);
    if (id == SLABCLASS_INVALID_ID) {
        log_debug(LOG_NOTICE, "client error on c %d for req of type %d and "
                  "slab id out of range for key size %"PRIu8" and value size "
                  "%"PRIu32, c->sd, c->req_type, nkey, vlen);

        asc_write_client_error(c);
        return;
    }

    exptime = (time_t)exptime_int;

    /* does cas value exist? */
    if (handle_cas) {
        if (!mc_strtoull(token[TOKEN_CAS].val, &req_cas_id)) {
            log_debug(LOG_NOTICE, "client error on c %d for req of type %d and "
                      "invalid cas '%.*s'", c->sd, c->req_type,
                      token[TOKEN_CAS].len, token[TOKEN_CAS].val);

            asc_write_client_error(c);
            return;
        }
    }

    it = item_alloc(id, key, nkey, flags, time_reltime(exptime), vlen);
    if (it == NULL) {
        log_warn("server error on c %d for req of type %d because of oom in "
                 "storing item", c->sd, c->req_type);

        asc_write_server_error(c);

        /* swallow the data line */
        c->write_and_go = CONN_SWALLOW;
        c->sbytes = vlen + CRLF_LEN;

        /*
         * Avoid stale data persisting in cache because we failed alloc.
         * Unacceptable for SET. Anywhere else too?
         *
         * FIXME: either don't delete anything or should be unacceptable for
         * all but add.
         */
        if (type == REQ_SET) {
            it = item_get(key, nkey);
            if (it != NULL) {
                item_delete(it);
            }
        }
        return;
    }

    item_set_cas(it, req_cas_id);

    c->item = it;
    c->ritem = item_data(it);
    c->rlbytes = it->nbyte + CRLF_LEN;
    conn_set_state(c, CONN_NREAD);
}
Example #7
0
static void
asc_process_update(struct conn *c, struct token *token, int ntoken)
{
    char *key;
    size_t nkey;
    unsigned int flags;
    int32_t exptime_int;
    time_t exptime;
    int vlen;
    uint64_t req_cas_id = 0;
    struct item *it;
    bool handle_cas;
    req_type_t type;

    asc_set_noreply_maybe(c, token, ntoken);

    if (!asc_ntoken_valid(c, ntoken)) {
        log_hexdump(LOG_INFO, c->req, c->req_len, "client error on c %d for "
                    "req of type %d with %d invalid tokens", c->sd,
                    c->req_type, ntoken);
        asc_write_client_error(c);
        return;
    }

    type = c->req_type;
    handle_cas = (type == REQ_CAS) ? true : false;
    key = token[TOKEN_KEY].val;
    nkey = token[TOKEN_KEY].len;

    if (nkey > KEY_MAX_LEN) {
        log_debug(LOG_INFO, "client error on c %d for req of type %d and %d "
                  "length key", c->sd, c->req_type, nkey);
        asc_write_client_error(c);
        return;
    }

    if (!mc_strtoul(token[TOKEN_FLAGS].val, (uint32_t *)&flags)) {
        log_debug(LOG_INFO, "client error on c %d for req of type %d and "
                  "invalid flags '%.*s'", c->sd, c->req_type,
                  token[TOKEN_FLAGS].len, token[TOKEN_FLAGS].val);
        asc_write_client_error(c);
        return;
    }

    if (!mc_strtol(token[TOKEN_EXPIRY].val, &exptime_int)) {
        log_debug(LOG_INFO, "client error on c %d for req of type %d and "
                  "invalid expiry '%.*s'", c->sd, c->req_type,
                  token[TOKEN_EXPIRY].len, token[TOKEN_EXPIRY].val);
        asc_write_client_error(c);
        return;
    }

    if (!mc_strtol(token[TOKEN_VLEN].val, (int32_t *)&vlen)) {
        log_debug(LOG_INFO, "client error on c %d for req of type %d and "
                  "invalid vlen '%.*s'", c->sd, c->req_type,
                  token[TOKEN_VLEN].len, token[TOKEN_VLEN].val);
        asc_write_client_error(c);
        return;
    }

    exptime = (time_t)exptime_int;

    /* does cas value exist? */
    if (handle_cas) {
        if (!mc_strtoull(token[TOKEN_CAS].val, &req_cas_id)) {
            log_debug(LOG_INFO, "client error on c %d for req of type %d and "
                      "invalid cas '%.*s'", c->sd, c->req_type,
                      token[TOKEN_CAS].len, token[TOKEN_CAS].val);
            asc_write_client_error(c);
            return;
        }
    }

    if (vlen < 0) {
        log_debug(LOG_INFO, "client error on c %d for req of type %d and "
                  "invalid vlen %d", c->sd, c->req_type, vlen);
        asc_write_client_error(c);
        return;
    }

    vlen += CRLF_LEN;

    it = item_alloc(key, nkey, flags, time_reltime(exptime), vlen);
    if (it == NULL) {
        log_debug(LOG_DEBUG, "server error on c %d for req of type %d because "
                  "of oom in storing item", c->sd, c->req_type);
        asc_write_server_error(c);

        /* swallow the data line */
        c->write_and_go = CONN_SWALLOW;
        c->sbytes = vlen;

        /*
         * Avoid stale data persisting in cache because we failed alloc.
         * Unacceptable for SET. Anywhere else too?
         */
        if (type == REQ_SET) {
            it = item_get(key, nkey);
            if (it != NULL) {
                item_unlink(it);
                item_remove(it);
            }
        }
        return;
    }

    item_set_cas(it, req_cas_id);

    c->item = it;
    c->ritem = item_data(it);
    c->rlbytes = it->nbyte;
    conn_set_state(c, CONN_NREAD);
}
Example #8
0
File: item.c Project: huayl/pelikan
item_rstatus_e
item_annex(struct item *oit, const struct bstring *key, const struct bstring
        *val, bool append)
{
    item_rstatus_e status = ITEM_OK;
    struct item *nit = NULL;
    uint8_t id;
    uint32_t ntotal = oit->vlen + val->len;

    id = item_slabid(oit->klen, ntotal, oit->olen);
    if (id == SLABCLASS_INVALID_ID) {
        log_info("client error: annex operation results in oversized item with"
                   "key size %"PRIu8" old value size %"PRIu32" and new value "
                   "size %"PRIu32, oit->klen, oit->vlen, ntotal);

        return ITEM_EOVERSIZED;
    }

    if (append) {
        /* if it is large enough to hold the extra data and left-aligned,
         * which is the default behavior, we copy the delta to the end of
         * the existing data. Otherwise, allocate a new item and store the
         * payload left-aligned.
         */
        if (id == oit->id && !(oit->is_raligned)) {
            cc_memcpy(item_data(oit) + oit->vlen, val->data, val->len);
            oit->vlen = ntotal;
            INCR_N(slab_metrics, item_keyval_byte, val->len);
            INCR_N(slab_metrics, item_val_byte, val->len);
            item_set_cas(oit);
        } else {
            status = _item_alloc(&nit, oit->klen, ntotal, oit->olen);
            if (status != ITEM_OK) {
                log_debug("annex failed due to failure to allocate new item");
                return status;
            }
            _copy_key_item(nit, oit);
            nit->expire_at = oit->expire_at;
            nit->create_at = time_proc_sec();
            item_set_cas(nit);
            /* value is left-aligned */
            cc_memcpy(item_data(nit), item_data(oit), oit->vlen);
            cc_memcpy(item_data(nit) + oit->vlen, val->data, val->len);
            nit->vlen = ntotal;
            item_insert(nit, key);
        }
    } else {
        /* if oit is large enough to hold the extra data and is already
         * right-aligned, we copy the delta to the front of the existing
         * data. Otherwise, allocate a new item and store the payload
         * right-aligned, assuming more prepends will happen in the future.
         */
        if (id == oit->id && oit->is_raligned) {
            cc_memcpy(item_data(oit) - val->len, val->data, val->len);
            oit->vlen = ntotal;
            INCR_N(slab_metrics, item_keyval_byte, val->len);
            INCR_N(slab_metrics, item_val_byte, val->len);
            item_set_cas(oit);
        } else {
            status = _item_alloc(&nit, oit->klen, ntotal, oit->olen);
            if (status != ITEM_OK) {
                log_debug("annex failed due to failure to allocate new item");
                return status;
            }
            _copy_key_item(nit, oit);
            nit->expire_at = oit->expire_at;
            nit->create_at = time_proc_sec();
            item_set_cas(nit);
            /* value is right-aligned */
            nit->is_raligned = 1;
            cc_memcpy(item_data(nit) - ntotal, val->data, val->len);
            cc_memcpy(item_data(nit) - oit->vlen, item_data(oit), oit->vlen);
            nit->vlen = ntotal;
            item_insert(nit, key);
        }
    }

    log_verb("annex to it %p of id %"PRIu8", new it at %p", oit, oit->id,
            nit ? oit : nit);

    return status;
}