/* * 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 * buf buffer for response string * * returns a response string 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, uint64_t *rcas, 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(delta > value) { value = 0; } else { value -= delta; } } *result = value; if ((res = snprintf(buf, sizeof(buf), "%" PRIu64, value)) == -1) { return ENGINE_EINVAL; } if (it->refcount == 1 && res <= 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()); *rcas = item_get_cas(it); } else { hash_item *new_it = do_item_alloc(engine, item_get_key(it), it->nkey, it->flags, it->exptime, res, cookie); 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); *rcas = item_get_cas(new_it); do_item_release(engine, new_it); /* release our reference */ } return ENGINE_SUCCESS; }
static ENGINE_ERROR_CODE do_item_dcp_step(struct default_engine *engine, struct dcp_connection *connection, const void *cookie, struct dcp_message_producers *producers) { ENGINE_ERROR_CODE ret = ENGINE_DISCONNECT; while (connection->it == NULL) { if (!do_item_walk_cursor(engine, &connection->cursor, 1, item_dcp_iterfunc, connection, &ret)) { /* find next slab class to look at.. */ bool linked = false; int ii; for (ii = connection->cursor.slabs_clsid + 1; ii < POWER_LARGEST && !linked; ++ii) { if (engine->items.heads[ii] != NULL) { /* add the item at the tail */ do_item_link_cursor(engine, &connection->cursor, ii); linked = true; } } if (!linked) { break; } } } if (connection->it != NULL) { rel_time_t current_time = engine->server.core->get_current_time(); rel_time_t exptime = connection->it->exptime; if (exptime != 0 && exptime < current_time) { const hash_key* key = item_get_key(connection->it); ret = producers->expiration(cookie, connection->opaque, hash_key_get_client_key(key), hash_key_get_client_key_len(key), item_get_cas(connection->it), 0, 0, 0, NULL, 0); if (ret == ENGINE_SUCCESS) { do_item_unlink(engine, connection->it); do_item_release(engine, connection->it); } } else { ret = producers->mutation(cookie, connection->opaque, connection->it, 0, 0, 0, 0, NULL, 0, 0); } if (ret == ENGINE_SUCCESS) { connection->it = NULL; } } else { return ENGINE_DISCONNECT; } return ret; }
/* * Stores an item in the cache (high level, obeys set/add/replace semantics) */ ENGINE_ERROR_CODE store_item(struct default_engine *engine, hash_item *item, uint64_t *cas, ENGINE_STORE_OPERATION operation, const void *cookie) { ENGINE_ERROR_CODE ret; hash_item* stored_item = NULL; cb_mutex_enter(&engine->items.lock); ret = do_store_item(engine, item, operation, cookie, &stored_item); if (ret == ENGINE_SUCCESS) { *cas = item_get_cas(stored_item); } cb_mutex_exit(&engine->items.lock); return ret; }
static bool get_item_info(ENGINE_HANDLE *handle, const void *cookie, const item* item, item_info *item_info) { mock_item* it = (mock_item*)item; if (item_info->nvalue < 1) { return false; } item_info->cas = item_get_cas(it); item_info->exptime = it->exptime; item_info->nbytes = it->nbytes; item_info->flags = it->flags; item_info->clsid = it->clsid; item_info->nkey = it->nkey; item_info->nvalue = 1; item_info->key = item_get_key(it); item_info->value[0].iov_base = item_get_data(it); item_info->value[0].iov_len = it->nbytes; return true; }
static ENGINE_ERROR_CODE do_arithmetic(struct default_engine *engine, const void* cookie, const void* key, const int nkey, const bool increment, const bool create, const uint64_t delta, const uint64_t initial, const rel_time_t exptime, uint64_t *cas, uint64_t *result) { hash_item *item = do_item_get(engine, key, nkey); ENGINE_ERROR_CODE ret; if (item == NULL) { if (!create) { return ENGINE_KEY_ENOENT; } else { char buffer[128]; int len = snprintf(buffer, sizeof(buffer), "%"PRIu64, (uint64_t)initial); item = do_item_alloc(engine, key, nkey, 0, exptime, len, cookie); if (item == NULL) { return ENGINE_ENOMEM; } memcpy((void*)item_get_data(item), buffer, len); if ((ret = do_store_item(engine, item, cas, OPERATION_ADD, cookie)) == ENGINE_SUCCESS) { *result = initial; *cas = item_get_cas(item); } do_item_release(engine, item); } } else { ret = do_add_delta(engine, item, increment, delta, cas, result, cookie); do_item_release(engine, item); } return ret; }
static bool _get_key(struct response *rsp, struct bstring *key) { struct item *it; it = item_get(key); if (it != NULL) { rsp->type = RSP_VALUE; rsp->key = *key; rsp->flag = item_flag(it); rsp->vcas = item_get_cas(it); rsp->vstr.len = it->vlen; rsp->vstr.data = item_data(it); log_verb("found key at %p, location %p", key, it); return true; } else { log_verb("key at %p not found", key); return false; } }
/* * Stores an item in the cache according to the semantics of one of the set * commands. In threaded mode, this is protected by the cache lock. * * Returns the state of storage. */ static ENGINE_ERROR_CODE do_store_item(struct default_engine *engine, hash_item *it, uint64_t *cas, ENGINE_STORE_OPERATION operation, const void *cookie) { const char *key = item_get_key(it); hash_item *old_it = do_item_get(engine, key, it->nkey); ENGINE_ERROR_CODE stored = ENGINE_NOT_STORED; hash_item *new_it = NULL; if (old_it != NULL && operation == OPERATION_ADD) { /* add only adds a nonexistent item, but promote to head of LRU */ do_item_update(engine, old_it); } else if (!old_it && (operation == OPERATION_REPLACE || operation == OPERATION_APPEND || operation == OPERATION_PREPEND)) { /* replace only replaces an existing value; don't store */ } else if (operation == OPERATION_CAS) { /* validate cas operation */ if(old_it == NULL) { // LRU expired stored = ENGINE_KEY_ENOENT; } else if (item_get_cas(it) == item_get_cas(old_it)) { // cas validates // it and old_it may belong to different classes. // I'm updating the stats for the one that's getting pushed out do_item_replace(engine, old_it, it); stored = ENGINE_SUCCESS; } else { if (engine->config.verbose > 1) { EXTENSION_LOGGER_DESCRIPTOR *logger; logger = (void*)engine->server.extension->get_extension(EXTENSION_LOGGER); logger->log(EXTENSION_LOG_INFO, NULL, "CAS: failure: expected %"PRIu64", got %"PRIu64"\n", item_get_cas(old_it), item_get_cas(it)); } stored = ENGINE_KEY_EEXISTS; } } else { /* * Append - combine new and old record into single one. Here it's * atomic and thread-safe. */ if (operation == OPERATION_APPEND || operation == OPERATION_PREPEND) { /* * Validate CAS */ if (item_get_cas(it) != 0) { // CAS much be equal if (item_get_cas(it) != item_get_cas(old_it)) { stored = ENGINE_KEY_EEXISTS; } } if (stored == ENGINE_NOT_STORED) { /* we have it and old_it here - alloc memory to hold both */ new_it = do_item_alloc(engine, key, it->nkey, old_it->flags, old_it->exptime, it->nbytes + old_it->nbytes, cookie); if (new_it == NULL) { /* SERVER_ERROR out of memory */ if (old_it != NULL) { do_item_release(engine, old_it); } return ENGINE_NOT_STORED; } /* copy data from it and old_it to new_it */ if (operation == OPERATION_APPEND) { memcpy(item_get_data(new_it), item_get_data(old_it), old_it->nbytes); memcpy(item_get_data(new_it) + old_it->nbytes, item_get_data(it), it->nbytes); } else { /* OPERATION_PREPEND */ memcpy(item_get_data(new_it), item_get_data(it), it->nbytes); memcpy(item_get_data(new_it) + it->nbytes, item_get_data(old_it), old_it->nbytes); } it = new_it; } } if (stored == ENGINE_NOT_STORED) { if (old_it != NULL) { do_item_replace(engine, old_it, it); } else { do_item_link(engine, it); } *cas = item_get_cas(it); stored = ENGINE_SUCCESS; } } if (old_it != NULL) { do_item_release(engine, old_it); /* release our reference */ } if (new_it != NULL) { do_item_release(engine, new_it); } if (stored == ENGINE_SUCCESS) { *cas = item_get_cas(it); } return stored; }