struct Stack* parseCmdLine(const char* const clStr) { struct CmdLine* cmdLine; struct Queue* tokens; char *token, *nextToken, *command; char **operators, **files; bool background, pushed, pipedIn, pipeOut; struct Stack* commandStack; int i; struct LinkedList* parameters; tokens=tokenize(clStr); commandStack=stackCreate(); pipedIn=false; pipeOut=false; while (!queueIsEmpty(tokens)) { //Get Command pushed=false; command=(char *)queueGetFront(tokens); //Parse Parameters parameters=listCreate(); if (!queueIsEmpty(tokens)) { while (!queueIsEmpty(tokens)) { token=(char *)queuePeek(tokens); if (!isDelimiter(token)) { listAppend(parameters, token); queueGetFront(tokens); } else { break; } } } //Check for Redirects operators=(char **)malloc(sizeof(char*)*REDIRECTS); files=(char **)malloc(sizeof(char*)*REDIRECTS); for (i=0; i<REDIRECTS; i++) { operators[i]=NULL; files[i]=NULL; } if (!queueIsEmpty(tokens)) { for (i=0; i<REDIRECTS && !queueIsEmpty(tokens); i++) { token=(char *)queuePeek(tokens); if (strcmp(token, CL_TOKEN_GREATER_THAN)==0 || strcmp(token, CL_TOKEN_LESS_THAN)==0) { operators[i]=token; queueGetFront(tokens); token=(char *)queueGetFront(tokens); files[i]=token; } else { break; } } } //Check for background process or pipe background=false; pipeOut=false; if (!queueIsEmpty(tokens)) { token=(char *)queuePeek(tokens); if (strcmp(token, CL_TOKEN_AMP)==0) { queueGetFront(tokens); background=true; } else if (strcmp(token, CL_TOKEN_PIPE)==0) { queueGetFront(tokens); pipeOut=true; } } //Add Command to Stack of Commands pushCmdLine(commandStack, command, parameters, operators, files, pipedIn, pipeOut, background); pushed=true; pipedIn=pipeOut; if (background || !pipeOut) { break; //Terminated because in background or syntax error } } if (!pushed) { free(operators); free(files); } queueDestroy(tokens, false); return commandStack; }
/* The SORT command is the most complex command in Redis. Warning: this code * is optimized for speed and a bit less for readability */ void sortCommand(redisClient *c) { list *operations; unsigned int outputlen = 0; int desc = 0, alpha = 0; long limit_start = 0, limit_count = -1, start, end; int j, dontsort = 0, vectorlen; int getop = 0; /* GET operation counter */ int int_convertion_error = 0; int syntax_error = 0; robj *sortval, *sortby = NULL, *storekey = NULL; redisSortObject *vector; /* Resulting vector to sort */ /* Lookup the key to sort. It must be of the right types */ sortval = lookupKeyRead(c->db,c->argv[1]); if (sortval && sortval->type != REDIS_SET && sortval->type != REDIS_LIST && sortval->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); return; } /* Create a list of operations to perform for every sorted element. * Operations can be GET */ operations = listCreate(); listSetFreeMethod(operations,zfree); j = 2; /* options start at argv[2] */ /* Now we need to protect sortval incrementing its count, in the future * SORT may have options able to overwrite/delete keys during the sorting * and the sorted key itself may get destroyed */ if (sortval) incrRefCount(sortval); else sortval = createQuicklistObject(); /* The SORT command has an SQL-alike syntax, parse it */ while(j < c->argc) { int leftargs = c->argc-j-1; if (!strcasecmp(c->argv[j]->ptr,"asc")) { desc = 0; } else if (!strcasecmp(c->argv[j]->ptr,"desc")) { desc = 1; } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) { alpha = 1; } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) { if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL) != REDIS_OK) || (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL) != REDIS_OK)) { syntax_error++; break; } j+=2; } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) { storekey = c->argv[j+1]; j++; } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) { sortby = c->argv[j+1]; /* If the BY pattern does not contain '*', i.e. it is constant, * we don't need to sort nor to lookup the weight keys. */ if (strchr(c->argv[j+1]->ptr,'*') == NULL) { dontsort = 1; } else { /* If BY is specified with a real patter, we can't accept * it in cluster mode. */ if (server.cluster_enabled) { addReplyError(c,"BY option of SORT denied in Cluster mode."); syntax_error++; break; } } j++; } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) { if (server.cluster_enabled) { addReplyError(c,"GET option of SORT denied in Cluster mode."); syntax_error++; break; } listAddNodeTail(operations,createSortOperation( REDIS_SORT_GET,c->argv[j+1])); getop++; j++; } else { addReply(c,shared.syntaxerr); syntax_error++; break; } j++; } /* Handle syntax errors set during options parsing. */ if (syntax_error) { decrRefCount(sortval); listRelease(operations); return; } /* When sorting a set with no sort specified, we must sort the output * so the result is consistent across scripting and replication. * * The other types (list, sorted set) will retain their native order * even if no sort order is requested, so they remain stable across * scripting and replication. */ if (dontsort && sortval->type == REDIS_SET && (storekey || c->flags & REDIS_LUA_CLIENT)) { /* Force ALPHA sorting */ dontsort = 0; alpha = 1; sortby = NULL; } /* Destructively convert encoded sorted sets for SORT. */ if (sortval->type == REDIS_ZSET) zsetConvert(sortval, REDIS_ENCODING_SKIPLIST); /* Objtain the length of the object to sort. */ switch(sortval->type) { case REDIS_LIST: vectorlen = listTypeLength(sortval); break; case REDIS_SET: vectorlen = setTypeSize(sortval); break; case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break; default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */ } /* Perform LIMIT start,count sanity checking. */ start = (limit_start < 0) ? 0 : limit_start; end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1; if (start >= vectorlen) { start = vectorlen-1; end = vectorlen-2; } if (end >= vectorlen) end = vectorlen-1; /* Whenever possible, we load elements into the output array in a more * direct way. This is possible if: * * 1) The object to sort is a sorted set or a list (internally sorted). * 2) There is nothing to sort as dontsort is true (BY <constant string>). * * In this special case, if we have a LIMIT option that actually reduces * the number of elements to fetch, we also optimize to just load the * range we are interested in and allocating a vector that is big enough * for the selected range length. */ if ((sortval->type == REDIS_ZSET || sortval->type == REDIS_LIST) && dontsort && (start != 0 || end != vectorlen-1)) { vectorlen = end-start+1; } /* Load the sorting vector with all the objects to sort */ vector = zmalloc(sizeof(redisSortObject)*vectorlen); j = 0; if (sortval->type == REDIS_LIST && dontsort) { /* Special handling for a list, if 'dontsort' is true. * This makes sure we return elements in the list original * ordering, accordingly to DESC / ASC options. * * Note that in this case we also handle LIMIT here in a direct * way, just getting the required range, as an optimization. */ if (end >= start) { listTypeIterator *li; listTypeEntry entry; li = listTypeInitIterator(sortval, desc ? (long)(listTypeLength(sortval) - start - 1) : start, desc ? REDIS_HEAD : REDIS_TAIL); while(j < vectorlen && listTypeNext(li,&entry)) { vector[j].obj = listTypeGet(&entry); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } listTypeReleaseIterator(li); /* Fix start/end: output code is not aware of this optimization. */ end -= start; start = 0; } } else if (sortval->type == REDIS_LIST) { listTypeIterator *li = listTypeInitIterator(sortval,0,REDIS_TAIL); listTypeEntry entry; while(listTypeNext(li,&entry)) { vector[j].obj = listTypeGet(&entry); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } listTypeReleaseIterator(li); } else if (sortval->type == REDIS_SET) { setTypeIterator *si = setTypeInitIterator(sortval); robj *ele; while((ele = setTypeNextObject(si)) != NULL) { vector[j].obj = ele; vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } setTypeReleaseIterator(si); } else if (sortval->type == REDIS_ZSET && dontsort) { /* Special handling for a sorted set, if 'dontsort' is true. * This makes sure we return elements in the sorted set original * ordering, accordingly to DESC / ASC options. * * Note that in this case we also handle LIMIT here in a direct * way, just getting the required range, as an optimization. */ zset *zs = sortval->ptr; zskiplist *zsl = zs->zsl; zskiplistNode *ln; robj *ele; int rangelen = vectorlen; /* Check if starting point is trivial, before doing log(N) lookup. */ if (desc) { long zsetlen = dictSize(((zset*)sortval->ptr)->dict); ln = zsl->tail; if (start > 0) ln = zslGetElementByRank(zsl,zsetlen-start); } else { ln = zsl->header->level[0].forward; if (start > 0) ln = zslGetElementByRank(zsl,start+1); } while(rangelen--) { redisAssertWithInfo(c,sortval,ln != NULL); ele = ln->obj; vector[j].obj = ele; vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; ln = desc ? ln->backward : ln->level[0].forward; } /* Fix start/end: output code is not aware of this optimization. */ end -= start; start = 0; } else if (sortval->type == REDIS_ZSET) { dict *set = ((zset*)sortval->ptr)->dict; dictIterator *di; dictEntry *setele; di = dictGetIterator(set); while((setele = dictNext(di)) != NULL) { vector[j].obj = dictGetKey(setele); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } dictReleaseIterator(di); } else { redisPanic("Unknown type"); } redisAssertWithInfo(c,sortval,j == vectorlen); /* Now it's time to load the right scores in the sorting vector */ if (dontsort == 0) { for (j = 0; j < vectorlen; j++) { robj *byval; if (sortby) { /* lookup value to sort by */ byval = lookupKeyByPattern(c->db,sortby,vector[j].obj); if (!byval) continue; } else { /* use object itself to sort by */ byval = vector[j].obj; } if (alpha) { if (sortby) vector[j].u.cmpobj = getDecodedObject(byval); } else { if (sdsEncodedObject(byval)) { char *eptr; vector[j].u.score = strtod(byval->ptr,&eptr); if (eptr[0] != '\0' || errno == ERANGE || isnan(vector[j].u.score)) { int_convertion_error = 1; } } else if (byval->encoding == REDIS_ENCODING_INT) { /* Don't need to decode the object if it's * integer-encoded (the only encoding supported) so * far. We can just cast it */ vector[j].u.score = (long)byval->ptr; } else { redisAssertWithInfo(c,sortval,1 != 1); } } /* when the object was retrieved using lookupKeyByPattern, * its refcount needs to be decreased. */ if (sortby) { decrRefCount(byval); } } } if (dontsort == 0) { server.sort_desc = desc; server.sort_alpha = alpha; server.sort_bypattern = sortby ? 1 : 0; server.sort_store = storekey ? 1 : 0; if (sortby && (start != 0 || end != vectorlen-1)) pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end); else qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare); } /* Send command output to the output buffer, performing the specified * GET/DEL/INCR/DECR operations if any. */ outputlen = getop ? getop*(end-start+1) : end-start+1; if (int_convertion_error) { addReplyError(c,"One or more scores can't be converted into double"); } else if (storekey == NULL) { /* STORE option not specified, sent the sorting result to client */ addReplyMultiBulkLen(c,outputlen); for (j = start; j <= end; j++) { listNode *ln; listIter li; if (!getop) addReplyBulk(c,vector[j].obj); listRewind(operations,&li); while((ln = listNext(&li))) { redisSortOperation *sop = ln->value; robj *val = lookupKeyByPattern(c->db,sop->pattern, vector[j].obj); if (sop->type == REDIS_SORT_GET) { if (!val) { addReply(c,shared.nullbulk); } else { addReplyBulk(c,val); decrRefCount(val); } } else { /* Always fails */ redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET); } } } } else { robj *sobj = createQuicklistObject(); /* STORE option specified, set the sorting result as a List object */ for (j = start; j <= end; j++) { listNode *ln; listIter li; if (!getop) { listTypePush(sobj,vector[j].obj,REDIS_TAIL); } else { listRewind(operations,&li); while((ln = listNext(&li))) { redisSortOperation *sop = ln->value; robj *val = lookupKeyByPattern(c->db,sop->pattern, vector[j].obj); if (sop->type == REDIS_SORT_GET) { if (!val) val = createStringObject("",0); /* listTypePush does an incrRefCount, so we should take care * care of the incremented refcount caused by either * lookupKeyByPattern or createStringObject("",0) */ listTypePush(sobj,val,REDIS_TAIL); decrRefCount(val); } else { /* Always fails */ redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET); } } } } if (outputlen) { setKey(c->db,storekey,sobj); notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"sortstore",storekey, c->db->id); server.dirty += outputlen; } else if (dbDelete(c->db,storekey)) { signalModifiedKey(c->db,storekey); notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",storekey,c->db->id); server.dirty++; } decrRefCount(sobj); addReplyLongLong(c,outputlen); } /* Cleanup */ if (sortval->type == REDIS_LIST || sortval->type == REDIS_SET) for (j = 0; j < vectorlen; j++) decrRefCount(vector[j].obj); decrRefCount(sortval); listRelease(operations); for (j = 0; j < vectorlen; j++) { if (alpha && vector[j].u.cmpobj) decrRefCount(vector[j].u.cmpobj); } zfree(vector); }
vnode_t *vnode_new(const char *root_dir, uint32_t id, enum eVnodeStorageType storage_type, vnode_write_queue_handle_write_cb vnode_write_queue_handle_write) { vnode_t *vnode = (vnode_t*)zmalloc(sizeof(vnode_t)); memset(vnode, 0, sizeof(vnode_t)); vnode->id = id; vnode->storage_type = storage_type; /*vnode->max_dbsize = 1024L * 1024L * 1024L * 4L;*/ vnode->max_dbsize = 1024L * 1024L * 500L; /* Create vnode root dir */ sprintf(vnode->root_dir, "%s/%04d", root_dir, id); if ( mkdir_if_not_exist(vnode->root_dir) != 0 ){ error_log("Cann't create vnode(%d) dir:%s", id, vnode->root_dir); zfree(vnode); return NULL; } /* datazones */ int i; for ( i = 0 ; i < MAX_DATAZONES ; i++ ){ vnode->datazones[i] = (datazone_t*)zmalloc(sizeof(datazone_t)); if ( datazone_init(vnode->datazones[i], vnode, i) != 0 ){ zfree(vnode); return NULL; } } /* Slices DB */ if ( vnode->storage_type >= STORAGE_KVDB ){ // Create Metadata DB. const char *metadata_dbname = "metadata"; kvdb_t *kvdb_metadata = vnode_open_kvdb(vnode, metadata_dbname); if ( kvdb_metadata == NULL ){ error_log("MetadataDB create failed. dbname:%s", metadata_dbname); zfree(vnode); return NULL; } vnode->kvdb_metadata = kvdb_metadata; uint32_t active_slicedb_id = 0; if ( kvdb_get_uint32(kvdb_metadata, "active_slicedb_id", &active_slicedb_id) != 0 ){ active_slicedb_id = 0; } notice_log("vnode active_slicedb_id:%d", active_slicedb_id); // Create Slice DB. for ( int db_id = 0 ; db_id <= active_slicedb_id ; db_id++ ){ slicedb_t *slicedb = vnode_open_slicedb(vnode, db_id); if ( slicedb != NULL ){ } else { /*char dbname[NAME_MAX];*/ /*sprintf(dbname, "slice-%03d", db_id);*/ /*kvdb_t *kvdb = vnode_open_kvdb(vnode, dbname);*/ /*if ( kvdb != NULL ){*/ /*vnode->slicedbs[db_id] = slicedb_new(db_id, kvdb, vnode->max_dbsize);*/ /*} else {*/ /*error_log("SliceDB create failed. dbname:%s", dbname);*/ for ( int n = 0 ; n < db_id ; n++ ){ slicedb_free(vnode->slicedbs[n]); vnode->slicedbs[n] = NULL; } zfree(vnode); return NULL; } } vnode->active_slicedb = vnode->slicedbs[active_slicedb_id]; /*vnode->kvdb = vnode->active_slicedb->kvdb;*/ } vnode->caching_objects = object_queue_new(object_compare_md5_func); vnode->received_objects = listCreate(); vnode->received_object_size = 0; vnode->standby_objects = listCreate(); vnode->standby_object_size = 0; vnode->write_queue = init_work_queue(vnode_write_queue_handle_write, VNODE_WRITE_QUEUE_INTERVAL); return vnode; }
int main(int argc, char **argv) { int i, maxnconn; pthread_t tid; struct file *fptr; int current; listNode *listNode; for (i = 0; i < MAXFILES; i++) { file[i].f_flags = 0; } context = connectRedis(localhost,port); queue = listCreate(); home_page(HOST, "/"); nlefttoread = nlefttoconn = queue->len; nconn = 0; while (nlefttoconn > 0 || nconn > 0) { printf("lefttoconn: %d, conn:%d\n",nlefttoconn,nconn); current = 0; while (nconn < MAXFILES && nlefttoconn > 0) { for (i = current ; i < MAXFILES; i++) if (file[i].f_flags == 0) { current = i; break; } pthread_mutex_lock(&queue_mutex); listNode = queue->head; if (listNode == NULL) { printf("have some but no!\n"); exit(1); } file[i].f_flags = F_CONNECTING; setFileNameAndHost(listNode->value,file[i].f_name,file[i].f_host); listDelNodeHead(queue); pthread_create(&tid, NULL, &do_get_read, &file[i]); file[i].f_tid = tid; nconn++; nlefttoconn = queue->len; pthread_mutex_unlock(&queue_mutex); } pthread_mutex_lock(&ndone_mutex); while (ndone == 0) pthread_cond_wait(&ndone_cond, &ndone_mutex); for (i = 0; i < MAXFILES; i++) { if (file[i].f_flags & F_DONE) { pthread_join(file[i].f_tid, (void **) &fptr); if (&file[i] != fptr) { printf("file[i]!=ptr\n"); exit(1); } fptr->f_flags = 0; /* clears F_DONE */ ndone--; nconn--; printf("thread %d for name:%s host:%s done\n", fptr->f_tid, fptr->f_name,fptr->f_host); } } pthread_mutex_unlock(&ndone_mutex); } listRelease(queue); disconnectRedis(context); exit(0); }
/* Watch for the specified key * * 让客户端 c 监视给定的键 key */ void watchForKey(redisClient *c, robj *key) { list *clients = NULL; listIter li; listNode *ln; watchedKey *wk; /* Check if we are already watching for this key */ // 检查 key 是否已经保存在 watched_keys 链表中, // 如果是的话,直接返回 listRewind(c->watched_keys,&li); while((ln = listNext(&li))) { wk = listNodeValue(ln); if (wk->db == c->db && equalStringObjects(key,wk->key)) return; /* Key already watched */ } // 键不存在于 watched_keys ,添加它 // 以下是一个 key 不存在于字典的例子: // before : // { // 'key-1' : [c1, c2, c3], // 'key-2' : [c1, c2], // } // after c-10086 WATCH key-1 and key-3: // { // 'key-1' : [c1, c2, c3, c-10086], // 'key-2' : [c1, c2], // 'key-3' : [c-10086] // } /* This key is not already watched in this DB. Let's add it */ // 检查 key 是否存在于数据库的 watched_keys 字典中 clients = dictFetchValue(c->db->watched_keys,key); // 如果不存在的话,添加它 if (!clients) { // 值为链表 clients = listCreate(); // 关联键值对到字典 dictAdd(c->db->watched_keys,key,clients); incrRefCount(key); } // 将客户端添加到链表的末尾 listAddNodeTail(clients,c); /* Add the new key to the list of keys watched by this client */ // 将新 watchedKey 结构添加到客户端 watched_keys 链表的表尾 // 以下是一个添加 watchedKey 结构的例子 // before: // [ // { // 'key': 'key-1', // 'db' : 0 // } // ] // after client watch key-123321 in db 0: // [ // { // 'key': 'key-1', // 'db' : 0 // } // , // { // 'key': 'key-123321', // 'db': 0 // } // ] wk = zmalloc(sizeof(*wk)); wk->key = key; wk->db = c->db; incrRefCount(key); listAddNodeTail(c->watched_keys,wk); }
void vmInit(void) { off totsize; int pipefds[2]; size_t stacksize; #ifndef _WIN32 struct flock fl; #endif if (server.vm_max_threads != 0) zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */ redisLog(REDIS_NOTICE,"Using '%s' as swap file",server.vm_swap_file); /* Try to open the old swap file, otherwise create it */ if ((server.vm_fp = fopen(server.vm_swap_file,"r+b")) == NULL) { server.vm_fp = fopen(server.vm_swap_file,"w+b"); } if (server.vm_fp == NULL) { redisLog(REDIS_WARNING, "Can't open the swap file: %s. Exiting.", strerror(errno)); exit(1); } server.vm_fd = fileno(server.vm_fp); /* Lock the swap file for writing, this is useful in order to avoid * another instance to use the same swap file for a config error. */ #ifdef _WIN32 // LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0) if(!LockFile((HANDLE) _get_osfhandle(server.vm_fd), (0x40000001), 0, 1, 0) ) { redisLog(REDIS_WARNING, "Can't lock the swap file at '%s': %s. Make sure it is not used by another Redis instance.", server.vm_swap_file, strerror(errno)); WSACleanup(); exit(1); } #else fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0; if (fcntl(server.vm_fd,F_SETLK,&fl) == -1) { redisLog(REDIS_WARNING, "Can't lock the swap file at '%s': %s. Make sure it is not used by another Redis instance.", server.vm_swap_file, strerror(errno)); exit(1); } #endif /* Initialize */ server.vm_next_page = 0; server.vm_near_pages = 0; server.vm_stats_used_pages = 0; server.vm_stats_swapped_objects = 0; server.vm_stats_swapouts = 0; server.vm_stats_swapins = 0; totsize = server.vm_pages*server.vm_page_size; #ifdef _WIN32 redisLog(REDIS_NOTICE,"Allocating %lld bytes of swap file",(long long) totsize); #else redisLog(REDIS_NOTICE,"Allocating %lld bytes of swap file",totsize); #endif if (ftruncate(server.vm_fd,totsize) == -1) { redisLog(REDIS_WARNING,"Can't ftruncate swap file: %s. Exiting.", strerror(errno)); exit(1); } else { redisLog(REDIS_NOTICE,"Swap file allocated with success"); } server.vm_bitmap = zcalloc((server.vm_pages+7)/8); redisLog(REDIS_VERBOSE,"Allocated %lld bytes page table for %lld pages", (long long) (server.vm_pages+7)/8, server.vm_pages); /* Initialize threaded I/O (used by Virtual Memory) */ server.io_newjobs = listCreate(); server.io_processing = listCreate(); server.io_processed = listCreate(); server.io_ready_clients = listCreate(); #ifndef _WIN32 /* moved to InitSharedObjecsts since they are first time used there */ pthread_mutex_init(&server.io_mutex,NULL); pthread_mutex_init(&server.io_swapfile_mutex,NULL); #endif server.io_active_threads = 0; if (pipe(pipefds) == -1) { redisLog(REDIS_WARNING,"Unable to intialized VM: pipe(2): %s. Exiting." ,strerror(errno)); exit(1); } server.io_ready_pipe_read = pipefds[0]; server.io_ready_pipe_write = pipefds[1]; #ifndef _WIN32 /* Windows distincts pipe and socket destriptors */ /* We will use blocking pipe, with peek before blocking read */ redisAssert(anetNonBlock(NULL,server.io_ready_pipe_read) != ANET_ERR); #endif /* LZF requires a lot of stack */ pthread_attr_init(&server.io_threads_attr); pthread_attr_getstacksize(&server.io_threads_attr, &stacksize); /* Solaris may report a stacksize of 0, let's set it to 1 otherwise * multiplying it by 2 in the while loop later will not really help ;) */ if (!stacksize) stacksize = 1; while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2; pthread_attr_setstacksize(&server.io_threads_attr, stacksize); /* Listen for events in the threaded I/O pipe */ #ifdef _WIN32 /* Windows fix: need to pass flag that notifies Api that file descriptor is pipe */ if (aeCreateFileEvent(server.el, server.io_ready_pipe_read, AE_READABLE | AE_PIPE, vmThreadedIOCompletedJob, NULL) == AE_ERR) oom("creating file event"); #else if (aeCreateFileEvent(server.el, server.io_ready_pipe_read, AE_READABLE, vmThreadedIOCompletedJob, NULL) == AE_ERR) oom("creating file event"); #endif }
Q330_CFG *q330ReadCfg(char *name) { int i, status, ntoken; FILE *fp = NULL; Q330_CFG *cfg = NULL; QDP_TYPE_C1_QCAL DefaultCalib = Q330_DEFAULT_CALIB; char *token[MAX_TOKEN]; char input[MAXLINELEN]; LNKLST *addr = NULL, *detector = NULL; static char *DefaultName = Q330_DEFAULT_CFG_PATH; /* Locate and open the file */ if (name == NULL) name = getenv(Q330_CFG_ENV_STRING); if (name == NULL) name = DefaultName; if (!utilFileExists(name)) return NULL; if ((fp = fopen(name, "r")) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL); /* Create the config structure and save the name */ if ((cfg = CreateCfg(name)) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL); cfg->calib = DefaultCalib; /* Initialize the temp lists */ if ((addr = listCreate()) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL); if ((detector = listCreate()) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL); /* Read the contents of the file into the list */ while ((status = utilGetLine(fp, input, MAXLINELEN, COMMENT, NULL)) == 0) { ntoken = utilParse(input, token, " \t", MAX_TOKEN, QUOTE); /* "q330" entries define Q330_ADDR structures */ if (strcasecmp(token[0], "q330") == 0) { if (!AddAddr(addr, token, ntoken)) return ReadCfgCleanReturn(fp, cfg, addr, NULL); /* "detector" entries define Q330_ADDR structures */ } else if (strcasecmp(token[0], "detector") == 0) { if (!AddDetector(detector, token, ntoken)) return ReadCfgCleanReturn(fp, cfg, addr, NULL); /* "calib" entries define QDP_TYPE_C1_QCAL structures */ } else if (strcasecmp(token[0], "calib") == 0) { if (!AddCalib(&cfg->calib, token, ntoken)) return ReadCfgCleanReturn(fp, cfg, addr, NULL); /* anything else is not supported */ } else { errno = EINVAL; return ReadCfgCleanReturn(fp, cfg, addr, NULL); } } if (status != 1) return ReadCfgCleanReturn(fp, cfg, addr, NULL); /* Copy the addresses into the output structure */ if (!listSetArrayView(addr)) return ReadCfgCleanReturn(fp, cfg, addr, NULL); cfg->naddr = addr->count; if ((cfg->addr = (Q330_ADDR *) malloc(sizeof(Q330_ADDR) * cfg->naddr)) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL); for (i = 0; i < addr->count; i++) cfg->addr[i] = *((Q330_ADDR *) addr->array[i]); /* Copy the detectors into the output structure */ if (!listSetArrayView(detector)) return ReadCfgCleanReturn(fp, cfg, addr, NULL); cfg->ndetector = detector->count; if ((cfg->detector = (Q330_DETECTOR *) malloc(sizeof(Q330_DETECTOR) * cfg->ndetector)) == NULL) return ReadCfgCleanReturn(fp, cfg, detector, NULL); for (i = 0; i < detector->count; i++) cfg->detector[i] = *((Q330_DETECTOR *) detector->array[i]); /* All done, return the newly created structure */ return ReadCfgCleanReturn(fp, NULL, addr, cfg); }
/* * * slotscheck * */ void slotscheckCommand(redisClient *c) { sds bug = NULL; int i; for (i = 0; i < HASH_SLOTS_SIZE && bug == NULL; i ++) { dict *d = c->db->hash_slots[i]; if (dictSize(d) == 0) { continue; } list *l = listCreate(); listSetFreeMethod(l, decrRefCountVoid); unsigned long cursor = 0; do { cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l); while (1) { listNode *head = listFirst(l); if (head == NULL) { break; } robj *key = listNodeValue(head); if (lookupKey(c->db, key) == NULL) { if (bug == NULL) { bug = sdsdup(key->ptr); } } listDelNode(l, head); } } while (cursor != 0 && bug == NULL); listRelease(l); } if (bug != NULL) { addReplyErrorFormat(c, "step 1, miss = '%s'", bug); sdsfree(bug); return; } do { dict *d = c->db->dict; if (dictSize(d) == 0) { break; } list *l = listCreate(); listSetFreeMethod(l, decrRefCountVoid); unsigned long cursor = 0; do { cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l); while (1) { listNode *head = listFirst(l); if (head == NULL) { break; } robj *key = listNodeValue(head); int slot = slots_num(key->ptr, NULL); if (dictFind(c->db->hash_slots[slot], key->ptr) == NULL) { if (bug == NULL) { bug = sdsdup(key->ptr); } } listDelNode(l, head); } } while (cursor != 0 && bug == NULL); listRelease(l); } while (0); if (bug != NULL) { addReplyErrorFormat(c, "step 2, miss = '%s'", bug); sdsfree(bug); return; } addReply(c, shared.ok); }
list *createClientlist( ) { list *clients = listCreate(); listSetFreeMethod(clients, listDeleteClientObjects); listSetMatchMethod(clients, listMatchClientObjects); return clients; }
int main(int argc, char **argv) { int i = 0; signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); config.el = aeCreateEventLoop(); config.debug = 0; config.done = 0; config.clients = listCreate(); config.num_clients = 50; config.num_requests = 10000; config.issued_requests = 0; config.keepalive = 1; config.set_perc = 50; config.del_perc = 0; config.swapin_perc = 0; config.lpush_perc = 0; config.lpop_perc = 0; config.hset_perc = 0; config.hget_perc = 0; config.hgetall_perc = 0; config.datasize_min = 1; config.datasize_max = 64; config.keyspace = DEFAULT_KEYSPACE; /* 100k */ config.hashkeyspace = DEFAULT_HASHKEYSPACE; /* 1k */ config.check = 0; config.rand = 0; config.longtail = 0; config.quiet = 0; config.loop = 0; config.idlemode = 0; config.latency = NULL; config.latency = zmalloc(sizeof(int)*(MAX_LATENCY+1)); config.ctrlc = 0; config.prngseed = (unsigned int) (microseconds()^getpid()); config.hostip = "127.0.0.1"; config.hostport = 6379; parseOptions(argc,argv); config.databuf = zmalloc(config.datasize_max); if (config.keepalive == 0) { printf("WARNING: keepalive disabled, you probably need 'echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse' for Linux and 'sudo sysctl -w net.inet.tcp.msl=1000' for Mac OS X in order to use a lot of clients/requests\n"); } if (config.idlemode) { printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.num_clients); memset(config.optab,REDIS_IDLE,100); } else { /* Setup the operation table. Start with a table with just GET * operations and overwrite it with others as needed. */ memset(config.optab,REDIS_GET,100); fillOpTab(&i,REDIS_SET,config.set_perc); fillOpTab(&i,REDIS_DEL,config.del_perc); fillOpTab(&i,REDIS_LPUSH,config.lpush_perc); fillOpTab(&i,REDIS_LPOP,config.lpop_perc); fillOpTab(&i,REDIS_HSET,config.hset_perc); fillOpTab(&i,REDIS_HGET,config.hget_perc); fillOpTab(&i,REDIS_HGETALL,config.hgetall_perc); fillOpTab(&i,REDIS_SWAPIN,config.swapin_perc); } signal(SIGINT,ctrlc); srandom(config.prngseed); printf("PRNG seed is: %u - use the 'seed' option to reproduce the same sequence\n", config.prngseed); do { prepareForBenchmark(); createMissingClients(); aeMain(config.el); endBenchmark(); } while(config.loop); return 0; }
/* * Entry point of Multi-core Insense runtime. */ int main(int argc, char* argv[]) { PRINTFMC("Cache line size: %dB\n", cache_line_size()); PRINTFMC("Main thread: %u\n", (unsigned) pthread_self()); errval_t err; coreid_t mycore = disp_get_core_id(); if (argc == 2) { num_to_span = atoi(argv[1]); if(num_to_span==0) all_spanned = true; debug_printf("Spanning onto %d cores\n", num_to_span); for (int i = 1; i < num_to_span; i++) { err = domain_new_dispatcher(mycore + i, span_cb, NULL); if (err_is_fail(err)) { DEBUG_ERR(err, "failed span %d", i); } } } else { debug_printf("ERROR: Must specify number of cores to span\n"); return EXIT_FAILURE; } posixcompat_pthread_set_placement_fn(rrPlacement); while (!all_spanned) { thread_yield(); } my_mutex_init(&shared_heap_mutex); #if HEAPS == HEAP_PRIVATE // Private heaps // Initialize mutex if (pthread_mutex_init(&thread_lock, NULL ) != 0) { PRINTF("Mutex initialization failed.\n"); return -1; } #endif mainThread = pthread_self(); // Note the ID of the main thread. // Create a list for storing references to p-threads threadList = listCreate(); // Create map used to store memory locations of small heaps (using Thread safe list) SHList = listCreate(); // Create map used to store memory locations what is allocated using malloc mallocList = listCreate(); // Start recording execution time #if TIMING // CPU time uint64_t start, end; uint64_t tsc_per_ms = 0; sys_debug_get_tsc_per_ms(&tsc_per_ms); start = rdtsc(); #endif // Call primordial_main. primordial_main(NULL ); // Join all p-threads if (threadList != NULL ) { listJoinThreads(threadList); } // Stop recording execution time #if TIMING end = rdtsc(); uint64_t diff = (end - start) / tsc_per_ms; float elapsed = (diff / 1000) + ((diff % 1000) / 1000.0); printf("CPU: %f seconds elapsed\n", elapsed); #endif // Destroy lists and free memory listDestroy(threadList); listDestroy(SHList); listDestroy(mallocList); #if HEAPS == HEAP_PRIVATE pthread_mutex_destroy(&thread_lock); // Destroy mutex lock used with pthreads #endif return 0; }
static void *check_thread_run(void *args) { int ret; thread_data *cdata = args; redis_group *srgroup = cdata->srgroup; redis_group *trgroup = cdata->trgroup; dict *nodes; dictEntry *de; dictIterator *di; redis_node *rnode; struct mbuf *mbuf; nodes = srgroup->nodes; di = dictGetIterator(nodes); while ((de = dictNext(di)) != NULL) { rnode = dictGetVal(de); rnode->write_data = cdata; /* remove the not used part for source redis node */ if (rnode->rdb != NULL) { redis_rdb_deinit(rnode->rdb); rmt_free(rnode->rdb); rnode->rdb = NULL; } if (rnode->cmd_data != NULL) { while (!mttlist_empty(rnode->cmd_data)) { mbuf = mttlist_pop(rnode->cmd_data); mbuf_put(mbuf); } mttlist_destroy(rnode->cmd_data); rnode->cmd_data = NULL; } if (rnode->sockpairfds[0] > 0) { close(rnode->sockpairfds[0]); rnode->sockpairfds[0] = -1; } if (rnode->sockpairfds[1] > 0) { close(rnode->sockpairfds[1]); rnode->sockpairfds[1] = -1; } if (rnode->rr != NULL) { redis_replication_deinit(rnode->rr); rmt_free(rnode->rr); rnode->rr = NULL; } if (rnode->piece_data != NULL) { while ((mbuf = listPop(rnode->piece_data)) != NULL) { mbuf_put(mbuf); } listRelease(rnode->piece_data); rnode->piece_data = NULL; } /* add the used part for source redis node */ if (rnode->send_data == NULL) { rnode->send_data = listCreate(); if (rnode->send_data == NULL) { log_error("ERROR: Create msg list failed: out of memory"); return 0; } } if (rnode->sent_data == NULL) { rnode->sent_data = listCreate(); if (rnode->sent_data == NULL) { log_error("ERROR: Create msg list failed: out of memory"); return 0; } } if (rnode->sk_event < 0) { rnode->sk_event = socket(AF_INET, SOCK_STREAM, 0); if(rnode->sk_event < 0){ log_error("ERROR: Create sk_event for node[%s] failed: %s", rnode->addr, strerror(errno)); return 0; } } ret = aeCreateFileEvent(cdata->loop, rnode->sk_event, AE_WRITABLE, check_begin, rnode); if (ret != AE_OK) { log_error("ERROR: send_data event create %ld failed: %s", cdata->thread_id, strerror(errno)); return 0; } } dictReleaseIterator(di); nodes = trgroup->nodes; di = dictGetIterator(nodes); while ((de = dictNext(di)) != NULL) { rnode = dictGetVal(de); rnode->write_data = cdata; } dictReleaseIterator(di); aeMain(cdata->loop); return 0; }
/* This function should be called by Redis every time a single command, * a MULTI/EXEC block, or a Lua script, terminated its execution after * being called by a client. * * All the keys with at least one client blocked that received at least * one new element via some PUSH/XADD operation are accumulated into * the server.ready_keys list. This function will run the list and will * serve clients accordingly. Note that the function will iterate again and * again as a result of serving BRPOPLPUSH we can have new blocking clients * to serve because of the PUSH side of BRPOPLPUSH. */ void handleClientsBlockedOnKeys(void) { while(listLength(server.ready_keys) != 0) { list *l; /* Point server.ready_keys to a fresh list and save the current one * locally. This way as we run the old list we are free to call * signalKeyAsReady() that may push new elements in server.ready_keys * when handling clients blocked into BRPOPLPUSH. */ l = server.ready_keys; server.ready_keys = listCreate(); while(listLength(l) != 0) { listNode *ln = listFirst(l); readyList *rl = ln->value; /* First of all remove this key from db->ready_keys so that * we can safely call signalKeyAsReady() against this key. */ dictDelete(rl->db->ready_keys,rl->key); /* Serve clients blocked on list key. */ robj *o = lookupKeyWrite(rl->db,rl->key); if (o != NULL && o->type == OBJ_LIST) { dictEntry *de; /* We serve clients in the same order they blocked for * this key, from the first blocked to the last. */ de = dictFind(rl->db->blocking_keys,rl->key); if (de) { list *clients = dictGetVal(de); int numclients = listLength(clients); while(numclients--) { listNode *clientnode = listFirst(clients); client *receiver = clientnode->value; if (receiver->btype != BLOCKED_LIST) { /* Put on the tail, so that at the next call * we'll not run into it again. */ listDelNode(clients,clientnode); listAddNodeTail(clients,receiver); continue; } robj *dstkey = receiver->bpop.target; int where = (receiver->lastcmd && receiver->lastcmd->proc == blpopCommand) ? LIST_HEAD : LIST_TAIL; robj *value = listTypePop(o,where); if (value) { /* Protect receiver->bpop.target, that will be * freed by the next unblockClient() * call. */ if (dstkey) incrRefCount(dstkey); unblockClient(receiver); if (serveClientBlockedOnList(receiver, rl->key,dstkey,rl->db,value, where) == C_ERR) { /* If we failed serving the client we need * to also undo the POP operation. */ listTypePush(o,value,where); } if (dstkey) decrRefCount(dstkey); decrRefCount(value); } else { break; } } } if (listTypeLength(o) == 0) { dbDelete(rl->db,rl->key); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",rl->key,rl->db->id); } /* We don't call signalModifiedKey() as it was already called * when an element was pushed on the list. */ } /* Serve clients blocked on stream key. */ else if (o != NULL && o->type == OBJ_STREAM) { dictEntry *de = dictFind(rl->db->blocking_keys,rl->key); stream *s = o->ptr; /* We need to provide the new data arrived on the stream * to all the clients that are waiting for an offset smaller * than the current top item. */ if (de) { list *clients = dictGetVal(de); listNode *ln; listIter li; listRewind(clients,&li); while((ln = listNext(&li))) { client *receiver = listNodeValue(ln); if (receiver->btype != BLOCKED_STREAM) continue; streamID *gt = dictFetchValue(receiver->bpop.keys, rl->key); if (s->last_id.ms > gt->ms || (s->last_id.ms == gt->ms && s->last_id.seq > gt->seq)) { streamID start = *gt; start.seq++; /* Can't overflow, it's an uint64_t */ /* If we blocked in the context of a consumer * group, we need to resolve the group and * consumer here. */ streamCG *group = NULL; streamConsumer *consumer = NULL; if (receiver->bpop.xread_group) { group = streamLookupCG(s, receiver->bpop.xread_group->ptr); /* In theory if the group is not found we * just perform the read without the group, * but actually when the group, or the key * itself is deleted (triggering the removal * of the group), we check for blocked clients * and send them an error. */ } if (group) { consumer = streamLookupConsumer(group, receiver->bpop.xread_consumer->ptr, 1); } /* Note that after we unblock the client, 'gt' * and other receiver->bpop stuff are no longer * valid, so we must do the setup above before * this call. */ unblockClient(receiver); /* Emit the two elements sub-array consisting of * the name of the stream and the data we * extracted from it. Wrapped in a single-item * array, since we have just one key. */ addReplyMultiBulkLen(receiver,1); addReplyMultiBulkLen(receiver,2); addReplyBulk(receiver,rl->key); streamPropInfo pi = { rl->key, receiver->bpop.xread_group }; streamReplyWithRange(receiver,s,&start,NULL, receiver->bpop.xread_count, 0, group, consumer, 0, &pi); } } } } /* Free this item. */ decrRefCount(rl->key); zfree(rl); listDelNode(l,ln); } listRelease(l); /* We have the new list on place at this point. */ } }
void init_res (void) { static char fname[] = "init_res"; int i, maxfds; if (logclass & (LC_TRACE | LC_HANG)) ls_syslog (LOG_DEBUG, "%s: Entering this routine...", fname); if (!sbdMode) { if (!debug) { if (geteuid () || getuid ()) { fprintf (stderr, "RES should be run as root.\n"); fflush (stderr); resExit_ (1); } chdir ("/tmp"); } if (debug <= 1) { daemonize_ (); ls_openlog ("res", resParams[LSF_LOGDIR].paramValue, 0, resParams[LSF_LOG_MASK].paramValue); umask (0); nice (NICE_LEAST); } } if ((Myhost = ls_getmyhostname ()) == NULL) { ls_syslog (LOG_ERR, I18N_FUNC_FAIL_MM, fname, "ls_getmyhostname"); resExit_ (-1); } if (isatty (0)) { tcgetattr (0, &defaultTty.attr); defaultTty.ws.ws_row = 24; defaultTty.ws.ws_col = 80; defaultTty.ws.ws_xpixel = defaultTty.ws.ws_ypixel = 0; } else { defaultTty.ws.ws_row = 24; defaultTty.ws.ws_col = 80; defaultTty.ws.ws_xpixel = defaultTty.ws.ws_ypixel = 0; } if (!sbdMode) { init_AcceptSock (); } client_cnt = child_cnt = 0; for (i = 0; i < MAXCLIENTS_HIGHWATER_MARK + 1; i++) { clients[i] = NULL; } children = (struct child **) calloc (sysconf (_SC_OPEN_MAX), sizeof (struct children *)); if (!children) { ls_syslog (LOG_ERR, I18N_FUNC_FAIL_M, fname, "calloc"); resExit_ (-1); } maxfds = sysconf (_SC_OPEN_MAX); for (i = 0; i < maxfds; i++) { children[i] = NULL; } initConn2NIOS (); resNotifyList = listCreate ("resNotifyList"); if (!resNotifyList) { ls_syslog (LOG_ERR, I18N_FUNC_FAIL_M, fname, "listCreate"); resExit_ (-1); } ls_syslog (LOG_INFO, (_i18n_msg_get (ls_catd, NL_SETN, 5346, "Daemon started"))); /* catgets 5346 */ initResLog (); }
rstatus_t init_server(struct instance *nci) { rstatus_t status; uint32_t i; redisDb *db; vr_conf *conf; conf = conf_create(nci->conf_filename); server.pid = getpid(); server.configfile = getAbsolutePath(nci->conf_filename); server.hz = 10; server.dbnum = 16; array_init(&server.dbs, server.dbnum, sizeof(redisDb)); server.pidfile = nci->pid_filename; server.executable = NULL; server.activerehashing = CONFIG_DEFAULT_ACTIVE_REHASHING; get_random_hex_chars(server.runid, CONFIG_RUN_ID_SIZE); server.arch_bits = (sizeof(long) == 8) ? 64 : 32; server.requirepass = NULL; server.starttime = time(NULL); server.maxclients = conf->max_clients; server.maxmemory = conf->maxmemory == CONF_UNSET_NUM ? 0 : conf->maxmemory; server.maxmemory_policy = CONFIG_DEFAULT_MAXMEMORY_POLICY; server.client_max_querybuf_len = PROTO_MAX_QUERYBUF_LEN; server.commands = dictCreate(&commandTableDictType,NULL); populateCommandTable(); server.delCommand = lookupCommandByCString("del"); server.multiCommand = lookupCommandByCString("multi"); server.lpushCommand = lookupCommandByCString("lpush"); server.lpopCommand = lookupCommandByCString("lpop"); server.rpopCommand = lookupCommandByCString("rpop"); server.sremCommand = lookupCommandByCString("srem"); server.execCommand = lookupCommandByCString("exec"); for (i = 0; i < server.dbnum; i ++) { db = array_push(&server.dbs); redisDbInit(db); } server.monitors = listCreate(); server.loading = 0; server.lua_timedout = 0; server.aof_state = AOF_OFF; server.stop_writes_on_bgsave_err = 0; server.ready_keys = listCreate(); server.slowlog = listCreate(); server.slowlog_entry_id = 0; server.slowlog_log_slower_than = -1; server.slowlog_max_len = CONFIG_DEFAULT_SLOWLOG_MAX_LEN; server.stat_peak_memory = 0; server.system_memory_size = zmalloc_get_memory_size(); server.rdb_child_pid = -1; server.aof_child_pid = -1; server.hash_max_ziplist_entries = OBJ_HASH_MAX_ZIPLIST_ENTRIES; server.hash_max_ziplist_value = OBJ_HASH_MAX_ZIPLIST_VALUE; server.list_max_ziplist_size = OBJ_LIST_MAX_ZIPLIST_SIZE; server.list_compress_depth = OBJ_LIST_COMPRESS_DEPTH; server.set_max_intset_entries = OBJ_SET_MAX_INTSET_ENTRIES; server.zset_max_ziplist_entries = OBJ_ZSET_MAX_ZIPLIST_ENTRIES; server.zset_max_ziplist_value = OBJ_ZSET_MAX_ZIPLIST_VALUE; server.hll_sparse_max_bytes = CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES; vr_replication_init(); createSharedObjects(); status = master_init(conf); if (status != VR_OK) { log_error("init master thread failed"); return VR_ERROR; } server.port = master.listen->port; status = workers_init(nci->thread_num); if (status != VR_OK) { log_error("init worker threads failed"); return VR_ERROR; } log_debug(LOG_NOTICE, "mem_alloc_lock_type: %s", malloc_lock_type()); log_debug(LOG_NOTICE, "malloc lib: %s", VR_MALLOC_LIB); return VR_OK; }
/* This function should be called by Redis every time a single command, * a MULTI/EXEC block, or a Lua script, terminated its execution after * being called by a client. * * All the keys with at least one client blocked that received at least * one new element via some PUSH operation are accumulated into * the server.ready_keys list. This function will run the list and will * serve clients accordingly. Note that the function will iterate again and * again as a result of serving BRPOPLPUSH we can have new blocking clients * to serve because of the PUSH side of BRPOPLPUSH. */ void handleClientsBlockedOnLists(void) { while(listLength(server.ready_keys) != 0) { list *l; /* Point server.ready_keys to a fresh list and save the current one * locally. This way as we run the old list we are free to call * signalListAsReady() that may push new elements in server.ready_keys * when handling clients blocked into BRPOPLPUSH. */ l = server.ready_keys; server.ready_keys = listCreate(); while(listLength(l) != 0) { listNode *ln = listFirst(l); readyList *rl = ln->value; /* First of all remove this key from db->ready_keys so that * we can safely call signalListAsReady() against this key. */ dictDelete(rl->db->ready_keys,rl->key); /* If the key exists and it's a list, serve blocked clients * with data. */ robj *o = lookupKeyWrite(rl->db,rl->key); if (o != NULL && o->type == REDIS_LIST) { dictEntry *de; /* We serve clients in the same order they blocked for * this key, from the first blocked to the last. */ de = dictFind(rl->db->blocking_keys,rl->key); if (de) { list *clients = dictGetVal(de); int numclients = listLength(clients); while(numclients--) { listNode *clientnode = listFirst(clients); redisClient *receiver = clientnode->value; robj *dstkey = receiver->bpop.target; int where = (receiver->lastcmd && receiver->lastcmd->proc == blpopCommand) ? REDIS_HEAD : REDIS_TAIL; robj *value = listTypePop(o,where); if (value) { /* Protect receiver->bpop.target, that will be * freed by the next unblockClientWaitingData() * call. */ if (dstkey) incrRefCount(dstkey); unblockClientWaitingData(receiver); if (serveClientBlockedOnList(receiver, rl->key,dstkey,rl->db,value, where) == REDIS_ERR) { /* If we failed serving the client we need * to also undo the POP operation. */ listTypePush(o,value,where); } if (dstkey) decrRefCount(dstkey); decrRefCount(value); } else { break; } } } if (listTypeLength(o) == 0) dbDelete(rl->db,rl->key); /* We don't call signalModifiedKey() as it was already called * when an element was pushed on the list. */ } /* Free this item. */ decrRefCount(rl->key); zfree(rl); listDelNode(l,ln); } listRelease(l); /* We have the new list on place at this point. */ } }
static list *index_list_create() { list *idx = listCreate(); listSetMatchMethod(idx, fts_doc_match); listSetFreeMethod(idx, index_item_free); return idx; }
/* * * do migrate mutli key-value(s) for {slotsmgrt/slotsmgrtone}with tag commands * return value: * -1 - error happens * >=0 - # of success migration * */ static int slotsmgrttag_command(redisClient *c, sds host, sds port, int timeout, robj *key) { int taglen; void *tag = slots_tag(key->ptr, &taglen); if (tag == NULL) { return slotsmgrtone_command(c, host, port, timeout, key); } int fd = slotsmgrt_get_socket(c, host, port, timeout); if (fd == -1) { return -1; } list *l = listCreate(); listSetFreeMethod(l, decrRefCountVoid); do { uint32_t crc; int slot = slots_num(key->ptr, &crc); dict *d = c->db->hash_slots[slot]; long long cursor = 0; void *args[] = {l, tag, &taglen, (void *)(long)crc}; do { cursor = dictScan(d, cursor, slotsScanSdsKeyTagCallback, args); } while (cursor != 0); } while (0); int max = listLength(l); if (max == 0) { listRelease(l); return 0; } robj **keys = zmalloc(sizeof(robj *) * max); robj **vals = zmalloc(sizeof(robj *) * max); int n = 0; for (int i = 0; i < max; i ++) { listNode *head = listFirst(l); robj *key = listNodeValue(head); robj *val = lookupKeyWrite(c->db, key); if (val != NULL) { keys[n] = key; vals[n] = val; n ++; incrRefCount(key); } listDelNode(l, head); } int ret = 0; if (n != 0) { if (slotsmgrt(c, host, port, fd, c->db->id, timeout, keys, vals, n) != 0) { slotsmgrt_close_socket(host, port); ret = -1; } else { slotsremove(c, keys, n, 1); ret = n; } } listRelease(l); for (int i = 0; i < n; i ++) { decrRefCount(keys[i]); } zfree(keys); zfree(vals); return ret; }
//函数会在redis每次执行完单个命令,事务块或lua脚本之后被调用 //对于所有被阻塞在client的key来说,只要key被执行了PUSH,那么这个key会被加入到server.ready_keys中 //处理client的阻塞状态 void handleClientsBlockedOnLists(void) { //只要server.ready_keys还有要解除阻塞的key,就循环遍历server.ready_keys链表 while(listLength(server.ready_keys) != 0) { list *l; /* Point server.ready_keys to a fresh list and save the current one * locally. This way as we run the old list we are free to call * signalListAsReady() that may push new elements in server.ready_keys * when handling clients blocked into BRPOPLPUSH. */ //备份一个server.ready_keys链表 l = server.ready_keys; //生成一个新的空链表 server.ready_keys = listCreate(); //只要链表中还有就绪的key while(listLength(l) != 0) { listNode *ln = listFirst(l); //链表头结点地址 readyList *rl = ln->value; //保存链表节点的值,每个值都是readyList结构 /* First of all remove this key from db->ready_keys so that * we can safely call signalListAsReady() against this key. */ //从rl->db->ready_keys中删除就绪的key dictDelete(rl->db->ready_keys,rl->key); /* If the key exists and it's a list, serve blocked clients * with data. */ //以读操作将就绪key的值读出来 robj *o = lookupKeyWrite(rl->db,rl->key); //读出的value对象必须是列表类型 if (o != NULL && o->type == OBJ_LIST) { dictEntry *de; /* We serve clients in the same order they blocked for * this key, from the first blocked to the last. */ // blocking_keys是一个字典,字典的键是造成client阻塞的键,字典的值是链表,保存被阻塞的client // 根据key取出被阻塞的client de = dictFind(rl->db->blocking_keys,rl->key); // 链表非空 if (de) { // 获取de节点的值 list *clients = dictGetVal(de); // 获取链表的长度 int numclients = listLength(clients); //遍历链表的所有节点 while(numclients--) { // 第一个client节点地址 listNode *clientnode = listFirst(clients); // 取出节点的值,是一个client类型 client *receiver = clientnode->value; // 从client类型中的target获得要PUSH出的dstkey,该键保存在target中 robj *dstkey = receiver->bpop.target; // 获取弹出的位置,根据BRPOPLPUSH命令 int where = (receiver->lastcmd && receiver->lastcmd->proc == blpopCommand) ? LIST_HEAD : LIST_TAIL; // 从列表中弹出元素 robj *value = listTypePop(o,where); // 弹出成功 if (value) { /* Protect receiver->bpop.target, that will be * freed by the next unblockClient() * call. */ //增加dstkey的引用计数,保护该键,在unblockClient函数中释放 if (dstkey) incrRefCount(dstkey); //取消client的阻塞状态 unblockClient(receiver); // 将value推入造成client阻塞的键上, if (serveClientBlockedOnList(receiver, rl->key,dstkey,rl->db,value, where) == C_ERR) { /* If we failed serving the client we need * to also undo the POP operation. */ // 如果推入失败,则需要键弹出的value还原回去 listTypePush(o,value,where); } // 释放dstkey和value if (dstkey) decrRefCount(dstkey); decrRefCount(value); } else { break; } } } // 如果弹出了所有元素,将key从数据库中删除 if (listTypeLength(o) == 0) { dbDelete(rl->db,rl->key); } /* We don't call signalModifiedKey() as it was already called * when an element was pushed on the list. */ } /* Free this item. */ //释放所有空间 decrRefCount(rl->key); zfree(rl); listDelNode(l,ln); } //释放原来的ready_keys,因为之前创建了新的链表 listRelease(l); /* We have the new list on place at this point. */ } }
/* This function makes the clinet 'c' waiting for the key 'key' to be loaded. * If there is not already a job loading the key, it is craeted. * The key is added to the io_keys list in the client structure, and also * in the hash table mapping swapped keys to waiting clients, that is, * server.io_waited_keys. */ int waitForSwappedKey(redisClient *c, robj *key) { struct dictEntry *de; robj *o; list *l; /* If the key does not exist or is already in RAM we don't need to * block the client at all. */ de = dictFind(c->db->dict,key->ptr); if (de == NULL) return 0; o = dictGetEntryVal(de); if (o->storage == REDIS_VM_MEMORY) { return 0; } else if (o->storage == REDIS_VM_SWAPPING) { /* We were swapping the key, undo it! */ vmCancelThreadedIOJob(o); return 0; } /* OK: the key is either swapped, or being loaded just now. */ /* Add the key to the list of keys this client is waiting for. * This maps clients to keys they are waiting for. */ listAddNodeTail(c->io_keys,key); incrRefCount(key); /* Add the client to the swapped keys => clients waiting map. */ de = dictFind(c->db->io_keys,key); if (de == NULL) { int retval; /* For every key we take a list of clients blocked for it */ l = listCreate(); retval = dictAdd(c->db->io_keys,key,l); incrRefCount(key); redisAssert(retval == DICT_OK); } else { l = dictGetEntryVal(de); } listAddNodeTail(l,c); /* Are we already loading the key from disk? If not create a job */ if (o->storage == REDIS_VM_SWAPPED) { iojob *j; vmpointer *vp = (vmpointer*)o; o->storage = REDIS_VM_LOADING; j = zmalloc(sizeof(*j)); j->type = REDIS_IOJOB_LOAD; j->db = c->db; j->id = (robj*)vp; j->key = key; incrRefCount(key); j->page = vp->page; j->val = NULL; j->canceled = 0; j->thread = (pthread_t) -1; lockThreadedIO(); queueIOJob(j); unlockThreadedIO(); } return 1; }
void vmInit(void) { off_t totsize; int pipefds[2]; size_t stacksize; struct flock fl; if (server.vm_max_threads != 0) zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */ redisLog(REDIS_NOTICE,"Using '%s' as swap file",server.vm_swap_file); /* Try to open the old swap file, otherwise create it */ if ((server.vm_fp = fopen(server.vm_swap_file,"r+b")) == NULL) { server.vm_fp = fopen(server.vm_swap_file,"w+b"); } if (server.vm_fp == NULL) { redisLog(REDIS_WARNING, "Can't open the swap file: %s. Exiting.", strerror(errno)); exit(1); } server.vm_fd = fileno(server.vm_fp); /* Lock the swap file for writing, this is useful in order to avoid * another instance to use the same swap file for a config error. */ fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0; if (fcntl(server.vm_fd,F_SETLK,&fl) == -1) { redisLog(REDIS_WARNING, "Can't lock the swap file at '%s': %s. Make sure it is not used by another Redis instance.", server.vm_swap_file, strerror(errno)); exit(1); } /* Initialize */ server.vm_next_page = 0; server.vm_near_pages = 0; server.vm_stats_used_pages = 0; server.vm_stats_swapped_objects = 0; server.vm_stats_swapouts = 0; server.vm_stats_swapins = 0; totsize = server.vm_pages*server.vm_page_size; redisLog(REDIS_NOTICE,"Allocating %lld bytes of swap file",totsize); if (ftruncate(server.vm_fd,totsize) == -1) { redisLog(REDIS_WARNING,"Can't ftruncate swap file: %s. Exiting.", strerror(errno)); exit(1); } else { redisLog(REDIS_NOTICE,"Swap file allocated with success"); } server.vm_bitmap = zcalloc((server.vm_pages+7)/8); redisLog(REDIS_VERBOSE,"Allocated %lld bytes page table for %lld pages", (long long) (server.vm_pages+7)/8, server.vm_pages); /* Initialize threaded I/O (used by Virtual Memory) */ server.io_newjobs = listCreate(); server.io_processing = listCreate(); server.io_processed = listCreate(); server.io_ready_clients = listCreate(); pthread_mutex_init(&server.io_mutex,NULL); pthread_mutex_init(&server.io_swapfile_mutex,NULL); server.io_active_threads = 0; if (pipe(pipefds) == -1) { redisLog(REDIS_WARNING,"Unable to intialized VM: pipe(2): %s. Exiting." ,strerror(errno)); exit(1); } server.io_ready_pipe_read = pipefds[0]; server.io_ready_pipe_write = pipefds[1]; redisAssert(anetNonBlock(NULL,server.io_ready_pipe_read) != ANET_ERR); /* LZF requires a lot of stack */ pthread_attr_init(&server.io_threads_attr); pthread_attr_getstacksize(&server.io_threads_attr, &stacksize); /* Solaris may report a stacksize of 0, let's set it to 1 otherwise * multiplying it by 2 in the while loop later will not really help ;) */ if (!stacksize) stacksize = 1; while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2; pthread_attr_setstacksize(&server.io_threads_attr, stacksize); /* Listen for events in the threaded I/O pipe */ if (aeCreateFileEvent(server.el, server.io_ready_pipe_read, AE_READABLE, vmThreadedIOCompletedJob, NULL) == AE_ERR) oom("creating file event"); }
int main(int argc, const char **argv) { int i; char *data, *cmd; int len; client c; #ifdef _WIN32 w32initWinSock(); #endif signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); config.numclients = 50; config.requests = 10000; config.liveclients = 0; config.el = aeCreateEventLoop(); aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL); config.keepalive = 1; config.datasize = 3; config.randomkeys = 0; config.randomkeys_keyspacelen = 0; config.quiet = 0; config.loop = 0; config.idlemode = 0; config.latency = NULL; config.clients = listCreate(); config.hostip = "127.0.0.1"; config.hostport = 6379; config.hostsocket = NULL; i = parseOptions(argc,argv); argc -= i; argv += i; config.latency = zmalloc(sizeof(long long)*config.requests); if (config.keepalive == 0) { printf("WARNING: keepalive disabled, you probably need 'echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse' for Linux and 'sudo sysctl -w net.inet.tcp.msl=1000' for Mac OS X in order to use a lot of clients/requests\n"); } if (config.idlemode) { printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.numclients); c = createClient("",0); /* will never receive a reply */ createMissingClients(c); aeMain(config.el); /* and will wait for every */ } /* Run benchmark with command in the remainder of the arguments. */ if (argc) { sds title = sdsnew(argv[0]); for (i = 1; i < argc; i++) { title = sdscatlen(title, " ", 1); title = sdscatlen(title, (char*)argv[i], strlen(argv[i])); } do { len = redisFormatCommandArgv(&cmd,argc,argv,NULL); benchmark(title,cmd,len); free(cmd); } while(config.loop); return 0; } /* Run default benchmark suite. */ do { data = zmalloc(config.datasize+1); memset(data,'x',config.datasize); data[config.datasize] = '\0'; benchmark("PING (inline)","PING\r\n",6); len = redisFormatCommand(&cmd,"PING"); benchmark("PING",cmd,len); free(cmd); const char *argv[21]; argv[0] = "MSET"; for (i = 1; i < 21; i += 2) { argv[i] = "foo:rand:000000000000"; argv[i+1] = data; } len = redisFormatCommandArgv(&cmd,21,argv,NULL); benchmark("MSET (10 keys)",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"SET foo:rand:000000000000 %s",data); benchmark("SET",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"GET foo:rand:000000000000"); benchmark("GET",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"INCR counter:rand:000000000000"); benchmark("INCR",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"LPUSH mylist %s",data); benchmark("LPUSH",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"LPOP mylist"); benchmark("LPOP",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"SADD myset counter:rand:000000000000"); benchmark("SADD",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"SPOP myset"); benchmark("SPOP",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"LPUSH mylist %s",data); benchmark("LPUSH (again, in order to bench LRANGE)",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"LRANGE mylist 0 99"); benchmark("LRANGE (first 100 elements)",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"LRANGE mylist 0 299"); benchmark("LRANGE (first 300 elements)",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"LRANGE mylist 0 449"); benchmark("LRANGE (first 450 elements)",cmd,len); free(cmd); len = redisFormatCommand(&cmd,"LRANGE mylist 0 599"); benchmark("LRANGE (first 600 elements)",cmd,len); free(cmd); printf("\n"); } while(config.loop); #ifdef _WIN32 WSACleanup(); #endif return 0; }
/* The SORT command is the most complex command in Redis. Warning: this code * is optimized for speed and a bit less for readability */ void sortCommand(redisClient *c) { list *operations; unsigned int outputlen = 0; int desc = 0, alpha = 0; int limit_start = 0, limit_count = -1, start, end; int j, dontsort = 0, vectorlen; int getop = 0; /* GET operation counter */ robj *sortval, *sortby = NULL, *storekey = NULL; redisSortObject *vector; /* Resulting vector to sort */ /* Lookup the key to sort. It must be of the right types */ sortval = lookupKeyRead(c->db,c->argv[1]); if (sortval == NULL) { addReply(c,shared.emptymultibulk); return; } if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST && sortval->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); return; } /* Create a list of operations to perform for every sorted element. * Operations can be GET/DEL/INCR/DECR */ operations = listCreate(); listSetFreeMethod(operations,zfree); j = 2; /* Now we need to protect sortval incrementing its count, in the future * SORT may have options able to overwrite/delete keys during the sorting * and the sorted key itself may get destroied */ incrRefCount(sortval); /* The SORT command has an SQL-alike syntax, parse it */ while(j < c->argc) { int leftargs = c->argc-j-1; if (!strcasecmp(c->argv[j]->ptr,"asc")) { desc = 0; } else if (!strcasecmp(c->argv[j]->ptr,"desc")) { desc = 1; } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) { alpha = 1; } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) { limit_start = atoi(c->argv[j+1]->ptr); limit_count = atoi(c->argv[j+2]->ptr); j+=2; } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) { storekey = c->argv[j+1]; j++; } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) { sortby = c->argv[j+1]; /* If the BY pattern does not contain '*', i.e. it is constant, * we don't need to sort nor to lookup the weight keys. */ if (strchr(c->argv[j+1]->ptr,'*') == NULL) dontsort = 1; j++; } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) { listAddNodeTail(operations,createSortOperation( REDIS_SORT_GET,c->argv[j+1])); getop++; j++; } else { decrRefCount(sortval); listRelease(operations); addReply(c,shared.syntaxerr); return; } j++; } /* Destructively convert encoded sorted sets for SORT. */ if (sortval->type == REDIS_ZSET) zsetConvert(sortval, REDIS_ENCODING_SKIPLIST); /* Load the sorting vector with all the objects to sort */ switch(sortval->type) { case REDIS_LIST: vectorlen = listTypeLength(sortval); break; case REDIS_SET: vectorlen = setTypeSize(sortval); break; case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break; default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */ } vector = zmalloc(sizeof(redisSortObject)*vectorlen); j = 0; if (sortval->type == REDIS_LIST) { listTypeIterator *li = listTypeInitIterator(sortval,0,REDIS_TAIL); listTypeEntry entry; while(listTypeNext(li,&entry)) { vector[j].obj = listTypeGet(&entry); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } listTypeReleaseIterator(li); } else if (sortval->type == REDIS_SET) { setTypeIterator *si = setTypeInitIterator(sortval); robj *ele; while((ele = setTypeNextObject(si)) != NULL) { vector[j].obj = ele; vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } setTypeReleaseIterator(si); } else if (sortval->type == REDIS_ZSET) { dict *set = ((zset*)sortval->ptr)->dict; dictIterator *di; dictEntry *setele; di = dictGetIterator(set); while((setele = dictNext(di)) != NULL) { vector[j].obj = dictGetEntryKey(setele); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } dictReleaseIterator(di); } else { redisPanic("Unknown type"); } redisAssert(j == vectorlen); /* Now it's time to load the right scores in the sorting vector */ if (dontsort == 0) { for (j = 0; j < vectorlen; j++) { robj *byval; if (sortby) { /* lookup value to sort by */ byval = lookupKeyByPattern(c->db,sortby,vector[j].obj); if (!byval) continue; } else { /* use object itself to sort by */ byval = vector[j].obj; } if (alpha) { if (sortby) vector[j].u.cmpobj = getDecodedObject(byval); } else { if (byval->encoding == REDIS_ENCODING_RAW) { vector[j].u.score = strtod(byval->ptr,NULL); } else if (byval->encoding == REDIS_ENCODING_INT) { /* Don't need to decode the object if it's * integer-encoded (the only encoding supported) so * far. We can just cast it */ vector[j].u.score = (long)byval->ptr; } else { redisAssert(1 != 1); } } /* when the object was retrieved using lookupKeyByPattern, * its refcount needs to be decreased. */ if (sortby) { decrRefCount(byval); } } } /* We are ready to sort the vector... perform a bit of sanity check * on the LIMIT option too. We'll use a partial version of quicksort. */ start = (limit_start < 0) ? 0 : limit_start; end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1; if (start >= vectorlen) { start = vectorlen-1; end = vectorlen-2; } if (end >= vectorlen) end = vectorlen-1; if (dontsort == 0) { server.sort_desc = desc; server.sort_alpha = alpha; server.sort_bypattern = sortby ? 1 : 0; if (sortby && (start != 0 || end != vectorlen-1)) pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end); else qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare); } /* Send command output to the output buffer, performing the specified * GET/DEL/INCR/DECR operations if any. */ outputlen = getop ? getop*(end-start+1) : end-start+1; if (storekey == NULL) { /* STORE option not specified, sent the sorting result to client */ addReplyMultiBulkLen(c,outputlen); for (j = start; j <= end; j++) { listNode *ln; listIter li; if (!getop) addReplyBulk(c,vector[j].obj); listRewind(operations,&li); while((ln = listNext(&li))) { redisSortOperation *sop = ln->value; robj *val = lookupKeyByPattern(c->db,sop->pattern, vector[j].obj); if (sop->type == REDIS_SORT_GET) { if (!val) { addReply(c,shared.nullbulk); } else { addReplyBulk(c,val); decrRefCount(val); } } else { redisAssert(sop->type == REDIS_SORT_GET); /* always fails */ } } } } else { robj *sobj = createZiplistObject(); /* STORE option specified, set the sorting result as a List object */ for (j = start; j <= end; j++) { listNode *ln; listIter li; if (!getop) { listTypePush(sobj,vector[j].obj,REDIS_TAIL); } else { listRewind(operations,&li); while((ln = listNext(&li))) { redisSortOperation *sop = ln->value; robj *val = lookupKeyByPattern(c->db,sop->pattern, vector[j].obj); if (sop->type == REDIS_SORT_GET) { if (!val) val = createStringObject("",0); /* listTypePush does an incrRefCount, so we should take care * care of the incremented refcount caused by either * lookupKeyByPattern or createStringObject("",0) */ listTypePush(sobj,val,REDIS_TAIL); decrRefCount(val); } else { /* always fails */ redisAssert(sop->type == REDIS_SORT_GET); } } } } setKey(c->db,storekey,sobj); decrRefCount(sobj); /* Note: we add 1 because the DB is dirty anyway since even if the * SORT result is empty a new key is set and maybe the old content * replaced. */ server.dirty += 1+outputlen; addReplyLongLong(c,outputlen); } /* Cleanup */ if (sortval->type == REDIS_LIST || sortval->type == REDIS_SET) for (j = 0; j < vectorlen; j++) decrRefCount(vector[j].obj); decrRefCount(sortval); listRelease(operations); for (j = 0; j < vectorlen; j++) { if (alpha && vector[j].u.cmpobj) decrRefCount(vector[j].u.cmpobj); } zfree(vector); }
/* This command implements SCAN, HSCAN and SSCAN commands. * If object 'o' is passed, then it must be a Hash or Set object, otherwise * if 'o' is NULL the command will operate on the dictionary associated with * the current database. * * When 'o' is not NULL the function assumes that the first argument in * the client arguments vector is a key so it skips it before iterating * in order to parse options. * * In the case of a Hash object the function returns both the field and value * of every element on the Hash. */ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { int i, j; list *keys = listCreate(); listNode *node, *nextnode; long count = 10; sds pat = NULL; int patlen = 0, use_pattern = 0; dict *ht; /* Object must be NULL (to iterate keys names), or the type of the object * must be Set, Sorted Set, or Hash. */ serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH || o->type == OBJ_ZSET); /* Set i to the first option argument. The previous one is the cursor. */ i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */ /* Step 1: Parse options. */ while (i < c->argc) { j = c->argc - i; if (!strcasecmp(c->argv[i]->ptr, "count") && j >= 2) { if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL) != C_OK) { goto cleanup; } if (count < 1) { addReply(c,shared.syntaxerr); goto cleanup; } i += 2; } else if (!strcasecmp(c->argv[i]->ptr, "match") && j >= 2) { pat = c->argv[i+1]->ptr; patlen = sdslen(pat); /* The pattern always matches if it is exactly "*", so it is * equivalent to disabling it. */ use_pattern = !(pat[0] == '*' && patlen == 1); i += 2; } else { addReply(c,shared.syntaxerr); goto cleanup; } } /* Step 2: Iterate the collection. * * Note that if the object is encoded with a ziplist, intset, or any other * representation that is not a hash table, we are sure that it is also * composed of a small number of elements. So to avoid taking state we * just return everything inside the object in a single call, setting the * cursor to zero to signal the end of the iteration. */ /* Handle the case of a hash table. */ ht = NULL; if (o == NULL) { ht = c->db->dict; } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) { ht = o->ptr; } else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) { ht = o->ptr; count *= 2; /* We return key / value for this type. */ } else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) { zset *zs = o->ptr; ht = zs->dict; count *= 2; /* We return key / value for this type. */ } if (ht) { void *privdata[2]; /* We set the max number of iterations to ten times the specified * COUNT, so if the hash table is in a pathological state (very * sparsely populated) we avoid to block too much time at the cost * of returning no or very few elements. */ long maxiterations = count*10; /* We pass two pointers to the callback: the list to which it will * add new elements, and the object containing the dictionary so that * it is possible to fetch more data in a type-dependent way. */ privdata[0] = keys; privdata[1] = o; do { cursor = dictScan(ht, cursor, scanCallback, privdata); } while (cursor && maxiterations-- && listLength(keys) < (unsigned long)count); } else if (o->type == OBJ_SET) { int pos = 0; int64_t ll; while(intsetGet(o->ptr,pos++,&ll)) listAddNodeTail(keys,createStringObjectFromLongLong(ll)); cursor = 0; } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) { unsigned char *p = ziplistIndex(o->ptr,0); unsigned char *vstr; unsigned int vlen; long long vll; while(p) { ziplistGet(p,&vstr,&vlen,&vll); listAddNodeTail(keys, (vstr != NULL) ? createStringObject((char*)vstr,vlen) : createStringObjectFromLongLong(vll)); p = ziplistNext(o->ptr,p); } cursor = 0; } else { serverPanic("Not handled encoding in SCAN."); } /* Step 3: Filter elements. */ node = listFirst(keys); while (node) { robj *kobj = listNodeValue(node); nextnode = listNextNode(node); int filter = 0; /* Filter element if it does not match the pattern. */ if (!filter && use_pattern) { if (sdsEncodedObject(kobj)) { if (!stringmatchlen(pat, patlen, kobj->ptr, sdslen(kobj->ptr), 0)) filter = 1; } else { char buf[LONG_STR_SIZE]; int len; serverAssert(kobj->encoding == OBJ_ENCODING_INT); len = ll2string(buf,sizeof(buf),(long)kobj->ptr); if (!stringmatchlen(pat, patlen, buf, len, 0)) filter = 1; } } /* Filter element if it is an expired key. */ if (!filter && o == NULL && expireIfNeeded(c->db, kobj)) filter = 1; /* Remove the element and its associted value if needed. */ if (filter) { decrRefCount(kobj); listDelNode(keys, node); } /* If this is a hash or a sorted set, we have a flat list of * key-value elements, so if this element was filtered, remove the * value, or skip it if it was not filtered: we only match keys. */ if (o && (o->type == OBJ_ZSET || o->type == OBJ_HASH)) { node = nextnode; nextnode = listNextNode(node); if (filter) { kobj = listNodeValue(node); decrRefCount(kobj); listDelNode(keys, node); } } node = nextnode; } /* Step 4: Reply to the client. */ addReplyMultiBulkLen(c, 2); addReplyBulkLongLong(c,cursor); addReplyMultiBulkLen(c, listLength(keys)); while ((node = listFirst(keys)) != NULL) { robj *kobj = listNodeValue(node); addReplyBulk(c, kobj); decrRefCount(kobj); listDelNode(keys, node); } cleanup: listSetFreeMethod(keys,decrRefCountVoid); listRelease(keys); }
/* Initialize the slow log. This function should be called a single time * at server startup. */ void slowlogInit(void) { server.slowlog = listCreate(); server.slowlog_entry_id = 0; listSetFreeMethod(server.slowlog,slowlogFreeEntry); }
int main(int argc, const char **argv) { int i; char *data, *cmd; int len; client c; srandom(time(NULL)); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); config.numclients = 50; config.requests = 100000; config.liveclients = 0; config.el = aeCreateEventLoop(1024*10); aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL); config.keepalive = 1; config.datasize = 3; config.pipeline = 1; config.randomkeys = 0; config.randomkeys_keyspacelen = 0; config.quiet = 0; config.csv = 0; config.loop = 0; config.idlemode = 0; config.latency = NULL; config.clients = listCreate(); config.hostip = "127.0.0.1"; config.hostport = 6379; config.hostsocket = NULL; config.tests = NULL; config.dbnum = 0; config.auth = NULL; i = parseOptions(argc,argv); argc -= i; argv += i; config.latency = zmalloc(sizeof(long long)*config.requests); if (config.keepalive == 0) { printf("WARNING: keepalive disabled, you probably need 'echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse' for Linux and 'sudo sysctl -w net.inet.tcp.msl=1000' for Mac OS X in order to use a lot of clients/requests\n"); } if (config.idlemode) { printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.numclients); c = createClient("",0,NULL); /* will never receive a reply */ createMissingClients(c); aeMain(config.el); /* and will wait for every */ } /* Run benchmark with command in the remainder of the arguments. */ if (argc) { sds title = sdsnew(argv[0]); for (i = 1; i < argc; i++) { title = sdscatlen(title, " ", 1); title = sdscatlen(title, (char*)argv[i], strlen(argv[i])); } do { len = redisFormatCommandArgv(&cmd,argc,argv,NULL); benchmark(title,cmd,len); free(cmd); } while(config.loop); return 0; } /* Run default benchmark suite. */ data = zmalloc(config.datasize+1); do { memset(data,'x',config.datasize); data[config.datasize] = '\0'; if (test_is_selected("ping_inline") || test_is_selected("ping")) benchmark("PING_INLINE","PING\r\n",6); if (test_is_selected("ping_mbulk") || test_is_selected("ping")) { len = redisFormatCommand(&cmd,"PING"); benchmark("PING_BULK",cmd,len); free(cmd); } if (test_is_selected("set")) { len = redisFormatCommand(&cmd,"SET key:__rand_int__ %s",data); benchmark("SET",cmd,len); free(cmd); } if (test_is_selected("get")) { len = redisFormatCommand(&cmd,"GET key:__rand_int__"); benchmark("GET",cmd,len); free(cmd); } if (test_is_selected("incr")) { len = redisFormatCommand(&cmd,"INCR counter:__rand_int__"); benchmark("INCR",cmd,len); free(cmd); } if (test_is_selected("lpush")) { len = redisFormatCommand(&cmd,"LPUSH mylist %s",data); benchmark("LPUSH",cmd,len); free(cmd); } if (test_is_selected("lpop")) { len = redisFormatCommand(&cmd,"LPOP mylist"); benchmark("LPOP",cmd,len); free(cmd); } if (test_is_selected("sadd")) { len = redisFormatCommand(&cmd, "SADD myset element:__rand_int__"); benchmark("SADD",cmd,len); free(cmd); } if (test_is_selected("spop")) { len = redisFormatCommand(&cmd,"SPOP myset"); benchmark("SPOP",cmd,len); free(cmd); } if (test_is_selected("lrange") || test_is_selected("lrange_100") || test_is_selected("lrange_300") || test_is_selected("lrange_500") || test_is_selected("lrange_600")) { len = redisFormatCommand(&cmd,"LPUSH mylist %s",data); benchmark("LPUSH (needed to benchmark LRANGE)",cmd,len); free(cmd); } if (test_is_selected("lrange") || test_is_selected("lrange_100")) { len = redisFormatCommand(&cmd,"LRANGE mylist 0 99"); benchmark("LRANGE_100 (first 100 elements)",cmd,len); free(cmd); } if (test_is_selected("lrange") || test_is_selected("lrange_300")) { len = redisFormatCommand(&cmd,"LRANGE mylist 0 299"); benchmark("LRANGE_300 (first 300 elements)",cmd,len); free(cmd); } if (test_is_selected("lrange") || test_is_selected("lrange_500")) { len = redisFormatCommand(&cmd,"LRANGE mylist 0 449"); benchmark("LRANGE_500 (first 450 elements)",cmd,len); free(cmd); } if (test_is_selected("lrange") || test_is_selected("lrange_600")) { len = redisFormatCommand(&cmd,"LRANGE mylist 0 599"); benchmark("LRANGE_600 (first 600 elements)",cmd,len); free(cmd); } if (test_is_selected("mset")) { const char *argv[21]; argv[0] = "MSET"; for (i = 1; i < 21; i += 2) { argv[i] = "key:__rand_int__"; argv[i+1] = data; } len = redisFormatCommandArgv(&cmd,21,argv,NULL); benchmark("MSET (10 keys)",cmd,len); free(cmd); } if (!config.csv) printf("\n"); } while(config.loop); return 0; }
int main(void) { const int N = 10; int i, isFound, action, pos, arg; List list; Iterator it1, it2; listCreate(&list, N); do { printf("Меню:\n"); printf("1) Вставить элемент\n"); printf("2) Удалить элемент\n"); printf("3) Печать списка\n"); printf("4) Подсчет длины списка\n"); printf("5) Выполнить задание над списком\n"); printf("6) Выход\n"); printf("Выберите действие: "); scanf("%d", &action); switch (action) { case 1: { printf("Введите позицию элемента: "); scanf("%d", &pos); printf("Введите значение элемента (1 - true, 0 - false): "); scanf("%d", &arg); if (arg != 0 && arg != 1) printf("Ошибка. Введено недопустимое значение\n"); else listInsert(&list, pos - 1, arg); break; } case 2: { printf("Введите номер элемента: "); scanf("%d", &pos); listRemove(&list, pos - 1); break; } case 3: { if (listEmpty(&list)) printf("Список пуст\n"); else listPrint(&list); break; } case 4: { printf("Длина списка: %d\n", listSize(&list)); break; } case 5: { it1 = itFirst(&list); it2 = it1; itNext(&it2); for (i = 0; i < listSize(&list) / 2; i++) { listSwapElems(&it1, &it2); itNext(&it1); itNext(&it1); itNext(&it2); itNext(&it2); } break; } case 6: break; default: { printf("Ошибка. Такого пункта меню не существует\n"); break; } } } while (action != 6); listDestroy(&list); return 0; }