/* Write the buffer (possibly composed of multiple blocks) into the specified * fd. If a short write or any other error happens -1 is returned, * otherwise the number of bytes written is returned. */ ssize_t aofRewriteBufferWrite(int fd) { listNode *ln; listIter li; ssize_t count = 0; listRewind(server.aof_rewrite_buf_blocks,&li); while((ln = listNext(&li))) { aofrwblock *block = listNodeValue(ln); ssize_t nwritten; if (block->used) { nwritten = write(fd,block->buf,block->used); if (nwritten != block->used) { if (nwritten == 0) errno = EIO; return -1; } count += nwritten; } } return count; }
//监视机制触发见touchWatchedKey决定是否触发REDIS_DIRTY_CAS 取消事物函数更具watch的键是否有触发REDIS_DIRTY_CAS来决定是否继续执行事物中的命令,见execCommand //取消watch见unwatchAllKeys void touchWatchedKey(redisDb *db, robj *key) { list *clients; listIter li; listNode *ln; // 字典为空,没有任何键被监视 if (dictSize(db->watched_keys) == 0) return; // 获取所有监视这个键的客户端 clients = dictFetchValue(db->watched_keys, key); if (!clients) return; /* Mark all the clients watching this key as REDIS_DIRTY_CAS */ /* Check if we are already watching for this key */ // 遍历所有客户端,打开他们的 REDIS_DIRTY_CAS 标识 listRewind(clients,&li); while((ln = listNext(&li))) { redisClient *c = listNodeValue(ln); c->flags |= REDIS_DIRTY_CAS; } }
// WATCH 某个 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 */ // 所有被 WATCHED 的 KEY 都被放在 redisClient.watched_keys 链表中 // 遍历这个链表,查看这个 KEY 是否已经处于监视状态(WATCHED) 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 */ } /* This key is not already watched in this DB. Let's add it */ // 如果 KEY 还没有被 WATCH 过,那么对它进行 WATCH clients = dictFetchValue(c->db->watched_keys,key); if (!clients) { // 如果 clients 链表不存在 // 说明这个客户端是第一个监视这个 DB 的这个 KEY 的客户端 // 那么 clients 创建链表,并将它添加到 c->db->watched_keys 字典中 clients = listCreate(); dictAdd(c->db->watched_keys,key,clients); incrRefCount(key); } // 将客户端添加到 clients 链表 listAddNodeTail(clients,c); /* Add the new key to the lits of keys watched by this client */ // 除了 c->db->watched_keys 之外 // 还要将被 WATCH 的 KEY 添加到 c->watched_keys wk = zmalloc(sizeof(*wk)); wk->key = key; wk->db = c->db; incrRefCount(key); listAddNodeTail(c->watched_keys,wk); }
void _msg_dump(const char *file, int line, struct msg *msg, int level, int begin) { struct mbuf *mbuf; listIter *iter; listNode *node; uint8_t *p, *q; long int len; if (log_loggable(level) == 0) { return; } _log(file, line, 0, "msg dump id %"PRIu64" request %d len %"PRIu32" type %d " "(err %d) kind %d result %d mbuf_count %u keys_count %u", msg->id, msg->request, msg->mlen, msg->type, msg->err, msg->kind, msg->result, listLength(msg->data), msg->keys == NULL?0:array_n(msg->keys)); iter = listGetIterator(msg->data, AL_START_HEAD); ASSERT(iter != NULL); while((node = listNext(iter)) != NULL) { mbuf = listNodeValue(node); if (begin) { p = mbuf->start; } else { p = mbuf->pos; } q = mbuf->last; len = q - p; _log(file, line, 0, "mbuf [%p] with %ld bytes of data, pos %p last %p", p, len, mbuf->pos, mbuf->last); _log_hexdump(file, line, p, len, NULL); } listReleaseIterator(iter); }
void lindexCommand(redisClient *c) { robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk); if (o == NULL || checkType(c,o,REDIS_LIST)) return; long index; robj *value = NULL; if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != REDIS_OK)) return; if (o->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *p; unsigned char *vstr; unsigned int vlen; long long vlong; p = ziplistIndex(o->ptr,index); if (ziplistGet(p,&vstr,&vlen,&vlong)) { if (vstr) { value = createStringObject((char*)vstr,vlen); } else { value = createStringObjectFromLongLong(vlong); } addReplyBulk(c,value); decrRefCount(value); } else { addReply(c,shared.nullbulk); } } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { listNode *ln = listIndex(o->ptr,index); if (ln != NULL) { value = listNodeValue(ln); addReplyBulk(c,value); } else { addReply(c,shared.nullbulk); } } else { redisPanic("Unknown list encoding"); } }
/* * 取消所有该客户端监视的 key * 对事务状态的清除由调用者执行 * * T = O(N^2) */ void unwatchAllKeys(redisClient *c) { listIter li; listNode *ln; // 没有键被 watch ,直接返回 if (listLength(c->watched_keys) == 0) return; // 从客户端以及 DB 中删除所有监视 key 和客户端的资料 // O(N^2) listRewind(c->watched_keys,&li); while((ln = listNext(&li))) { list *clients; watchedKey *wk; /* Lookup the watched key -> clients list and remove the client * from the list */ // 取出 watchedKey 结构 wk = listNodeValue(ln); // 删除 db 中的客户端信息, O(1) clients = dictFetchValue(wk->db->watched_keys, wk->key); redisAssertWithInfo(c,NULL,clients != NULL); // O(N) listDelNode(clients,listSearchKey(clients,c)); /* Kill the entry at all if this was the only client */ if (listLength(clients) == 0) dictDelete(wk->db->watched_keys, wk->key); /* Remove this watched key from the client->watched list */ // 将 key 从客户端的监视列表中删除, O(1) listDelNode(c->watched_keys,ln); decrRefCount(wk->key); zfree(wk); } }
/* result buffer aggregation is taken from scripting.c */ sds fakeClientResultBuffer(redisClient *c) { sds reply; if (listLength(c->reply) == 0 && c->bufpos < REDIS_REPLY_CHUNK_BYTES) { /* This is a fast path for the common case of a reply inside the * client static buffer. Don't create an SDS string but just use * the client buffer directly. */ c->buf[c->bufpos] = '\0'; reply = c->buf; c->bufpos = 0; } else { reply = sdsnewlen(c->buf, c->bufpos); c->bufpos = 0; while (listLength(c->reply)) { robj *o = listNodeValue(listFirst(c->reply)); reply = sdscatlen(reply, o->ptr, sdslen(o->ptr)); listDelNode(c->reply, listFirst(c->reply)); } } return reply; }
/* Unwatch all the keys watched by this client. To clean the EXEC dirty * flag is up to the caller. * * 取消客户端对所有键的监视。 * * 清除客户端事务状态的任务由调用者执行。 */ void unwatchAllKeys(redisClient *c) { listIter li; listNode *ln; // 没有键被监视,直接返回 if (listLength(c->watched_keys) == 0) return; // 遍历链表中所有被客户端监视的键 listRewind(c->watched_keys,&li); while((ln = listNext(&li))) { list *clients; watchedKey *wk; /* Lookup the watched key -> clients list and remove the client * from the list */ // 从数据库的 watched_keys 字典的 key 键中 // 删除链表里包含的客户端节点 wk = listNodeValue(ln); // 取出客户端链表 clients = dictFetchValue(wk->db->watched_keys, wk->key); redisAssertWithInfo(c, NULL, clients != NULL); // 删除链表中的客户端节点 listDelNode(clients,listSearchKey(clients,c)); /* Kill the entry at all if this was the only client */ // 如果链表已经被清空,那么删除这个键 if (listLength(clients) == 0) dictDelete(wk->db->watched_keys, wk->key); /* Remove this watched key from the client->watched list */ // 从链表中移除 key 节点 listDelNode(c->watched_keys,ln); decrRefCount(wk->key); zfree(wk); } }
struct client *createClient(int fd, char *ip, int port, struct protocol *p) { struct client *c; struct listNode *node; node = listFirst(FreeClients); if (node == NULL) { c = wmalloc(sizeof(*c)); } else { c = listNodeValue(node); removeListNode(FreeClients, node); appendToListTail(Clients, c); } if (c == NULL) return NULL; c->clifd = fd; c->ip = wstrNew(ip); c->port = port; c->protocol = p; c->conns = createList(); listSetFree(c->conns, (void (*)(void*))connDealloc); c->req_buf = msgCreate(Server.mbuf_size); c->is_outer = 1; c->should_close = 0; c->valid = 1; c->pending = NULL; c->client_data = NULL; c->notify = NULL; c->last_io = Server.cron_time; c->name = wstrEmpty(); createEvent(WorkerProcess->center, c->clifd, EVENT_READABLE, handleRequest, c); getStatVal(StatTotalClient)++; return c; }
ssize_t aofRewriteBufferWrite(int fd){ /*write cotents in AOF rewrite buffer blocks into given file*/ /*return -1 for short count or write error, else return bytes written*/ listIter *li; listNode *ln; aofrwblock *block; int nwritten, nbytes=0; li = listGetIterator(server.aof_rewrite_buf_blocks, AL_START_HEAD); while((ln = listNext(li)) != NULL){ block = listNodeValue(ln); if((nwritten = write(fd, block->buf, block->used)) == -1){ return -1; } else if((nwritten == 0) && (block.used != 0)){ errno = EIO; return 0; } nbytes += nwritten; } listReleaseIterator(li); return nbytes; }
/* Watch for the specified 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 */ 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 */ } // 获取监视该键值的客户端链表 /* This key is not already watched in this DB. Let's add it */ clients = dictFetchValue(c->db->watched_keys,key); // 如果不存在链表,需要新建一个 if (!clients) { clients = listCreate(); dictAdd(c->db->watched_keys,key,clients); incrRefCount(key); } // 尾插法。将客户端添加到链表尾部 listAddNodeTail(clients,c); // 将监视键添加到 redisClient.watched_keys 的尾部 /* Add the new key to the list of keys watched by this client */ wk = zmalloc(sizeof(*wk)); wk->key = key; wk->db = c->db; incrRefCount(key); listAddNodeTail(c->watched_keys,wk); }
/* Return entry or NULL at the current position of the iterator. */ robj *listTypeGet(listTypeEntry *entry) { listTypeIterator *li = entry->li; robj *value = NULL; if (li->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *vstr; unsigned int vlen; long long vlong; redisAssert(entry->zi != NULL); if (ziplistGet(entry->zi,&vstr,&vlen,&vlong)) { if (vstr) { value = createStringObject((char*)vstr,vlen); } else { value = createStringObjectFromLongLong(vlong); } } } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) { redisAssert(entry->ln != NULL); value = listNodeValue(entry->ln); incrRefCount(value); } else { redisPanic("Unknown list encoding"); } return value; }
// 让client监视所有的指定的key void watchForKey(client *c, robj *key) { list *clients = NULL; listIter li; listNode *ln; watchedKey *wk; /* Check if we are already watching for this key */ 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 */ } /* This key is not already watched in this DB. Let's add it */ // 如果数据库中该键没有被client监视则添加它 clients = dictFetchValue(c->db->watched_keys,key); // 没有被client监视 if (!clients) { // 创建一个空链表 clients = listCreate(); // 值是被client监控的key,键是client,添加到数据库的watched_keys字典中 dictAdd(c->db->watched_keys,key,clients); incrRefCount(key); } // 将当前client添加到监视该key的client链表的尾部 listAddNodeTail(clients,c); /* Add the new key to the list of keys watched by this client */ // 将新的被监视的key和与该key关联的数据库加入到客户端的watched_keys中 wk = zmalloc(sizeof(*wk)); wk->key = key; wk->db = c->db; incrRefCount(key); listAddNodeTail(c->watched_keys,wk); }
void _masterProcessFinishedIO() { sds key = NULL; sds content = NULL; /* For each IO worker */ int tid = 0; /* Polling all io thread */ for(tid=0;tid<CCACHE_NUM_BIO_THREADS;tid++) { while(bioGetResult(tid,&key,&content)) { master_numjob++; objSds *value = dictFetchValue(master_cache,key); if(content == NULL) { value->ptr = HTTP_NOT_FOUND->ptr; } else { value->ptr = content; master_total_mem += sdslen(content); } value->state = OBJSDS_OK; listIter li; listNode *ln; cacheEntry *ce; /* notify all cache entries waiting for this key */ listRewind(value->waiting_entries,&li); /* for each waiting ce */ while ((ln = listNext(&li)) != NULL){ /* unwatch client */ ce = listNodeValue(ln); ce->val = value->ptr; /* notify all clients waiting for this entry */ cacheSendMessage(ce->mycache,ce,CACHE_REPLY_NEW); printf("Cache in Worker %.2lf \n", (double)(clock())); } } } }
robj *listTypePop(robj *subject, int where) { robj *value = NULL; if (subject->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *p; unsigned char *vstr; unsigned int vlen; long long vlong; int pos = (where == REDIS_HEAD) ? 0 : -1; p = ziplistIndex(subject->ptr,pos); if (ziplistGet(p,&vstr,&vlen,&vlong)) { if (vstr) { value = createStringObject((char*)vstr,vlen); } else { value = createStringObjectFromLongLong(vlong); } /* We only need to delete an element when it exists */ subject->ptr = ziplistDelete(subject->ptr,&p); } } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = subject->ptr; listNode *ln; if (where == REDIS_HEAD) { ln = listFirst(list); } else { ln = listLast(list); } if (ln != NULL) { value = listNodeValue(ln); incrRefCount(value); listDelNode(list,ln); } } else { redisPanic("Unknown list encoding"); } return value; }
/* 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 sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int mask) { redisClient *c = privdata; int nwritten = 0, totwritten = 0, objlen, willwrite; robj *o; struct iovec iov[REDIS_WRITEV_IOVEC_COUNT]; int offset, ion = 0; REDIS_NOTUSED(el); REDIS_NOTUSED(mask); listNode *node; while (listLength(c->reply)) { offset = c->sentlen; ion = 0; willwrite = 0; /* fill-in the iov[] array */ for(node = listFirst(c->reply); node; node = listNextNode(node)) { o = listNodeValue(node); objlen = sdslen(o->ptr); if (totwritten + objlen - offset > REDIS_MAX_WRITE_PER_EVENT) break; if(ion == REDIS_WRITEV_IOVEC_COUNT) break; /* no more iovecs */ iov[ion].iov_base = ((char*)o->ptr) + offset; iov[ion].iov_len = objlen - offset; willwrite += objlen - offset; offset = 0; /* just for the first item */ ion++; } if(willwrite == 0) break; /* write all collected blocks at once */ if((nwritten = writev(fd, iov, ion)) < 0) { if (errno != EAGAIN) { redisLog(REDIS_VERBOSE, "Error writing to client: %s", strerror(errno)); freeClient(c); return; } break; } totwritten += nwritten; offset = c->sentlen; /* remove written robjs from c->reply */ while (nwritten && listLength(c->reply)) { o = listNodeValue(listFirst(c->reply)); objlen = sdslen(o->ptr); if(nwritten >= objlen - offset) { listDelNode(c->reply, listFirst(c->reply)); nwritten -= objlen - offset; c->sentlen = 0; } else { /* partial write */ c->sentlen += nwritten; break; } offset = 0; } } if (totwritten > 0) c->lastinteraction = time(NULL); if (listLength(c->reply) == 0) { c->sentlen = 0; aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE); } }
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) { redisClient *c = privdata; int nwritten = 0, totwritten = 0, objlen; robj *o; REDIS_NOTUSED(el); REDIS_NOTUSED(mask); /* Use writev() if we have enough buffers to send */ if (!server.glueoutputbuf && listLength(c->reply) > REDIS_WRITEV_THRESHOLD && !(c->flags & REDIS_MASTER)) { sendReplyToClientWritev(el, fd, privdata, mask); return; } while(listLength(c->reply)) { if (server.glueoutputbuf && listLength(c->reply) > 1) glueReplyBuffersIfNeeded(c); o = listNodeValue(listFirst(c->reply)); objlen = sdslen(o->ptr); if (objlen == 0) { listDelNode(c->reply,listFirst(c->reply)); continue; } if (c->flags & REDIS_MASTER) { /* Don't reply to a master */ nwritten = objlen - c->sentlen; } else { nwritten = write(fd, ((char*)o->ptr)+c->sentlen, objlen - c->sentlen); if (nwritten <= 0) break; } c->sentlen += nwritten; totwritten += nwritten; /* If we fully sent the object on head go to the next one */ if (c->sentlen == objlen) { listDelNode(c->reply,listFirst(c->reply)); c->sentlen = 0; } /* Note that we avoid to send more thank REDIS_MAX_WRITE_PER_EVENT * bytes, in a single threaded server it's a good idea to serve * other clients as well, even if a very large request comes from * super fast link that is always able to accept data (in real world * scenario think about 'KEYS *' against the loopback interfae) */ if (totwritten > REDIS_MAX_WRITE_PER_EVENT) break; } if (nwritten == -1) { if (errno == EAGAIN) { nwritten = 0; } else { redisLog(REDIS_VERBOSE, "Error writing to client: %s", strerror(errno)); freeClient(c); return; } } if (totwritten > 0) c->lastinteraction = time(NULL); if (listLength(c->reply) == 0) { c->sentlen = 0; aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE); } }
if (c->bufpos == 0 && listLength(c->reply) == 0 && (c->replstate == REDIS_REPL_NONE || c->replstate == REDIS_REPL_ONLINE) && aeCreateFileEvent(server.el, c->fd, AE_WRITABLE, sendReplyToClient, c) == AE_ERR) return REDIS_ERR; return REDIS_OK; } /* Create a duplicate of the last object in the reply list when * it is not exclusively owned by the reply list. */ robj *dupLastObjectIfNeeded(list *reply) { robj *new, *cur; listNode *ln; redisAssert(listLength(reply) > 0); ln = listLast(reply); cur = listNodeValue(ln); if (cur->refcount > 1) { new = dupStringObject(cur); decrRefCount(cur); listNodeValue(ln) = new; } return listNodeValue(ln); } /* ----------------------------------------------------------------------------- * Low level functions to add more data to output buffers. * -------------------------------------------------------------------------- */ int _addReplyToBuffer(redisClient *c, char *s, size_t len) { size_t available = sizeof(c->buf)-c->bufpos;
/* 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); }
/* Write a sequence of commands able to fully rebuild the dataset into * "filename". Used both by REWRITEAOF and BGREWRITEAOF. */ int rewriteAppendOnlyFile(char *filename) { dictIterator *di = NULL; dictEntry *de; FILE *fp; char tmpfile[256]; int j; time_t now = time(NULL); /* Note that we have to use a different temp name here compared to the * one used by rewriteAppendOnlyFileBackground() function. */ snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid()); fp = fopen(tmpfile,"w"); if (!fp) { redisLog(REDIS_WARNING, "Failed rewriting the append only file: %s", strerror(errno)); return REDIS_ERR; } for (j = 0; j < server.dbnum; j++) { char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n"; redisDb *db = server.db+j; dict *d = db->dict; if (dictSize(d) == 0) continue; di = dictGetSafeIterator(d); if (!di) { fclose(fp); return REDIS_ERR; } /* SELECT the new DB */ if (fwrite(selectcmd,sizeof(selectcmd)-1,1,fp) == 0) goto werr; if (fwriteBulkLongLong(fp,j) == 0) goto werr; /* Iterate this DB writing every entry */ while((de = dictNext(di)) != NULL) { sds keystr = dictGetEntryKey(de); robj key, *o; time_t expiretime; int swapped; keystr = dictGetEntryKey(de); o = dictGetEntryVal(de); initStaticStringObject(key,keystr); /* If the value for this key is swapped, load a preview in memory. * We use a "swapped" flag to remember if we need to free the * value object instead to just increment the ref count anyway * in order to avoid copy-on-write of pages if we are forked() */ if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY || o->storage == REDIS_VM_SWAPPING) { swapped = 0; } else { o = vmPreviewObject(o); swapped = 1; } expiretime = getExpire(db,&key); /* Save the key and associated value */ if (o->type == REDIS_STRING) { /* Emit a SET command */ char cmd[]="*3\r\n$3\r\nSET\r\n"; if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; /* Key and value */ if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkObject(fp,o) == 0) goto werr; } else if (o->type == REDIS_LIST) { /* Emit the RPUSHes needed to rebuild the list */ char cmd[]="*3\r\n$5\r\nRPUSH\r\n"; if (o->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *zl = o->ptr; unsigned char *p = ziplistIndex(zl,0); unsigned char *vstr; unsigned int vlen; long long vlong; while(ziplistGet(p,&vstr,&vlen,&vlong)) { if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (vstr) { if (fwriteBulkString(fp,(char*)vstr,vlen) == 0) goto werr; } else { if (fwriteBulkLongLong(fp,vlong) == 0) goto werr; } p = ziplistNext(zl,p); } } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = o->ptr; listNode *ln; listIter li; listRewind(list,&li); while((ln = listNext(&li))) { robj *eleobj = listNodeValue(ln); if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkObject(fp,eleobj) == 0) goto werr; } } else { redisPanic("Unknown list encoding"); } } else if (o->type == REDIS_SET) { char cmd[]="*3\r\n$4\r\nSADD\r\n"; /* Emit the SADDs needed to rebuild the set */ if (o->encoding == REDIS_ENCODING_INTSET) { int ii = 0; int64_t llval; while(intsetGet(o->ptr,ii++,&llval)) { if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkLongLong(fp,llval) == 0) goto werr; } } else if (o->encoding == REDIS_ENCODING_HT) { dictIterator *di = dictGetIterator(o->ptr); dictEntry *de; while((de = dictNext(di)) != NULL) { robj *eleobj = dictGetEntryKey(de); if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkObject(fp,eleobj) == 0) goto werr; } dictReleaseIterator(di); } else { redisPanic("Unknown set encoding"); } } else if (o->type == REDIS_ZSET) { /* Emit the ZADDs needed to rebuild the sorted set */ char cmd[]="*4\r\n$4\r\nZADD\r\n"; if (o->encoding == REDIS_ENCODING_ZIPLIST) { unsigned char *zl = o->ptr; unsigned char *eptr, *sptr; unsigned char *vstr; unsigned int vlen; long long vll; double score; eptr = ziplistIndex(zl,0); redisAssert(eptr != NULL); sptr = ziplistNext(zl,eptr); redisAssert(sptr != NULL); while (eptr != NULL) { redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll)); score = zzlGetScore(sptr); if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkDouble(fp,score) == 0) goto werr; if (vstr != NULL) { if (fwriteBulkString(fp,(char*)vstr,vlen) == 0) goto werr; } else { if (fwriteBulkLongLong(fp,vll) == 0) goto werr; } zzlNext(zl,&eptr,&sptr); } } else if (o->encoding == REDIS_ENCODING_SKIPLIST) { zset *zs = o->ptr; dictIterator *di = dictGetIterator(zs->dict); dictEntry *de; while((de = dictNext(di)) != NULL) { robj *eleobj = dictGetEntryKey(de); double *score = dictGetEntryVal(de); if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkDouble(fp,*score) == 0) goto werr; if (fwriteBulkObject(fp,eleobj) == 0) goto werr; } dictReleaseIterator(di); } else { redisPanic("Unknown sorted set encoding"); } } else if (o->type == REDIS_HASH) { char cmd[]="*4\r\n$4\r\nHSET\r\n"; /* Emit the HSETs needed to rebuild the hash */ if (o->encoding == REDIS_ENCODING_ZIPMAP) { unsigned char *p = zipmapRewind(o->ptr); unsigned char *field, *val; unsigned int flen, vlen; while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) { if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkString(fp,(char*)field,flen) == 0) goto werr; if (fwriteBulkString(fp,(char*)val,vlen) == 0) goto werr; } } else { dictIterator *di = dictGetIterator(o->ptr); dictEntry *de; while((de = dictNext(di)) != NULL) { robj *field = dictGetEntryKey(de); robj *val = dictGetEntryVal(de); if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkObject(fp,field) == 0) goto werr; if (fwriteBulkObject(fp,val) == 0) goto werr; } dictReleaseIterator(di); } } else { redisPanic("Unknown object type"); } /* Save the expire time */ if (expiretime != -1) { char cmd[]="*3\r\n$8\r\nEXPIREAT\r\n"; /* If this key is already expired skip it */ if (expiretime < now) continue; if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; if (fwriteBulkObject(fp,&key) == 0) goto werr; if (fwriteBulkLongLong(fp,expiretime) == 0) goto werr; } if (swapped) decrRefCount(o); } dictReleaseIterator(di); } /* Make sure data will not remain on the OS's output buffers */ fflush(fp); aof_fsync(fileno(fp)); fclose(fp); /* Use RENAME to make sure the DB file is changed atomically only * if the generate DB file is ok. */ if (rename(tmpfile,filename) == -1) { redisLog(REDIS_WARNING,"Error moving temp append only file on the final destination: %s", strerror(errno)); unlink(tmpfile); return REDIS_ERR; } redisLog(REDIS_NOTICE,"SYNC append only file rewrite performed"); return REDIS_OK; werr: fclose(fp); unlink(tmpfile); redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno)); if (di) dictReleaseIterator(di); return REDIS_ERR; }
int luaRedisGenericCommand(lua_State *lua, int raise_error) { int j, argc = lua_gettop(lua); struct redisCommand *cmd; redisClient *c = server.lua_client; sds reply; /* Cached across calls. */ static robj **argv = NULL; static int argv_size = 0; static robj *cached_objects[LUA_CMD_OBJCACHE_SIZE]; static size_t cached_objects_len[LUA_CMD_OBJCACHE_SIZE]; static int inuse = 0; /* Recursive calls detection. */ /* By using Lua debug hooks it is possible to trigger a recursive call * to luaRedisGenericCommand(), which normally should never happen. * To make this function reentrant is futile and makes it slower, but * we should at least detect such a misuse, and abort. */ if (inuse) { char *recursion_warning = "luaRedisGenericCommand() recursive call detected. " "Are you doing funny stuff with Lua debug hooks?"; redisLog(REDIS_WARNING,"%s",recursion_warning); luaPushError(lua,recursion_warning); return 1; } inuse++; /* Require at least one argument */ if (argc == 0) { luaPushError(lua, "Please specify at least one argument for redis.call()"); inuse--; return 1; } /* Build the arguments vector */ if (argv_size < argc) { argv = zrealloc(argv,sizeof(robj*)*argc); argv_size = argc; } for (j = 0; j < argc; j++) { char *obj_s; size_t obj_len; char dbuf[64]; if (lua_type(lua,j+1) == LUA_TNUMBER) { /* We can't use lua_tolstring() for number -> string conversion * since Lua uses a format specifier that loses precision. */ lua_Number num = lua_tonumber(lua,j+1); obj_len = snprintf(dbuf,sizeof(dbuf),"%.17g",(double)num); obj_s = dbuf; } else { obj_s = (char*)lua_tolstring(lua,j+1,&obj_len); if (obj_s == NULL) break; /* Not a string. */ } /* Try to use a cached object. */ if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] && cached_objects_len[j] >= obj_len) { char *s = cached_objects[j]->ptr; struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); argv[j] = cached_objects[j]; cached_objects[j] = NULL; memcpy(s,obj_s,obj_len+1); sh->free += sh->len - obj_len; sh->len = obj_len; } else { argv[j] = createStringObject(obj_s, obj_len); } } /* Check if one of the arguments passed by the Lua script * is not a string or an integer (lua_isstring() return true for * integers as well). */ if (j != argc) { j--; while (j >= 0) { decrRefCount(argv[j]); j--; } luaPushError(lua, "Lua redis() command arguments must be strings or integers"); inuse--; return 1; } /* Setup our fake client for command execution */ c->argv = argv; c->argc = argc; /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity))) { if (cmd) luaPushError(lua, "Wrong number of args calling Redis command From Lua script"); else luaPushError(lua,"Unknown Redis command called from Lua script"); goto cleanup; } c->cmd = cmd; /* There are commands that are not allowed inside scripts. */ if (cmd->flags & REDIS_CMD_NOSCRIPT) { luaPushError(lua, "This Redis command is not allowed from scripts"); goto cleanup; } /* Write commands are forbidden against read-only slaves, or if a * command marked as non-deterministic was already called in the context * of this script. */ if (cmd->flags & REDIS_CMD_WRITE) { if (server.lua_random_dirty) { luaPushError(lua, "Write commands not allowed after non deterministic commands"); goto cleanup; } else if (server.masterhost && server.repl_slave_ro && !server.loading && !(server.lua_caller->flags & REDIS_MASTER)) { luaPushError(lua, shared.roslaveerr->ptr); goto cleanup; } else if (server.stop_writes_on_bgsave_err && server.saveparamslen > 0 && server.lastbgsave_status == REDIS_ERR) { luaPushError(lua, shared.bgsaveerr->ptr); goto cleanup; } } /* If we reached the memory limit configured via maxmemory, commands that * could enlarge the memory usage are not allowed, but only if this is the * first write in the context of this script, otherwise we can't stop * in the middle. */ if (server.maxmemory && server.lua_write_dirty == 0 && (cmd->flags & REDIS_CMD_DENYOOM)) { if (freeMemoryIfNeeded() == REDIS_ERR) { luaPushError(lua, shared.oomerr->ptr); goto cleanup; } } if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1; if (cmd->flags & REDIS_CMD_WRITE) server.lua_write_dirty = 1; /* If this is a Redis Cluster node, we need to make sure Lua is not * trying to access non-local keys, with the exception of commands * received from our master. */ if (server.cluster_enabled && !(server.lua_caller->flags & REDIS_MASTER)) { /* Duplicate relevant flags in the lua client. */ c->flags &= ~(REDIS_READONLY|REDIS_ASKING); c->flags |= server.lua_caller->flags & (REDIS_READONLY|REDIS_ASKING); if (getNodeByQuery(c,c->cmd,c->argv,c->argc,NULL,NULL) != server.cluster->myself) { luaPushError(lua, "Lua script attempted to access a non local key in a " "cluster node"); goto cleanup; } } /* Run the command */ call(c,REDIS_CALL_SLOWLOG | REDIS_CALL_STATS); /* Convert the result of the Redis command into a suitable Lua type. * The first thing we need is to create a single string from the client * output buffers. */ if (listLength(c->reply) == 0 && c->bufpos < REDIS_REPLY_CHUNK_BYTES) { /* This is a fast path for the common case of a reply inside the * client static buffer. Don't create an SDS string but just use * the client buffer directly. */ c->buf[c->bufpos] = '\0'; reply = c->buf; c->bufpos = 0; } else { reply = sdsnewlen(c->buf,c->bufpos); c->bufpos = 0; while(listLength(c->reply)) { robj *o = listNodeValue(listFirst(c->reply)); reply = sdscatlen(reply,o->ptr,sdslen(o->ptr)); listDelNode(c->reply,listFirst(c->reply)); } } if (raise_error && reply[0] != '-') raise_error = 0; redisProtocolToLuaType(lua,reply); /* Sort the output array if needed, assuming it is a non-null multi bulk * reply as expected. */ if ((cmd->flags & REDIS_CMD_SORT_FOR_SCRIPT) && (reply[0] == '*' && reply[1] != '-')) { luaSortArray(lua); } if (reply != c->buf) sdsfree(reply); c->reply_bytes = 0; cleanup: /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ for (j = 0; j < c->argc; j++) { robj *o = c->argv[j]; /* Try to cache the object in the cached_objects array. * The object must be small, SDS-encoded, and with refcount = 1 * (we must be the only owner) for us to cache it. */ if (j < LUA_CMD_OBJCACHE_SIZE && o->refcount == 1 && (o->encoding == REDIS_ENCODING_RAW || o->encoding == REDIS_ENCODING_EMBSTR) && sdslen(o->ptr) <= LUA_CMD_OBJCACHE_MAX_LEN) { struct sdshdr *sh = (void*)(((char*)(o->ptr))-(sizeof(struct sdshdr))); if (cached_objects[j]) decrRefCount(cached_objects[j]); cached_objects[j] = o; cached_objects_len[j] = sh->free + sh->len; } else { decrRefCount(o); } } if (c->argv != argv) { zfree(c->argv); argv = NULL; argv_size = 0; } if (raise_error) { /* If we are here we should have an error in the stack, in the * form of a table with an "err" field. Extract the string to * return the plain error. */ lua_pushstring(lua,"err"); lua_gettable(lua,-2); inuse--; return lua_error(lua); } inuse--; return 1; }
/* * * 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); }
int luaRedisGenericCommand(lua_State *lua, int raise_error) { int j, argc = lua_gettop(lua); struct redisCommand *cmd; robj **argv; redisClient *c = server.lua_client; sds reply; /* Require at least one argument */ if (argc == 0) { luaPushError(lua, "Please specify at least one argument for redis.call()"); return 1; } /* Build the arguments vector */ argv = zmalloc(sizeof(robj*)*argc); for (j = 0; j < argc; j++) { if (!lua_isstring(lua,j+1)) break; argv[j] = createStringObject((char*)lua_tostring(lua,j+1), lua_strlen(lua,j+1)); } /* Check if one of the arguments passed by the Lua script * is not a string or an integer (lua_isstring() return true for * integers as well). */ if (j != argc) { j--; while (j >= 0) { decrRefCount(argv[j]); j--; } zfree(argv); luaPushError(lua, "Lua redis() command arguments must be strings or integers"); return 1; } /* Setup our fake client for command execution */ c->argv = argv; c->argc = argc; /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity))) { if (cmd) luaPushError(lua, "Wrong number of args calling Redis command From Lua script"); else luaPushError(lua,"Unknown Redis command called from Lua script"); goto cleanup; } /* There are commands that are not allowed inside scripts. */ if (cmd->flags & REDIS_CMD_NOSCRIPT) { luaPushError(lua, "This Redis command is not allowed from scripts"); goto cleanup; } /* Write commands are forbidden against read-only slaves, or if a * command marked as non-deterministic was already called in the context * of this script. */ if (cmd->flags & REDIS_CMD_WRITE) { if (server.lua_random_dirty) { luaPushError(lua, "Write commands not allowed after non deterministic commands"); goto cleanup; } else if (server.masterhost && server.repl_slave_ro && !server.loading && !(server.lua_caller->flags & REDIS_MASTER)) { luaPushError(lua, shared.roslaveerr->ptr); goto cleanup; } else if (server.stop_writes_on_bgsave_err && server.saveparamslen > 0 && server.lastbgsave_status == REDIS_ERR) { luaPushError(lua, shared.bgsaveerr->ptr); goto cleanup; } } /* If we reached the memory limit configured via maxmemory, commands that * could enlarge the memory usage are not allowed, but only if this is the * first write in the context of this script, otherwise we can't stop * in the middle. */ if (server.maxmemory && server.lua_write_dirty == 0 && (cmd->flags & REDIS_CMD_DENYOOM)) { if (freeMemoryIfNeeded() == REDIS_ERR) { luaPushError(lua, shared.oomerr->ptr); goto cleanup; } } if (cmd->flags & REDIS_CMD_RANDOM) server.lua_random_dirty = 1; if (cmd->flags & REDIS_CMD_WRITE) server.lua_write_dirty = 1; /* Run the command */ c->cmd = cmd; call(c,REDIS_CALL_SLOWLOG | REDIS_CALL_STATS); /* Convert the result of the Redis command into a suitable Lua type. * The first thing we need is to create a single string from the client * output buffers. */ reply = sdsempty(); if (c->bufpos) { reply = sdscatlen(reply,c->buf,c->bufpos); c->bufpos = 0; } while(listLength(c->reply)) { robj *o = listNodeValue(listFirst(c->reply)); reply = sdscatlen(reply,o->ptr,sdslen(o->ptr)); listDelNode(c->reply,listFirst(c->reply)); } if (raise_error && reply[0] != '-') raise_error = 0; redisProtocolToLuaType(lua,reply); /* Sort the output array if needed, assuming it is a non-null multi bulk * reply as expected. */ if ((cmd->flags & REDIS_CMD_SORT_FOR_SCRIPT) && (reply[0] == '*' && reply[1] != '-')) { luaSortArray(lua); } sdsfree(reply); c->reply_bytes = 0; cleanup: /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ for (j = 0; j < c->argc; j++) decrRefCount(c->argv[j]); zfree(c->argv); if (raise_error) { /* If we are here we should have an error in the stack, in the * form of a table with an "err" field. Extract the string to * return the plain error. */ lua_pushstring(lua,"err"); lua_gettable(lua,-2); return lua_error(lua); } return 1; }
int freeMemoryIfNeeded(void) { size_t mem_reported, mem_used, mem_tofree, mem_freed; int slaves = listLength(server.slaves); mstime_t latency, eviction_latency; long long delta; /* Check if we are over the memory usage limit. If we are not, no need * to subtract the slaves output buffers. We can just return ASAP. */ mem_reported = zmalloc_used_memory(); if (mem_reported <= server.maxmemory) return C_OK; /* Remove the size of slaves output buffers and AOF buffer from the * count of used memory. */ mem_used = mem_reported; if (slaves) { listIter li; listNode *ln; listRewind(server.slaves,&li); while((ln = listNext(&li))) { client *slave = listNodeValue(ln); unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave); if (obuf_bytes > mem_used) mem_used = 0; else mem_used -= obuf_bytes; } } if (server.aof_state != AOF_OFF) { mem_used -= sdslen(server.aof_buf); mem_used -= aofRewriteBufferSize(); } /* Check if we are still over the memory limit. */ if (mem_used <= server.maxmemory) return C_OK; /* Compute how much memory we need to free. */ mem_tofree = mem_used - server.maxmemory; mem_freed = 0; if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION) goto cant_free; /* We need to free memory, but policy forbids. */ latencyStartMonitor(latency); while (mem_freed < mem_tofree) { int j, k, i, keys_freed = 0; static int next_db = 0; sds bestkey = NULL; int bestdbid; redisDb *db; dict *dict; dictEntry *de; if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == MAXMEMORY_VOLATILE_LRU) { struct evictionPoolEntry *pool = EvictionPoolLRU; while(bestkey == NULL) { unsigned long total_keys = 0, keys; /* We don't want to make local-db choices when expiring keys, * so to start populate the eviction pool sampling keys from * every DB. */ for (i = 0; i < server.dbnum; i++) { db = server.db+i; dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU) ? db->dict : db->expires; if ((keys = dictSize(dict)) != 0) { evictionPoolPopulate(i, dict, db->dict, pool); total_keys += keys; } } if (!total_keys) break; /* No keys to evict. */ /* Go backward from best to worst element to evict. */ for (k = EVPOOL_SIZE-1; k >= 0; k--) { if (pool[k].key == NULL) continue; bestdbid = pool[k].dbid; if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU) { de = dictFind(server.db[pool[k].dbid].dict, pool[k].key); } else { de = dictFind(server.db[pool[k].dbid].expires, pool[k].key); } /* Remove the entry from the pool. */ if (pool[k].key != pool[k].cached) sdsfree(pool[k].key); pool[k].key = NULL; pool[k].idle = 0; /* If the key exists, is our pick. Otherwise it is * a ghost and we need to try the next element. */ if (de) { bestkey = dictGetKey(de); break; } else { /* Ghost... Iterate again. */ } } } } /* volatile-random and allkeys-random policy */ else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM || server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM) { /* When evicting a random key, we try to evict a key for * each DB, so we use the static 'next_db' variable to * incrementally visit all DBs. */ for (i = 0; i < server.dbnum; i++) { j = (++next_db) % server.dbnum; db = server.db+j; dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM) ? db->dict : db->expires; if (dictSize(dict) != 0) { de = dictGetRandomKey(dict); bestkey = dictGetKey(de); bestdbid = j; break; } } } /* volatile-ttl */ else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) { long bestttl = 0; /* Initialized to avoid warning. */ /* In this policy we scan a single DB per iteration (visiting * a different DB per call), expiring the key with the smallest * TTL among the few sampled. * * Note that this algorithm makes local-DB choices, and should * use a pool and code more similr to the one used in the * LRU eviction policies in the future. */ for (i = 0; i < server.dbnum; i++) { j = (++next_db) % server.dbnum; db = server.db+j; dict = db->expires; if (dictSize(dict) != 0) { for (k = 0; k < server.maxmemory_samples; k++) { sds thiskey; long thisttl; de = dictGetRandomKey(dict); thiskey = dictGetKey(de); thisttl = (long) dictGetVal(de); /* Keys expiring sooner (smaller unix timestamp) are * better candidates for deletion */ if (bestkey == NULL || thisttl < bestttl) { bestkey = thiskey; bestttl = thisttl; bestdbid = j; } } } } } /* Finally remove the selected key. */ if (bestkey) { db = server.db+bestdbid; robj *keyobj = createStringObject(bestkey,sdslen(bestkey)); propagateExpire(db,keyobj,server.lazyfree_lazy_eviction); /* We compute the amount of memory freed by db*Delete() alone. * It is possible that actually the memory needed to propagate * the DEL in AOF and replication link is greater than the one * we are freeing removing the key, but we can't account for * that otherwise we would never exit the loop. * * AOF and Output buffer memory will be freed eventually so * we only care about memory used by the key space. */ delta = (long long) zmalloc_used_memory(); latencyStartMonitor(eviction_latency); if (server.lazyfree_lazy_eviction) dbAsyncDelete(db,keyobj); else dbSyncDelete(db,keyobj); latencyEndMonitor(eviction_latency); latencyAddSampleIfNeeded("eviction-del",eviction_latency); latencyRemoveNestedEvent(latency,eviction_latency); delta -= (long long) zmalloc_used_memory(); mem_freed += delta; server.stat_evictedkeys++; notifyKeyspaceEvent(NOTIFY_EVICTED, "evicted", keyobj, db->id); decrRefCount(keyobj); keys_freed++; /* When the memory to free starts to be big enough, we may * start spending so much time here that is impossible to * deliver data to the slaves fast enough, so we force the * transmission here inside the loop. */ if (slaves) flushSlavesOutputBuffers(); } if (!keys_freed) { latencyEndMonitor(latency); latencyAddSampleIfNeeded("eviction-cycle",latency); goto cant_free; /* nothing to free... */ } } latencyEndMonitor(latency); latencyAddSampleIfNeeded("eviction-cycle",latency); return C_OK; cant_free: /* We are here if we are not able to reclaim memory. There is only one * last thing we can try: check if the lazyfree thread has jobs in queue * and wait... */ while(bioPendingJobsOfType(BIO_LAZY_FREE)) { if (((mem_reported - zmalloc_used_memory()) + mem_freed) >= mem_tofree) break; usleep(1000); } return C_ERR; }
/* * * 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; }
/* return array of sockets that are ready for read or write depending on the mask for each socket */ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { aeApiState *state = (aeApiState *)eventLoop->apidata; aeSockState *sockstate; ULONG j; int numevents = 0; ULONG numComplete = 0; int rc; int mswait = (tvp == NULL) ? 100 : (tvp->tv_sec * 1000) + (tvp->tv_usec / 1000); if (pGetQueuedCompletionStatusEx != NULL) { /* first get an array of completion notifications */ rc = pGetQueuedCompletionStatusEx(state->iocp, state->entries, MAX_COMPLETE_PER_POLL, &numComplete, mswait, FALSE); } else { /* need to get one at a time. Use first array element */ rc = GetQueuedCompletionStatus(state->iocp, &state->entries[0].dwNumberOfBytesTransferred, &state->entries[0].lpCompletionKey, &state->entries[0].lpOverlapped, mswait); if (!rc && state->entries[0].lpOverlapped == NULL) { // timeout. Return. return 0; } else { // check if more completions are ready int lrc = 1; rc = 1; numComplete = 1; while (numComplete < MAX_COMPLETE_PER_POLL) { lrc = GetQueuedCompletionStatus(state->iocp, &state->entries[numComplete].dwNumberOfBytesTransferred, &state->entries[numComplete].lpCompletionKey, &state->entries[numComplete].lpOverlapped, 0); if (lrc) { numComplete++; } else { if (state->entries[numComplete].lpOverlapped == NULL) break; } } } } if (rc && numComplete > 0) { LPOVERLAPPED_ENTRY entry = state->entries; for (j = 0; j < numComplete && numevents < state->setsize; j++, entry++) { /* the competion key is the socket */ int rfd = (int)entry->lpCompletionKey; sockstate = aeGetExistingSockState(state, rfd); if (sockstate != NULL) { if ((sockstate->masks & LISTEN_SOCK) && entry->lpOverlapped != NULL) { /* need to set event for listening */ aacceptreq *areq = (aacceptreq *)entry->lpOverlapped; areq->next = sockstate->reqs; sockstate->reqs = areq; sockstate->masks &= ~ACCEPT_PENDING; if (sockstate->masks & AE_READABLE) { eventLoop->fired[numevents].fd = rfd; eventLoop->fired[numevents].mask = AE_READABLE; numevents++; } } else if (sockstate->masks & CONNECT_PENDING) { /* check if connect complete */ if (entry->lpOverlapped == &sockstate->ov_read) { sockstate->masks &= ~CONNECT_PENDING; /* enable read and write events for this connection */ aeApiAddEvent(eventLoop, rfd, sockstate->masks); } } else { int matched = 0; /* check if event is read complete (may be 0 length read) */ if (entry->lpOverlapped == &sockstate->ov_read) { matched = 1; sockstate->masks &= ~READ_QUEUED; if (sockstate->masks & AE_READABLE) { eventLoop->fired[numevents].fd = rfd; eventLoop->fired[numevents].mask = AE_READABLE; numevents++; } } else if (sockstate->wreqs > 0 && entry->lpOverlapped != NULL) { /* should be write complete. Get results */ asendreq *areq = (asendreq *)entry->lpOverlapped; matched = removeMatchFromList(&sockstate->wreqlist, areq); if (matched) { /* call write complete callback so buffers can be freed */ if (areq->proc != NULL) { DWORD written = 0; DWORD flags; WSAGetOverlappedResult(rfd, &areq->ov, &written, FALSE, &flags); areq->proc(areq->eventLoop, rfd, &areq->req, (int)written); } sockstate->wreqs--; zfree(areq); /* if no active write requests, set ready to write */ if (sockstate->wreqs == 0 && sockstate->masks & AE_WRITABLE) { eventLoop->fired[numevents].fd = rfd; eventLoop->fired[numevents].mask = AE_WRITABLE; numevents++; } } } if (matched == 0) { /* redisLog */printf("Sec:%lld Unknown complete (closed) on %d\n", gettimeofdaysecs(NULL), rfd); sockstate = NULL; } } } else { // no match for active connection. // Try the closing list. list *socklist = &(state->closing); listNode *node; node = listFirst(socklist); while (node != NULL) { sockstate = (aeSockState *)listNodeValue(node); if (sockstate->fd == rfd) { if (sockstate->masks & CONNECT_PENDING) { /* check if connect complete */ if (entry->lpOverlapped == &sockstate->ov_read) { sockstate->masks &= ~CONNECT_PENDING; } } else if (entry->lpOverlapped == &sockstate->ov_read) { // read complete sockstate->masks &= ~READ_QUEUED; } else { // check pending writes asendreq *areq = (asendreq *)entry->lpOverlapped; if (removeMatchFromList(&sockstate->wreqlist, areq)) { sockstate->wreqs--; zfree(areq); } } if (sockstate->wreqs == 0 && (sockstate->masks & (CONNECT_PENDING | READ_QUEUED | SOCKET_ATTACHED)) == 0) { if ((sockstate->masks & CLOSE_PENDING) != 0) { close(rfd); sockstate->masks &= ~(CLOSE_PENDING); } // safe to delete sockstate aeDelSockState(state, sockstate); } break; } node = listNextNode(node); } } } } return numevents; }
int staticFileCall(struct conn *c, void *arg) { wstr path = arg; struct staticFileData *static_data; off_t len; time_t m_time; int ret; struct stat stat; static_data = c->app_private_data; static_data->fd = open(path, O_RDONLY); if (static_data->fd == -1) { wheatLog(WHEAT_VERBOSE, "open file failed: %s", strerror(errno)); goto failed404; } ret = lstat(path, &stat); if (ret == -1) goto failed404; if (!S_ISREG(stat.st_mode)) { close(static_data->fd); static_data->fd = -1; if (S_ISDIR(stat.st_mode) && DirectoryIndex) { struct listNode *node; struct listIterator *iter; wstr last; char append_path[255]; iter = listGetIterator(DirectoryIndex, START_HEAD); while ((node = listNext(iter)) != NULL) { last = listNodeValue(node); snprintf(append_path, 255, "%s/%s", path, last); static_data->fd = open(append_path, O_RDONLY); if (static_data->fd != -1) { break; } } } if (static_data->fd == -1) { wheatLog(WHEAT_VERBOSE, "open file failed: %s", strerror(errno)); goto failed404; } } if (AllowExtensions && static_data->extension && !dictFetchValue(AllowExtensions, static_data->extension)) { goto failed404; } ret = getFileSizeAndMtime(static_data->fd , &len, &m_time); if (ret == WHEAT_WRONG) { wheatLog(WHEAT_VERBOSE, "open file failed: %s", strerror(errno)); goto failed404; } if (len > MaxFileSize) { wheatLog(WHEAT_NOTICE, "file exceed max limit %d", len); wheatLog(WHEAT_VERBOSE, "open file failed: %s", strerror(errno)); goto failed404; } wstr modified = dictFetchValue(httpGetReqHeaders(c), IfModifiedSince); if (modified != NULL) { char buf[wstrlen(modified)]; memcpy(buf, modified, wstrlen(modified)); time_t client_m_time = fromHttpDate(buf); if (m_time <= client_m_time) { fillResInfo(c, 304, "Not Modified"); ret = fillResHeaders(c, 0, 0); if (ret == -1) goto failed; ret = httpSendHeaders(c); if (ret == -1) goto failed; return WHEAT_OK; } } fillResInfo(c, 200, "OK"); ret = fillResHeaders(c, len, m_time); if (ret == -1) { wheatLog(WHEAT_WARNING, "fill Res Headers failes: %s", strerror(errno)); goto failed; } ret = httpSendHeaders(c); if (ret == -1) { wheatLog(WHEAT_WARNING, "static file send headers failed: %s", strerror(errno)); goto failed; } ret = sendClientFile(c, static_data->fd , len); if (ret == WHEAT_WRONG) { wheatLog(WHEAT_WARNING, "send static file failed: %s", strerror(errno)); goto failed; } return WHEAT_OK; failed404: sendResponse404(c); return WHEAT_OK; failed: setClientClose(c); return WHEAT_OK; }
int main(int argc, char *argv[]) { unsigned char *zl, *p; unsigned char *entry; unsigned int elen; long long value; /* If an argument is given, use it as the random seed. */ if (argc == 2) srand(atoi(argv[1])); zl = createIntList(); ziplistRepr(zl); zl = createList(); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); pop(zl,ZIPLIST_HEAD); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); printf("Get element at index 3:\n"); { zl = createList(); p = ziplistIndex(zl, 3); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index 3\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index 4 (out of range):\n"); { zl = createList(); p = ziplistIndex(zl, 4); if (p == NULL) { printf("No entry\n"); } else { printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); return 1; } printf("\n"); } printf("Get element at index -1 (last element):\n"); { zl = createList(); p = ziplistIndex(zl, -1); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index -1\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index -4 (first element):\n"); { zl = createList(); p = ziplistIndex(zl, -4); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index -4\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index -5 (reverse out of range):\n"); { zl = createList(); p = ziplistIndex(zl, -5); if (p == NULL) { printf("No entry\n"); } else { printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); return 1; } printf("\n"); } printf("Iterate list from 0 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 0); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate list from 1 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate list from 2 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 2); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate starting out of range:\n"); { zl = createList(); p = ziplistIndex(zl, 4); if (!ziplistGet(p, &entry, &elen, &value)) { printf("No entry\n"); } else { printf("ERROR\n"); } printf("\n"); } printf("Iterate from back to front:\n"); { zl = createList(); p = ziplistIndex(zl, -1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistPrev(zl,p); printf("\n"); } printf("\n"); } printf("Iterate from back to front, deleting all items:\n"); { zl = createList(); p = ziplistIndex(zl, -1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } zl = ziplistDelete(zl,&p); p = ziplistPrev(zl,p); printf("\n"); } printf("\n"); } printf("Delete inclusive range 0,0:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 0, 1); ziplistRepr(zl); } printf("Delete inclusive range 0,1:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 0, 2); ziplistRepr(zl); } printf("Delete inclusive range 1,2:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 1, 2); ziplistRepr(zl); } printf("Delete with start index out of range:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 5, 1); ziplistRepr(zl); } printf("Delete with num overflow:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 1, 5); ziplistRepr(zl); } printf("Delete foo while iterating:\n"); { zl = createList(); p = ziplistIndex(zl,0); while (ziplistGet(p,&entry,&elen,&value)) { if (entry && strncmp("foo",(char*)entry,elen) == 0) { printf("Delete foo\n"); zl = ziplistDelete(zl,&p); } else { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld",value); } p = ziplistNext(zl,p); printf("\n"); } } printf("\n"); ziplistRepr(zl); } printf("Regression test for >255 byte strings:\n"); { char v1[257],v2[257]; memset(v1,'x',256); memset(v2,'y',256); zl = ziplistNew(); zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL); zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL); /* Pop values again and compare their value. */ p = ziplistIndex(zl,0); assert(ziplistGet(p,&entry,&elen,&value)); assert(strncmp(v1,(char*)entry,elen) == 0); p = ziplistIndex(zl,1); assert(ziplistGet(p,&entry,&elen,&value)); assert(strncmp(v2,(char*)entry,elen) == 0); printf("SUCCESS\n\n"); } printf("Regression test deleting next to last entries:\n"); { char v[3][257]; zlentry e[3]; int i; for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) { memset(v[i], 'a' + i, sizeof(v[0])); } v[0][256] = '\0'; v[1][ 1] = '\0'; v[2][256] = '\0'; zl = ziplistNew(); for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) { zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL); } verify(zl, e); assert(e[0].prevrawlensize == 1); assert(e[1].prevrawlensize == 5); assert(e[2].prevrawlensize == 1); /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */ unsigned char *p = e[1].p; zl = ziplistDelete(zl, &p); verify(zl, e); assert(e[0].prevrawlensize == 1); assert(e[1].prevrawlensize == 5); printf("SUCCESS\n\n"); } printf("Create long list and check indices:\n"); { zl = ziplistNew(); char buf[32]; int i,len; for (i = 0; i < 1000; i++) { len = sprintf(buf,"%d",i); zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL); } for (i = 0; i < 1000; i++) { p = ziplistIndex(zl,i); assert(ziplistGet(p,NULL,NULL,&value)); assert(i == value); p = ziplistIndex(zl,-i-1); assert(ziplistGet(p,NULL,NULL,&value)); assert(999-i == value); } printf("SUCCESS\n\n"); } printf("Compare strings with ziplist entries:\n"); { zl = createList(); p = ziplistIndex(zl,0); if (!ziplistCompare(p,(unsigned char*)"hello",5)) { printf("ERROR: not \"hello\"\n"); return 1; } if (ziplistCompare(p,(unsigned char*)"hella",5)) { printf("ERROR: \"hella\"\n"); return 1; } p = ziplistIndex(zl,3); if (!ziplistCompare(p,(unsigned char*)"1024",4)) { printf("ERROR: not \"1024\"\n"); return 1; } if (ziplistCompare(p,(unsigned char*)"1025",4)) { printf("ERROR: \"1025\"\n"); return 1; } printf("SUCCESS\n\n"); } printf("Stress with random payloads of different encoding:\n"); { int i,j,len,where; unsigned char *p; char buf[1024]; int buflen; list *ref; listNode *refnode; /* Hold temp vars from ziplist */ unsigned char *sstr; unsigned int slen; long long sval; for (i = 0; i < 20000; i++) { zl = ziplistNew(); ref = listCreate(); listSetFreeMethod(ref, sdsfree); len = rand() % 256; /* Create lists */ for (j = 0; j < len; j++) { where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL; if (rand() % 2) { buflen = randstring(buf,1,sizeof(buf)-1); } else { switch(rand() % 3) { case 0: buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20); break; case 1: buflen = sprintf(buf,"%lld",(0LL + rand())); break; case 2: buflen = sprintf(buf,"%lld",(0LL + rand()) << 20); break; default: assert(NULL); } } /* Add to ziplist */ zl = ziplistPush(zl, (unsigned char*)buf, buflen, where); /* Add to reference list */ if (where == ZIPLIST_HEAD) { listAddNodeHead(ref,sdsnewlen(buf, buflen)); } else if (where == ZIPLIST_TAIL) { listAddNodeTail(ref,sdsnewlen(buf, buflen)); } else { assert(NULL); } } assert(listLength(ref) == ziplistLen(zl)); for (j = 0; j < len; j++) { /* Naive way to get elements, but similar to the stresser * executed from the Tcl test suite. */ p = ziplistIndex(zl,j); refnode = listIndex(ref,j); assert(ziplistGet(p,&sstr,&slen,&sval)); if (sstr == NULL) { buflen = sprintf(buf,"%lld",sval); } else { buflen = slen; memcpy(buf,sstr,buflen); buf[buflen] = '\0'; } assert(memcmp(buf,listNodeValue(refnode),buflen) == 0); } zfree(zl); listRelease(ref); } printf("SUCCESS\n\n"); } printf("Stress with variable ziplist size:\n"); { stress(ZIPLIST_HEAD,100000,16384,256); stress(ZIPLIST_TAIL,100000,16384,256); } return 0; }
static int send_msg_to_all(check_unit *cunit, struct msg *msg) { int ret; thread_data *cdata = cunit->cdata; redis_group *trgroup = cdata->trgroup; redis_node *trnode; struct msg *msg_same; listNode *lnode; struct mbuf *mbuf; if (cunit == NULL || msg == NULL) { return RMT_ERROR; } msg_same = msg_get(msg->mb, msg->request, msg->kind); if (msg_same == NULL) { log_error("ERROR: msg clone failed."); msg_put(msg); msg_free(msg); msg = NULL; return RMT_ERROR; } lnode = listFirst(msg->data); while (lnode) { mbuf = listNodeValue(lnode); lnode = lnode->next; ret = msg_append_full(msg_same, mbuf->pos, mbuf_length(mbuf)); if (ret != RMT_OK) { log_error("ERROR: out of memory."); msg_put(msg_same); msg_free(msg_same); msg = NULL; return RMT_ERROR; } } msg_same->ptr = msg->ptr; msg_same->resp_check = msg->resp_check; ret = prepare_send_msg(cunit->srnode, msg, cunit->srnode); if (ret != RMT_OK) { msg_put(msg); msg_free(msg); msg = NULL; msg_put(msg_same); msg_free(msg_same); return RMT_ERROR; } msg = NULL; trnode = trgroup->get_backend_node(trgroup, (uint8_t *)cunit->key, (uint32_t)sdslen(cunit->key)); if(prepare_send_msg(trnode, msg_same, trnode) != RMT_OK){ msg_put(msg_same); msg_free(msg_same); return RMT_ERROR; } return RMT_OK; }