void complete_nread(conn *c) { item *it = c->item; int comm = c->item_comm; item *old_it; time_t now = time(0); stats.set_cmds++; while(1) { if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) { out_string(c, "CLIENT_ERROR bad data chunk"); break; } old_it = assoc_find(ITEM_key(it)); if (old_it && settings.oldest_live && old_it->time <= settings.oldest_live) { item_unlink(old_it); old_it = 0; } if (old_it && old_it->exptime && old_it->exptime < now) { item_unlink(old_it); old_it = 0; } if (old_it && comm==NREAD_ADD) { item_update(old_it); out_string(c, "NOT_STORED"); break; } if (!old_it && comm == NREAD_REPLACE) { out_string(c, "NOT_STORED"); break; } if (old_it && (old_it->it_flags & ITEM_DELETED) && (comm == NREAD_REPLACE || comm == NREAD_ADD)) { out_string(c, "NOT_STORED"); break; } if (old_it) { item_replace(old_it, it); } else item_link(it); c->item = 0; out_string(c, "STORED"); return; } item_free(it); c->item = 0; return; }
enum store_item_type do_store_item ( item *it, int comm, const uint32_t hv ) { char *key = ITEM_key (it); item *old_it = do_item_get (key, it->nkey, hv); enum store_item_type stored = NOT_STORED; item *new_it = NULL; int flags; if ( old_it != NULL && comm == NREAD_ADD ) { do_item_update (old_it); } else if ( ! old_it && ( comm == NREAD_REPLACE || comm == NREAD_APPEND || comm == NREAD_PREPEND ) ) { } else { if ( comm == NREAD_APPEND || comm == NREAD_PREPEND ) { if ( stored == NOT_STORED ) { flags = ( int ) strtol (ITEM_suffix (old_it), ( char ** ) NULL, 10); new_it = do_item_alloc (key, it->nkey, flags, old_it->exptime, ITEM_data (it), it->nbytes + old_it->nbytes - 2, hv); if ( ! new_it ) { if ( old_it ) do_item_remove (old_it); return NOT_STORED; } if ( comm == NREAD_APPEND ) { memcpy (ITEM_data (new_it), ITEM_data (old_it), old_it->nbytes); memcpy (ITEM_data (new_it) + old_it->nbytes - 2, ITEM_data (it), it->nbytes); } else { memcpy (ITEM_data (new_it), ITEM_data (it), it->nbytes); memcpy (ITEM_data (new_it) + it->nbytes - 2, ITEM_data (old_it), old_it->nbytes); } it = new_it; } } if ( stored == NOT_STORED ) { if ( old_it != NULL ) { item_replace (old_it, it, hv); } else { do_item_link (it, hv); } stored = STORED; } } if ( old_it != NULL ) { do_item_remove (old_it); } if ( new_it != NULL ) { do_item_remove (new_it); } return stored; }
int LRU_list::do_store_item(base_item* it, Conn* c, uint32_t hv) { char* key = it->data; //获取旧的数据项 base_item* old_it = do_item_get(key, it->nkey, hv); int store_stat = LRU_list::NOT_STORED; base_item* new_it = 0; int flags = 0; //已经有该项item存在 if (old_it != 0 && c->cmd == NREAD_ADD) { /* * 更新当前item目的 * 1.更新时间,重建LRU链 * 2.后面执行do_item_remove,每次remove会把refcount引用计数减一 * 如果引用计数=1则被删除,重建之后refcount为2 * */ do_item_update(old_it); //旧的item不存在 }else if (!old_it && (c->cmd == NREAD_REPLACE || c->cmd == NREAD_APPEND || c->cmd == NREAD_PREPEND)) { //什么也不做,因为只有replace替换已有值 }else if (c->cmd == NREAD_CAS) { //不存在此项 if (old_it == 0) { store_stat = LRU_list::NOT_FOUND; } if (it->cas == old_it->cas) { item_replace(old_it, it, hv); store_stat = LRU_list::STORED; } else { if (mem_setting.verbose > 1) { std::cerr << "CAS: failure: expected " << old_it->get_cas() << " but got " << it->cas; } store_stat = LRU_list::EXISTS; } } else { //与上面第二个判断不同,这里是旧的item存在的replace append prepend set命令 if (c->cmd == NREAD_APPEND || c->cmd == NREAD_PREPEND) { //if (it->cas != 0) { if (it->cas != old_it->cas) { store_stat = LRU_list::EXISTS; } //} if (store_stat == LRU_list::NOT_STORED) { new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2, hv); //分配失败 if (new_it == 0) { if (old_it != 0) do_item_remove(old_it); return LRU_list::NOT_STORED; } new_it->nkey = old_it->nkey; new_it->item_flag = flags; new_it->exptime = old_it->exptime; new_it->nbytes = it->nbytes + old_it->nbytes - 2; memcpy(new_it->data, old_it->data, old_it->nkey); new_it->data[old_it->nkey] = '0'; //copy数据 if (c->cmd == NREAD_APPEND) { memcpy(new_it->real_data_addr(), old_it->real_data_addr(), old_it->nbytes); memcpy(new_it->real_data_addr() + old_it->nbytes - 2/*\r\n*/, it->real_data_addr(), it->nbytes); } else { //NREAD_PREPEND memcpy(new_it->real_data_addr(), it->real_data_addr(), it->nbytes); memcpy(new_it->real_data_addr() + it->nbytes - 2, old_it->real_data_addr(), old_it->nbytes); } it = new_it; } } if (store_stat == LRU_list::NOT_STORED) { it->cas++; if (old_it != 0) item_replace(old_it, it, hv); else //set a new key-value do_item_link(it, hv); store_stat = LRU_list::STORED; } } if (old_it != 0) do_item_remove(old_it); if (new_it != 0) do_item_remove(new_it); if (store_stat == LRU_list::STORED) { c->cas = it->get_cas(); } return store_stat; }
static int storage_write(void *storage, const int clsid, const int item_age) { int did_moves = 0; struct lru_pull_tail_return it_info; it_info.it = NULL; lru_pull_tail(clsid, COLD_LRU, 0, LRU_PULL_RETURN_ITEM, 0, &it_info); /* Item is locked, and we have a reference to it. */ if (it_info.it == NULL) { return did_moves; } obj_io io; item *it = it_info.it; /* First, storage for the header object */ size_t orig_ntotal = ITEM_ntotal(it); uint32_t flags; if ((it->it_flags & ITEM_HDR) == 0 && (item_age == 0 || current_time - it->time > item_age)) { FLAGS_CONV(it, flags); item *hdr_it = do_item_alloc(ITEM_key(it), it->nkey, flags, it->exptime, sizeof(item_hdr)); /* Run the storage write understanding the start of the item is dirty. * We will fill it (time/exptime/etc) from the header item on read. */ if (hdr_it != NULL) { int bucket = (it->it_flags & ITEM_CHUNKED) ? PAGE_BUCKET_CHUNKED : PAGE_BUCKET_DEFAULT; // Compress soon to expire items into similar pages. if (it->exptime - current_time < settings.ext_low_ttl) { bucket = PAGE_BUCKET_LOWTTL; } hdr_it->it_flags |= ITEM_HDR; io.len = orig_ntotal; io.mode = OBJ_IO_WRITE; // NOTE: when the item is read back in, the slab mover // may see it. Important to have refcount>=2 or ~ITEM_LINKED assert(it->refcount >= 2); // NOTE: write bucket vs free page bucket will disambiguate once // lowttl feature is better understood. if (extstore_write_request(storage, bucket, bucket, &io) == 0) { // cuddle the hash value into the time field so we don't have // to recalculate it. item *buf_it = (item *) io.buf; buf_it->time = it_info.hv; // copy from past the headers + time headers. // TODO: should be in items.c if (it->it_flags & ITEM_CHUNKED) { // Need to loop through the item and copy item_chunk *sch = (item_chunk *) ITEM_schunk(it); int remain = orig_ntotal; int copied = 0; // copy original header int hdrtotal = ITEM_ntotal(it) - it->nbytes; memcpy((char *)io.buf+STORE_OFFSET, (char *)it+STORE_OFFSET, hdrtotal - STORE_OFFSET); copied = hdrtotal; // copy data in like it were one large object. while (sch && remain) { assert(remain >= sch->used); memcpy((char *)io.buf+copied, sch->data, sch->used); // FIXME: use one variable? remain -= sch->used; copied += sch->used; sch = sch->next; } } else { memcpy((char *)io.buf+STORE_OFFSET, (char *)it+STORE_OFFSET, io.len-STORE_OFFSET); } // crc what we copied so we can do it sequentially. buf_it->it_flags &= ~ITEM_LINKED; buf_it->exptime = crc32c(0, (char*)io.buf+STORE_OFFSET, orig_ntotal-STORE_OFFSET); extstore_write(storage, &io); item_hdr *hdr = (item_hdr *) ITEM_data(hdr_it); hdr->page_version = io.page_version; hdr->page_id = io.page_id; hdr->offset = io.offset; // overload nbytes for the header it hdr_it->nbytes = it->nbytes; /* success! Now we need to fill relevant data into the new * header and replace. Most of this requires the item lock */ /* CAS gets set while linking. Copy post-replace */ item_replace(it, hdr_it, it_info.hv); ITEM_set_cas(hdr_it, ITEM_get_cas(it)); do_item_remove(hdr_it); did_moves = 1; LOGGER_LOG(NULL, LOG_EVICTIONS, LOGGER_EXTSTORE_WRITE, it, bucket); } else { /* Failed to write for some reason, can't continue. */ slabs_free(hdr_it, ITEM_ntotal(hdr_it), ITEM_clsid(hdr_it)); } } } do_item_remove(it); item_unlock(it_info.hv); return did_moves; }
void process_command(conn *c, char *command) { int comm = 0; int incr = 0; /* * for commands set/add/replace, we build an item and read the data * directly into it, then continue in nread_complete(). */ if (settings.verbose > 1) fprintf(stderr, "<%d %s\n", c->sfd, command); /* All incoming commands will require a response, so we cork at the beginning, and uncork at the very end (usually by means of out_string) */ set_cork(c, 1); if ((strncmp(command, "add ", 4) == 0 && (comm = NREAD_ADD)) || (strncmp(command, "set ", 4) == 0 && (comm = NREAD_SET)) || (strncmp(command, "replace ", 8) == 0 && (comm = NREAD_REPLACE))) { char key[251]; int flags; time_t expire; int len, res; item *it; res = sscanf(command, "%*s %250s %u %ld %d\n", key, &flags, &expire, &len); if (res!=4 || strlen(key)==0 ) { out_string(c, "CLIENT_ERROR bad command line format"); return; } expire = realtime(expire); it = item_alloc(key, flags, expire, len+2); if (it == 0) { out_string(c, "SERVER_ERROR out of memory"); /* swallow the data line */ c->write_and_go = conn_swallow; c->sbytes = len+2; return; } c->item_comm = comm; c->item = it; c->rcurr = ITEM_data(it); c->rlbytes = it->nbytes; c->state = conn_nread; return; } if ((strncmp(command, "incr ", 5) == 0 && (incr = 1)) || (strncmp(command, "decr ", 5) == 0)) { char temp[32]; unsigned int value; item *it; unsigned int delta; char key[251]; int res; char *ptr; time_t now = time(0); res = sscanf(command, "%*s %250s %u\n", key, &delta); if (res!=2 || strlen(key)==0 ) { out_string(c, "CLIENT_ERROR bad command line format"); return; } it = assoc_find(key); if (it && (it->it_flags & ITEM_DELETED)) { it = 0; } if (it && it->exptime && it->exptime < now) { item_unlink(it); it = 0; } if (!it) { out_string(c, "NOT_FOUND"); return; } ptr = ITEM_data(it); while (*ptr && (*ptr<'0' && *ptr>'9')) ptr++; value = atoi(ptr); if (incr) value+=delta; else { if (delta >= value) value = 0; else value-=delta; } sprintf(temp, "%u", value); res = strlen(temp); if (res + 2 > it->nbytes) { /* need to realloc */ item *new_it; new_it = item_alloc(ITEM_key(it), it->flags, it->exptime, res + 2 ); if (new_it == 0) { out_string(c, "SERVER_ERROR out of memory"); return; } memcpy(ITEM_data(new_it), temp, res); memcpy(ITEM_data(new_it) + res, "\r\n", 2); item_replace(it, new_it); } else { /* replace in-place */ memcpy(ITEM_data(it), temp, res); memset(ITEM_data(it) + res, ' ', it->nbytes-res-2); } out_string(c, temp); return; } if (strncmp(command, "get ", 4) == 0) { char *start = command + 4; char key[251]; int next; int i = 0; item *it; time_t now = time(0); while(sscanf(start, " %250s%n", key, &next) >= 1) { start+=next; stats.get_cmds++; it = assoc_find(key); if (it && (it->it_flags & ITEM_DELETED)) { it = 0; } if (settings.oldest_live && it && it->time <= settings.oldest_live) { item_unlink(it); it = 0; } if (it && it->exptime && it->exptime < now) { item_unlink(it); it = 0; } if (it) { if (i >= c->isize) { item **new_list = realloc(c->ilist, sizeof(item *)*c->isize*2); if (new_list) { c->isize *= 2; c->ilist = new_list; } else break; } stats.get_hits++; it->refcount++; item_update(it); *(c->ilist + i) = it; i++; } else stats.get_misses++; } c->icurr = c->ilist; c->ileft = i; if (c->ileft) { c->ipart = 0; c->state = conn_mwrite; c->ibytes = 0; return; } else { out_string(c, "END"); return; } } if (strncmp(command, "delete ", 7) == 0) { char key[251]; item *it; int res; time_t exptime = 0; res = sscanf(command, "%*s %250s %ld", key, &exptime); it = assoc_find(key); if (!it) { out_string(c, "NOT_FOUND"); return; } if (exptime == 0) { item_unlink(it); out_string(c, "DELETED"); return; } if (delcurr >= deltotal) { item **new_delete = realloc(todelete, sizeof(item *) * deltotal * 2); if (new_delete) { todelete = new_delete; deltotal *= 2; } else { /* * can't delete it immediately, user wants a delay, * but we ran out of memory for the delete queue */ out_string(c, "SERVER_ERROR out of memory"); return; } } exptime = realtime(exptime); it->refcount++; /* use its expiration time as its deletion time now */ it->exptime = exptime; it->it_flags |= ITEM_DELETED; todelete[delcurr++] = it; out_string(c, "DELETED"); return; } if (strncmp(command, "stats", 5) == 0) { process_stat(c, command); return; } if (strcmp(command, "flush_all") == 0) { settings.oldest_live = time(0); out_string(c, "OK"); return; } if (strcmp(command, "version") == 0) { out_string(c, "VERSION " VERSION); return; } if (strcmp(command, "quit") == 0) { c->state = conn_closing; return; } if (strncmp(command, "slabs reassign ", 15) == 0) { int src, dst; char *start = command+15; if (sscanf(start, "%u %u\r\n", &src, &dst) == 2) { int rv = slabs_reassign(src, dst); if (rv == 1) { out_string(c, "DONE"); return; } if (rv == 0) { out_string(c, "CANT"); return; } if (rv == -1) { out_string(c, "BUSY"); return; } } out_string(c, "CLIENT_ERROR bogus command"); return; } out_string(c, "ERROR"); return; }