int process_update_command ( char *key, size_t nkey, uint32_t flags, char *value, size_t nbytes, int32_t exptime_int, int comm ) { item *it; enum store_item_type ret; int32_t vlen = ( int32_t ) nbytes; if ( exptime_int < 0 ) exptime_int = REALTIME_MAXDELTA + 1; vlen += 2; if ( vlen < 0 || vlen - 2 < 0 ) { return false; } it = item_alloc (key, nkey, flags, realtime (exptime_int), value, vlen); if ( it == 0 ) { if ( comm == NREAD_SET ) { it = item_get (key, nkey); if ( it ) { item_unlink (it); item_remove (it); } } return false; } ret = store_item (it, comm); item_remove (it); if ( ret != STORED ) { return false; } return true; }
static inline struct item * _add_key(struct response *rsp, struct bstring *key) { struct element *reply = (struct element *)array_get(rsp->token, 0); struct item *it; item_rstatus_e istatus; it = item_get(key); if (it != NULL) { rsp->type = reply->type = ELEM_ERR; reply->bstr = str2bstr(RSP_EXIST); INCR(process_metrics, list_create_exist); return NULL; } else { /* TODO: figure out a TTL story here */ istatus = item_reserve(&it, key, NULL, ZIPLIST_HEADER_SIZE, 0, INT32_MAX); if (istatus != ITEM_OK) { rsp->type = reply->type = ELEM_ERR; reply->bstr = str2bstr(RSP_ERR_STORAGE); INCR(process_metrics, list_create_ex); INCR(process_metrics, process_ex); } else { INCR(process_metrics, list_create_stored); } return it; } }
const wchar_t *history_next_match( const wchar_t *needle) { if( current_mode ) { /* The index of previous search matches are saved in the 'used' list. We just need to pop the top item and set the new position. Easy! */ if( al_get_count( ¤t_mode->used ) ) { al_pop( ¤t_mode->used ); if( al_get_count( ¤t_mode->used ) ) { current_mode->pos = (int) al_peek_long( ¤t_mode->used ); item_t *i = item_get( current_mode, al_get( ¤t_mode->item, current_mode->pos ) ); return i->data; } } /* The used-list is empty. Set position to 'past end of list' and return the search string. */ current_mode->pos = al_get_count( ¤t_mode->item ); } return needle; }
static void req_process_set(struct context *ctx, struct conn *conn, struct msg *msg) { uint8_t *key, nkey, cid; struct item *it; key = msg->key_start; nkey = (uint8_t)(msg->key_end - msg->key_start); cid = item_slabcid(nkey, msg->vlen); if (cid == SLABCLASS_INVALID_ID) { rsp_send_error(ctx, conn, msg, MSG_RSP_CLIENT_ERROR, EINVAL); return; } itemx_removex(msg->hash, msg->md); it = item_get(key, nkey, cid, msg->vlen, time_reltime(msg->expiry), msg->flags, msg->md, msg->hash); if (it == NULL) { rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, ENOMEM); return; } mbuf_copy_to(&msg->mhdr, msg->value, item_data(it), msg->vlen); rsp_send_status(ctx, conn, msg, MSG_RSP_STORED); }
bool item_delete(const struct bstring *key) { struct item *it; it = item_get(key); if (it != NULL) { _item_delete(&it); return true; } else { return false; } }
int process_delete_command ( char *key, size_t nkey ) { item *it; it = item_get (key, nkey); if ( it ) { item_unlink (it); item_remove (it); return true; } else { //item_remove(it); return false; } }
static void asc_process_delete(struct conn *c, struct token *token, int ntoken) { char *key; /* key to be deleted */ size_t nkey; /* # key bytes */ struct item *it; /* item for this key */ 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; } 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; } /* * FIXME: This is not thread-safe, two threads could try to delete the same * item twice after succeeding in item_get, leading to erroneous stats */ it = item_get(key, nkey); if (it != NULL) { stats_slab_incr(it->id, delete_hit); item_delete(it); asc_write_deleted(c); } else { stats_thread_incr(delete_miss); asc_write_not_found(c); } }
/** Check if the specified string has already been used a s a search match */ static int history_is_used( const wchar_t *str ) { int i; int res =0; item_t *it = 0; for( i=0; i<al_get_count( ¤t_mode->used); i++ ) { long idx = al_get_long( ¤t_mode->used, i ); it = item_get( current_mode, al_get( ¤t_mode->item, (int)idx ) ); if( wcscmp( it->data, str ) == 0 ) { res = 1; break; } } return res; }
int process_get_command ( char *key, size_t nkey, char *dst, int *len ) { item *it; char *src = NULL; it = item_get (key, nkey); if ( it ) { item_update (it); src = ITEM_data (it); *len = it->nbytes - 2; memcpy (dst, src, it->nbytes); item_remove (it); return true; } else { //item_remove(it); return false; } }
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; } }
static void asc_process_delete(struct conn *c, struct token *token, int ntoken) { char *key; /* key to be deleted */ size_t nkey; /* # key bytes */ struct item *it; /* item for this key */ 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; } 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; } it = item_get(key, nkey); if (it != NULL) { stats_slab_incr(it->id, delete_hit); item_unlink(it); item_remove(it); asc_write_deleted(c); } else { stats_thread_incr(delete_miss); asc_write_not_found(c); } }
wchar_t *history_get( int idx ) { int len; if( !current_mode ) return 0; len = al_get_count( ¤t_mode->item ); if( (idx >= len ) && !current_mode->has_loaded ) { history_load( current_mode ); len = al_get_count( ¤t_mode->item ); } if( idx < 0 ) return 0; if( idx >= len ) return 0; return item_get( current_mode, al_get( ¤t_mode->item, len - 1 - idx ) )->data; }
static void req_process_num(struct context *ctx, struct conn *conn, struct msg *msg) { rstatus_t status; uint8_t *key, nkey, cid; struct item *it; struct itemx *itx; uint64_t cnum, nnum; char numstr[FC_UINT64_MAXLEN]; int n; key = msg->key_start; nkey = (uint8_t)(msg->key_end - msg->key_start); /* 1). look up existing itemx */ itx = itemx_getx(msg->hash, msg->md); if (itx == NULL) { /* 2a). miss -> return NOT_FOUND */ rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_FOUND); return; } /* 2b). hit -> read existing item into it */ it = slab_read_item(itx->sid, itx->offset); if (it == NULL) { rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, errno); return; } if (item_expired(it)) { rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_FOUND); return; } /* 3). sanity check item data to be a number */ status = fc_atou64(item_data(it), it->ndata, &cnum); if (status != FC_OK) { rsp_send_error(ctx, conn, msg, MSG_RSP_CLIENT_ERROR, EINVAL); return; } /* 4). remove existing itemx of it */ itemx_removex(msg->hash, msg->md); /* 5). compute the new incr/decr number nnum and numstr */ if (msg->type == MSG_REQ_INCR) { nnum = cnum + msg->num; } else { if (cnum < msg->num) { nnum = 0; } else { nnum = cnum - msg->num; } } n = fc_scnprintf(numstr, sizeof(numstr), "%"PRIu64"", nnum); /* 6). alloc new item that can hold n worth of bytes */ cid = item_slabcid(nkey, n); ASSERT(cid != SLABCLASS_INVALID_ID); it = item_get(key, nkey, cid, n, time_reltime(msg->expiry), msg->flags, msg->md, msg->hash); if (it == NULL) { rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, ENOMEM); return; } /* 7). copy numstr to it */ fc_memcpy(item_data(it), numstr, n); rsp_send_num(ctx, conn, msg, it); }
static void req_process_concat(struct context *ctx, struct conn *conn, struct msg *msg) { uint8_t *key, nkey, cid; struct item *oit, *it; uint32_t ndata; struct itemx *itx; key = msg->key_start; nkey = (uint8_t)(msg->key_end - msg->key_start); /* 1). look up existing itemx */ itx = itemx_getx(msg->hash, msg->md); if (itx == NULL) { /* 2a). miss -> return NOT_STORED */ rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_STORED); return; } /* 2b). hit -> read existing item into oit */ oit = slab_read_item(itx->sid, itx->offset); if (oit == NULL) { rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, errno); return; } if (item_expired(oit)) { rsp_send_status(ctx, conn, msg, MSG_RSP_NOT_STORED); return; } ndata = msg->vlen + oit->ndata; cid = item_slabcid(nkey, ndata); if (cid == SLABCLASS_INVALID_ID) { rsp_send_error(ctx, conn, msg, MSG_RSP_CLIENT_ERROR, EINVAL); return; } /* 3). remove existing itemx of oit */ itemx_removex(msg->hash, msg->md); /* 4). alloc new item that can hold ndata worth of bytes */ it = item_get(key, nkey, cid, ndata, time_reltime(msg->expiry), msg->flags, msg->md, msg->hash); if (it == NULL) { rsp_send_error(ctx, conn, msg, MSG_RSP_SERVER_ERROR, ENOMEM); return; } /* 5). copy data from msg to head or tail of new item it */ switch (msg->type) { case MSG_REQ_PREPEND: mbuf_copy_to(&msg->mhdr, msg->value, item_data(it), msg->vlen); fc_memcpy(item_data(it) + msg->vlen, item_data(oit), oit->ndata); break; case MSG_REQ_APPEND: fc_memcpy(item_data(it), item_data(oit), oit->ndata); mbuf_copy_to(&msg->mhdr, msg->value, item_data(it) + oit->ndata, msg->vlen); break; default: NOT_REACHED(); } rsp_send_status(ctx, conn, msg, MSG_RSP_STORED); }
// process a memcached get(s) command. (we don't support CAS). void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) { char *key; size_t nkey; int i = 0; item *it; token_t *key_token = &tokens[KEY_TOKEN]; char *suffix; assert(c != NULL); if (config.alloc && c->mem_blob == NULL) { long size = config.alloc_mean + gsl_ran_gaussian(c->thread->r, config.alloc_stddev); size = size <= 0 ? 10 : size; if (config.verbose > 0) { fprintf(stderr, "allocated blob: %ld\n", size); } c->mem_blob = malloc(sizeof(char) * size); c->mem_free_delay = 0; if (config.rtt_delay) { double r = config.rtt_mean + gsl_ran_gaussian(c->thread->r, config.rtt_stddev); if (r >= config.rtt_cutoff) { int wait = r / 100; if (config.verbose > 0) { fprintf(stderr, "delay: %d\n", wait); } c->mem_free_delay = wait; conn_set_state(c, conn_mwrite); } } } // process the whole command line, (only part of it may be tokenized right now) do { // process all tokenized keys at this stage. while(key_token->length != 0) { key = key_token->value; nkey = key_token->length; if(nkey > KEY_MAX_LENGTH) { error_response(c, "CLIENT_ERROR bad command line format"); return; } // lookup key-value. it = item_get(key, nkey); // hit. if (it) { if (i >= c->isize && !conn_expand_items(c)) { item_remove(it); break; } // Construct the response. Each hit adds three elements to the // outgoing data list: // "VALUE <key> <flags> <data_length>\r\n" // "<data>\r\n" // The <data> element is stored on the connection item list, not on // the iov list. if (!conn_add_iov(c, "VALUE ", 6) != 0 || !conn_add_iov(c, ITEM_key(it), it->nkey) != 0 || !conn_add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0) { item_remove(it); break; } if (config.verbose > 1) { fprintf(stderr, ">%d sending key %s\n", c->sfd, key); } // add item to remembered list (i.e., we've taken ownership of them // through refcounting and later must release them once we've // written out the iov associated with them). item_update(it); *(c->ilist + i) = it; i++; } key_token++; } /* * If the command string hasn't been fully processed, get the next set * of tokens. */ if(key_token->value != NULL) { ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS); key_token = tokens; } } while(key_token->value != NULL); c->icurr = c->ilist; c->ileft = i; if (config.verbose > 1) { fprintf(stderr, ">%d END\n", c->sfd); } // If the loop was terminated because of out-of-memory, it is not reliable // to add END\r\n to the buffer, because it might not end in \r\n. So we // send SERVER_ERROR instead. if (key_token->value != NULL || !conn_add_iov(c, "END\r\n", 5) != 0) { error_response(c, "SERVER_ERROR out of memory writing get response"); } else { conn_set_state(c, conn_mwrite); } }
/** Write the specified item to the specified file. */ static int item_write( FILE *f, history_mode_t *m, void *v ) { item_t *i = item_get( m, v ); return fwprintf( f, L"# %d\n%ls\n", i->timestamp, history_escape_newlines( i->data ) ); }
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); }
bool multiget_ascii_downstream(downstream *d, conn *uc, int (*emit_start)(conn *c, char *cmd, int cmd_len), int (*emit_skey)(conn *c, char *skey, int skey_len), int (*emit_end)(conn *c), mcache *front_cache) { assert(d != NULL); assert(d->downstream_conns != NULL); assert(d->multiget == NULL); assert(uc != NULL); assert(uc->noreply == false); proxy_td *ptd = d->ptd; assert(ptd != NULL); proxy_stats_cmd *psc_get = &ptd->stats.stats_cmd[STATS_CMD_TYPE_REGULAR][STATS_CMD_GET]; proxy_stats_cmd *psc_get_key = &ptd->stats.stats_cmd[STATS_CMD_TYPE_REGULAR][STATS_CMD_GET_KEY]; int nwrite = 0; int nconns = mcs_server_count(&d->mst); for (int i = 0; i < nconns; i++) { if (d->downstream_conns[i] != NULL && cproxy_prep_conn_for_write(d->downstream_conns[i]) == false) { d->ptd->stats.stats.err_downstream_write_prep++; cproxy_close_conn(d->downstream_conns[i]); return false; } } if (uc->next != NULL) { // More than one upstream conn, so we need a hashtable // to track keys for de-deplication. // d->multiget = genhash_init(128, skeyhash_ops); if (settings.verbose > 1) { fprintf(stderr, "cproxy multiget hash table new\n"); } } // Snapshot the volatile only once. // uint32_t msec_current_time_snapshot = msec_current_time; int uc_num = 0; conn *uc_cur = uc; while (uc_cur != NULL) { assert(uc_cur->cmd == -1); assert(uc_cur->item == NULL); assert(uc_cur->state == conn_pause); assert(IS_ASCII(uc_cur->protocol)); assert(IS_PROXY(uc_cur->protocol)); char *command = uc_cur->cmd_start; assert(command != NULL); char *space = strchr(command, ' '); assert(space > command); int cmd_len = space - command; assert(cmd_len == 3 || cmd_len == 4); // Either get or gets. int cas_emit = (command[3] == 's'); if (settings.verbose > 1) { fprintf(stderr, "forward multiget %s (%d %d)\n", command, cmd_len, uc_num); } while (space != NULL) { char *key = space + 1; char *next_space = strchr(key, ' '); int key_len; if (next_space != NULL) { key_len = next_space - key; } else { key_len = strlen(key); // We've reached the last key. // psc_get->read_bytes += (key - command + key_len); } // This key_len check helps skip consecutive spaces. // if (key_len > 0) { ptd->stats.stats.tot_multiget_keys++; psc_get_key->seen++; psc_get_key->read_bytes += key_len; // Update key-based statistics. // bool do_key_stats = matcher_check(&ptd->key_stats_matcher, key, key_len, true) == true && matcher_check(&ptd->key_stats_unmatcher, key, key_len, false) == false; if (do_key_stats) { touch_key_stats(ptd, key, key_len, msec_current_time_snapshot, STATS_CMD_TYPE_REGULAR, STATS_CMD_GET_KEY, 1, 0, 0, key_len, 0); } // Handle a front cache hit by queuing response. // // Note, front cache stats are part of mcache. // if (!cas_emit) { item *it = mcache_get(front_cache, key, key_len, msec_current_time_snapshot); if (it != NULL) { assert(it->nkey == key_len); assert(strncmp(ITEM_key(it), key, it->nkey) == 0); cproxy_upstream_ascii_item_response(it, uc_cur, 0); psc_get_key->hits++; psc_get_key->write_bytes += it->nbytes; if (do_key_stats) { touch_key_stats(ptd, key, key_len, msec_current_time_snapshot, STATS_CMD_TYPE_REGULAR, STATS_CMD_GET_KEY, 0, 1, 0, 0, it->nbytes); } // The refcount was inc'ed by mcache_get() for us. // item_remove(it); goto loop_next; } } bool self = false; conn *c = cproxy_find_downstream_conn(d, key, key_len, &self); if (c != NULL) { if (self) { // Optimization for talking with ourselves, // to avoid extra network hop. // ptd->stats.stats.tot_optimize_self++; item *it = item_get(key, key_len); if (it != NULL) { cproxy_upstream_ascii_item_response(it, uc_cur, cas_emit); psc_get_key->hits++; psc_get_key->write_bytes += it->nbytes; if (do_key_stats) { touch_key_stats(ptd, key, key_len, msec_current_time_snapshot, STATS_CMD_TYPE_REGULAR, STATS_CMD_GET_KEY, 0, 1, 0, 0, it->nbytes); } // The refcount was inc'ed by item_get() for us. // item_remove(it); if (settings.verbose > 1) { fprintf(stderr, "optimize self multiget hit: %s\n", key); } } else { psc_get_key->misses++; if (do_key_stats) { touch_key_stats(ptd, key, key_len, msec_current_time_snapshot, STATS_CMD_TYPE_REGULAR, STATS_CMD_GET_KEY, 0, 0, 1, 0, 0); } if (settings.verbose > 1) { fprintf(stderr, "optimize self multiget miss: %s\n", key); } } goto loop_next; } // See if we've already requested this key via // the multiget hash table, in order to // de-deplicate repeated keys. // bool first_request = true; if (d->multiget != NULL) { // TODO: Use Trond's allocator here. // multiget_entry *entry = calloc(1, sizeof(multiget_entry)); if (entry != NULL) { entry->upstream_conn = uc_cur; entry->opaque = 0; entry->hits = 0; entry->next = genhash_find(d->multiget, key); genhash_update(d->multiget, key, entry); if (entry->next != NULL) { first_request = false; } } else { // TODO: Handle out of multiget entry memory. } } if (first_request) { assert(c->item == NULL); assert(c->state == conn_pause); assert(IS_PROXY(c->protocol)); assert(c->ilist != NULL); assert(c->isize > 0); if (c->msgused <= 1 && c->msgbytes <= 0) { emit_start(c, command, cmd_len); } // Provide the preceding space as optimization // for ascii-to-ascii configuration. // emit_skey(c, key - 1, key_len + 1); } else { ptd->stats.stats.tot_multiget_keys_dedupe++; if (settings.verbose > 1) { char buf[KEY_MAX_LENGTH + 10]; memcpy(buf, key, key_len); buf[key_len] = '\0'; fprintf(stderr, "%d cproxy multiget dedpue: %s\n", uc_cur->sfd, buf); } } } else { // TODO: Handle when downstream conn is down. } } loop_next: space = next_space; } uc_num++; uc_cur = uc_cur->next; } for (int i = 0; i < nconns; i++) { conn *c = d->downstream_conns[i]; if (c != NULL && (c->msgused > 1 || c->msgbytes > 0)) { emit_end(c); conn_set_state(c, conn_mwrite); c->write_and_go = conn_new_cmd; if (update_event(c, EV_WRITE | EV_PERSIST)) { nwrite++; if (uc->noreply) { c->write_and_go = conn_pause; } } else { if (settings.verbose > 1) { fprintf(stderr, "Couldn't update cproxy write event\n"); } d->ptd->stats.stats.err_oom++; cproxy_close_conn(c); } } } if (settings.verbose > 1) { fprintf(stderr, "forward multiget nwrite %d out of %d\n", nwrite, nconns); } d->downstream_used_start = nwrite; d->downstream_used = nwrite; if (cproxy_dettach_if_noreply(d, uc) == false) { d->upstream_suffix = "END\r\n"; cproxy_start_downstream_timeout(d, NULL); } return nwrite > 0; }
/** Go through the mmaped region and insert pointers to suitable loacations into the item list */ static void history_populate_from_mmap( history_mode_t *m ) { char *begin = m->mmap_start; char *end = begin + m->mmap_length; char *pos; array_list_t old_item; array_list_t session_item_list; int ignore_newline = 0; int do_push = 1; al_init( &old_item ); al_init( &session_item_list ); al_push_all( &old_item, &m->item ); al_truncate( &m->item, 0 ); for( pos = begin; pos <end; pos++ ) { if( do_push ) { item_t *i; item_t *i_orig; ignore_newline = *pos == '#'; i = item_get( m, pos ); if( (i_orig=hash_get( ¤t_mode->session_item, i ) ) ) { /* This item comes from this session. Insert the original item at the end of the item list. */ al_push( &session_item_list, i_orig ); } else { /* Old item. Insert pointer directly to the item list */ al_push( &m->item, pos ); } do_push = 0; } switch( *pos ) { case '\\': { pos++; break; } case '\n': { if( ignore_newline ) { ignore_newline = 0; } else { do_push = 1; } break; } } } al_push_all( &m->item, &session_item_list ); m->pos += al_get_count( &m->item ); al_push_all( &m->item, &old_item ); al_destroy( &session_item_list ); al_destroy( &old_item ); }
const wchar_t *history_prev_match( const wchar_t *needle ) { if( current_mode ) { if( current_mode->pos > 0 ) { for( current_mode->pos--; current_mode->pos>=0; current_mode->pos-- ) { item_t *i = item_get( current_mode, al_get( ¤t_mode->item, current_mode->pos ) ); wchar_t *haystack = (wchar_t *)i->data; if( history_test( needle, haystack ) ) { int is_used; /* This is ugly. Whenever we call item_get(), there is a chance that the return value of any previous call to item_get will become invalid. The history_is_used function uses the item_get() function. Therefore, we must create a copy of the haystack string, and if the string is unused, we must call item_get anew. */ haystack = wcsdup(haystack ); is_used = history_is_used( haystack ); free( haystack ); if( !is_used ) { i = item_get( current_mode, al_get( ¤t_mode->item, current_mode->pos ) ); al_push_long( ¤t_mode->used, current_mode->pos ); return i->data; } } } } if( !current_mode->has_loaded ) { /* We found no match in the list, try loading the history file and continue the search */ history_load( current_mode ); return history_prev_match( needle ); } else { /* We found no match in the list, and the file is already loaded. Set poition before first element and return original search string. */ current_mode->pos=-1; if( al_peek_long( ¤t_mode->used ) != -1 ) al_push_long( ¤t_mode->used, -1 ); } } return needle; }
static inline void asc_process_read(struct conn *c, struct token *token, int ntoken) { rstatus_t status; char *key; size_t nkey; unsigned valid_key_iter = 0; struct item *it; struct token *key_token; bool return_cas; 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; } return_cas = (c->req_type == REQ_GETS) ? true : false; key_token = &token[TOKEN_KEY]; do { while (key_token->len != 0) { key = key_token->val; nkey = key_token->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 (return_cas) { stats_thread_incr(gets); } else { stats_thread_incr(get); } it = item_get(key, nkey); if (it != NULL) { /* item found */ if (return_cas) { stats_slab_incr(it->id, gets_hit); } else { stats_slab_incr(it->id, get_hit); } if (valid_key_iter >= c->isize) { struct item **new_list; new_list = mc_realloc(c->ilist, sizeof(struct item *) * c->isize * 2); if (new_list != NULL) { c->isize *= 2; c->ilist = new_list; } else { item_remove(it); break; } } status = asc_respond_get(c, valid_key_iter, it, return_cas); if (status != MC_OK) { log_debug(LOG_NOTICE, "client error on c %d for req of type " "%d with %d tokens", c->sd, c->req_type, ntoken); stats_thread_incr(cmd_error); item_remove(it); break; } log_debug(LOG_VVERB, ">%d sending key %.*s", c->sd, it->nkey, item_key(it)); item_touch(it); *(c->ilist + valid_key_iter) = it; valid_key_iter++; } else { /* item not found */ if (return_cas) { stats_thread_incr(gets_miss); } else { stats_thread_incr(get_miss); } klog_write(c->peer, c->req_type, key, nkey, 1, 0); } key_token++; } /* * If the command string hasn't been fully processed, get the next set * of token. */ if (key_token->val != NULL) { ntoken = asc_tokenize(key_token->val, token, TOKEN_MAX); /* ntoken is unused */ key_token = token; } } while (key_token->val != NULL); c->icurr = c->ilist; c->ileft = valid_key_iter; if (return_cas) { c->scurr = c->slist; c->sleft = valid_key_iter; } log_debug(LOG_VVERB, ">%d END", c->sd); /* * If the loop was terminated because of out-of-memory, it is not * reliable to add END\r\n to the buffer, because it might not end * in \r\n. So we send SERVER_ERROR instead. */ if (key_token->val != NULL || conn_add_iov(c, "END\r\n", 5) != MC_OK || (c->udp && conn_build_udp_headers(c) != MC_OK)) { log_warn("server error on c %d for req of type %d with enomem", c->sd, c->req_type); asc_write_server_error(c); } else { conn_set_state(c, CONN_MWRITE); c->msg_curr = 0; } }
int main(int argc, char **argv) { config_init(); int c; while (-1 != (c = getopt(argc, argv, "n:m:k:d:p:h"))) { switch(c) { case 'n': config.num = atoi(optarg); break; case 'm': config.maxbytes = ((size_t)atoi(optarg)) * 1024 * 1024; break; case 'k': config.keysize = atoi(optarg); break; case 'd': config.datasize = atoi(optarg); break; case 'p': config.hashpower = atoi(optarg); break; case 'h': usage(); return EXIT_SUCCESS; default: usage(); return EXIT_FAILURE; } } generate_key_init(); print_env(); lru *l = lru_init(config.maxbytes, config.hashpower); char *bvalue = malloc(config.datasize); memset(bvalue, 'x', config.datasize); char *key = malloc(config.keysize); memset(key, 0, config.keysize); int gnum = config.keysize - num_; generate_key_reset(); bench_start("SET"); int i; for (i = 0; i < config.num; i++) { snprintf(key, config.keysize, fmt_, gnum, generate_key(gnum), i); int r = item_set(l, key, config.keysize, bvalue, config.datasize); assert(r == 0); process_report(); } bench_stop(); print_stat(l); char *buf = malloc(config.datasize); size_t sz; generate_key_reset(); bench_start("GET"); for (i = 0; i < config.num; i++) { snprintf(key, config.keysize, fmt_, gnum, generate_key(gnum), i); int r = item_get(l, key, config.keysize, buf, config.datasize, &sz); if (!r) { assert((int)sz == config.datasize); assert(memcmp(bvalue, buf, config.datasize) == 0); } memset(buf, 0, config.datasize); process_report(); } bench_stop(); print_stat(l); generate_key_reset(); bench_start("DELETE"); for (i = 0; i < config.num; i++) { snprintf(key, config.keysize, fmt_, gnum, generate_key(gnum), i); item_delete(l, key, config.keysize); process_report(); } bench_stop(); print_stat(l); free(buf); free(bvalue); free(key); free(fmt_); free(key_); lru_free(l); /* printf("print any key to exit...\n"); getchar(); */ return EXIT_SUCCESS; }
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); }
/** Save the specified mode to file */ static void history_save_mode( void *n, history_mode_t *m ) { FILE *out; history_mode_t *on_disk; int i; int has_new=0; wchar_t *tmp_name; int ok = 1; /* First check if there are any new entries to save. If not, then we can just return */ for( i=0; i<al_get_count(&m->item); i++ ) { void *ptr = al_get( &m->item, i ); has_new = item_is_new( m, ptr ); if( has_new ) { break; } } if( !has_new ) { return; } signal_block(); /* Set up on_disk variable to describe the current contents of the history file */ on_disk = history_create_mode( m->name ); history_load( on_disk ); tmp_name = history_filename( on_disk, m->name, L".tmp" ); if( tmp_name ) { tmp_name = wcsdup(tmp_name ); if( (out=wfopen( tmp_name, "w" ) ) ) { hash_table_t mine; hash_init( &mine, &hash_item_func, &hash_item_cmp ); for( i=0; i<al_get_count(&m->item); i++ ) { void *ptr = al_get( &m->item, i ); int is_new = item_is_new( m, ptr ); if( is_new ) { hash_put( &mine, item_get( m, ptr ), L"" ); } } /* Re-save the old history */ for( i=0; ok && (i<al_get_count(&on_disk->item)); i++ ) { void *ptr = al_get( &on_disk->item, i ); item_t *i = item_get( on_disk, ptr ); if( !hash_get( &mine, i ) ) { if( item_write( out, on_disk, ptr ) == -1 ) { ok = 0; break; } } } hash_destroy( &mine ); /* Add our own items last */ for( i=0; ok && (i<al_get_count(&m->item)); i++ ) { void *ptr = al_get( &m->item, i ); int is_new = item_is_new( m, ptr ); if( is_new ) { if( item_write( out, m, ptr ) == -1 ) { ok = 0; } } } if( fclose( out ) || !ok ) { /* This message does not have high enough priority to be shown by default. */ debug( 2, L"Error when writing history file" ); } else { wrename( tmp_name, history_filename( on_disk, m->name, 0 ) ); } } free( tmp_name ); } halloc_free( on_disk); if( ok ) { /* Reset the history. The item_t entries created in this session are not lost or dropped, they are stored in the session_item hash table. On reload, they will be automatically inserted at the end of the history list. */ if( m->mmap_start && (m->mmap_start != MAP_FAILED ) ) { munmap( m->mmap_start, m->mmap_length ); } al_truncate( &m->item, 0 ); al_truncate( &m->used, 0 ); m->pos = 0; m->has_loaded = 0; m->mmap_start=0; m->mmap_length=0; m->save_timestamp=time(0); m->new_count = 0; } signal_unblock(); }