/* * 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; }
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); }
/* 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; }
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; }
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; }
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); }
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); }
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; }