/* On FLUSHDB or FLUSHALL all the watched keys that are present before the * flush but will be deleted as effect of the flushing operation should * be touched. "dbid" is the DB that's getting the flush. -1 if it is * a FLUSHALL operation (all the DBs flushed). * * 当一个数据库被 FLUSHDB 或者 FLUSHALL 清空时, * 它数据库内的所有 key 都应该被触碰。 * * dbid 参数指定要被 FLUSH 的数据库。 * * 如果 dbid 为 -1 ,那么表示执行的是 FLUSHALL , * 所有数据库都将被 FLUSH */ void touchWatchedKeysOnFlush(int dbid) { listIter li1, li2; listNode *ln; // 这里的思路挺有趣的,不是遍历数据库的所有 key 来让客户端变为 DIRTY // 而是遍历所有客户端,然后遍历客户端监视的键,再让相应的客户端变为 DIRTY // 后者要比前者高效很多 /* For every client, check all the waited keys */ // 遍历所有客户端 listRewind(server.clients,&li1); while((ln = listNext(&li1))) { redisClient *c = listNodeValue(ln); // 遍历客户端监视的键 listRewind(c->watched_keys,&li2); while((ln = listNext(&li2))) { // 取出监视的键和键的数据库 watchedKey *wk = listNodeValue(ln); /* For every watched key matching the specified DB, if the * key exists, mark the client as dirty, as the key will be * removed. */ // 如果数据库号码相同,或者执行的命令为 FLUSHALL // 那么将客户端设置为 REDIS_DIRTY_CAS if (dbid == -1 || wk->db->id == dbid) { if (dictFind(wk->db->dict, wk->key->ptr) != NULL) c->flags |= REDIS_DIRTY_CAS; } } } }
// 当数据库执行了FLUSHDB或FLUSHALL命令,所有的key都被清空,那么数据库中的所有的键都被touched // dbid指定被FLUSH的数据库id,如果dbid为-1,表示执行了FLUSHALL,所有的数据库都被FLUSH void touchWatchedKeysOnFlush(int dbid) { listIter li1, li2; listNode *ln; /* For every client, check all the waited keys */ listRewind(server.clients,&li1); // 遍历所有的client while((ln = listNext(&li1))) { client *c = listNodeValue(ln); listRewind(c->watched_keys,&li2); // 遍历当前client所监视的key while((ln = listNext(&li2))) { // 取出被监视的key和关联的数据库 watchedKey *wk = listNodeValue(ln); /* For every watched key matching the specified DB, if the * key exists, mark the client as dirty, as the key will be * removed. */ // 如果数据库id合法 if (dbid == -1 || wk->db->id == dbid) { // 数据库中存在该key,则设置client的DIRTY_CAS标识 if (dictFind(wk->db->dict, wk->key->ptr) != NULL) c->flags |= CLIENT_DIRTY_CAS; } } } }
/* * 为 FLUSHDB 和 FLUSHALL 特别设置的触碰函数 * * T = O(N^2) */ void touchWatchedKeysOnFlush(int dbid) { listIter li1, li2; listNode *ln; /* For every client, check all the waited keys */ // 列出所有客户端,O(N) listRewind(server.clients,&li1); while((ln = listNext(&li1))) { redisClient *c = listNodeValue(ln); // 列出所有监视 key ,O(N) listRewind(c->watched_keys,&li2); while((ln = listNext(&li2))) { watchedKey *wk = listNodeValue(ln); /* For every watched key matching the specified DB, if the * key exists, mark the client as dirty, as the key will be * removed. */ // 如果目标 db 和监视 key 的 DB 相同, // 那么打开客户端的 REDIS_DIRTY_CAS 选项 // O(1) if (dbid == -1 || wk->db->id == dbid) { if (dictFind(wk->db->dict, wk->key->ptr) != NULL) c->flags |= REDIS_DIRTY_CAS; } } } }
/* 发布一则消息,即将消息发送给所有订阅了指定频道channel的所有客户端以及所有订阅了和指定频道匹配的模式的客户端。*/ int pubsubPublishMessage(robj *channel, robj *message) { int receivers = 0; dictEntry *de; listNode *ln; listIter li; /* Send to clients listening for that channel */ // 取出订阅指定频道的客户端链表 de = dictFind(server.pubsub_channels,channel); if (de) { list *list = dictGetVal(de); listNode *ln; listIter li; listRewind(list,&li); // 遍历该客户端链表,并将消息逐一发送给这些客户端 while ((ln = listNext(&li)) != NULL) { redisClient *c = ln->value; // 发送消息 addReply(c,shared.mbulkhdr[3]); // 回复“message”字符串 addReply(c,shared.messagebulk); // 回复消息的来源频道 addReplyBulk(c,channel); // 回复消息内容 addReplyBulk(c,message); // 统计接收客户端的数量 receivers++; } } /* Send to clients listening to matching channels */ // 将消息发送个订阅了和指定频道匹配的模式的客户端 if (listLength(server.pubsub_patterns)) { listRewind(server.pubsub_patterns,&li); channel = getDecodedObject(channel); // 遍历server.pubsub_patterns while ((ln = listNext(&li)) != NULL) { // 取出当前模式 pubsubPattern *pat = ln->value; // 判断当前模式是否和给定频道相匹配 if (stringmatchlen((char*)pat->pattern->ptr, sdslen(pat->pattern->ptr), (char*)channel->ptr, sdslen(channel->ptr),0)) { // 发送消息 addReply(pat->client,shared.mbulkhdr[4]); addReply(pat->client,shared.pmessagebulk); addReplyBulk(pat->client,pat->pattern); addReplyBulk(pat->client,channel); addReplyBulk(pat->client,message); receivers++; } } decrRefCount(channel); } return receivers; }
void updateSlavesWaitingBgsave(int bgsaveerr){ listNode *ln; int startbgsave = 0; listIter li; listRewind(server.slaves, &li); while((ln=listNext(&li))){ clientContext *slave = ln->value; if (slave->repl_state == REPL_WAIT_BGSAVE_START){ startbgsave = 1; slave->repl_state = REPL_WAIT_BGSAVE_END; } else if (slave->repl_state == REPL_WAIT_BGSAVE_END) { if (bgsaveerr != 0){ cc_freeClient(slave); xlog(LOG_WARN, "replication: 数据同步失败, db 保存子进程返回错误\n"); continue; } struct stat buf; if ( ((slave->repl_dbfd=open(server.dbfilename, O_RDONLY)) == -1) || (fstat(slave->repl_dbfd, &buf)==-1)) { cc_freeClient(slave); xlog(LOG_WARN, "replication: db 文件打开失败\n"); continue; } slave->repl_dboff = 0; slave->repl_dbsize = buf.st_size; slave->repl_state = REPL_SEND_DB; if (multi_deleteFileEvent(server.el, slave->fd, MULTI_WRITABLE) < 0) { cc_freeClient(slave); continue; } // 添加发送 db 数据事件函数 if (multi_createFileEvent(server.el, slave->fd, MULTI_WRITABLE, sendDBToSlave, slave) < 0){ cc_freeClient(slave); continue; } } } if (startbgsave){ if (db_bgsave(server.dbfilename) != 0){ listIter li; listRewind(server.slaves, &li); xlog(LOG_WARN, "bgsave: db 保存失败\n"); while((ln=listNext(&li))){ clientContext *slave = ln->value; if (slave->repl_state == REPL_WAIT_BGSAVE_END){ cc_freeClient(slave); } } } } }
/* This function is called at the end of every background saving. * The argument bgsaveerr is REDIS_OK if the background saving succeeded * otherwise REDIS_ERR is passed to the function. * * The goal of this function is to handle slaves waiting for a successful * background saving in order to perform non-blocking synchronization. */ void updateSlavesWaitingBgsave(int bgsaveerr) { //backgroundSaveDoneHandler在BGSAVE操作完成后,调用这里来处理可能的从库事件。 listNode *ln; int startbgsave = 0; listIter li; listRewind(server.slaves,&li); while((ln = listNext(&li))) {//循环遍历每一个从库,查看其状态进行相应的处理。 redisClient *slave = ln->value; if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) { startbgsave = 1;//刚才在做bgsave的时候,有客户端来请求sync同步,但是我没有理他,现在得给他准备了。 slave->replstate = REDIS_REPL_WAIT_BGSAVE_END;//修改这个状态后,新的写入操作会记录到这个连接的缓存里 } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) { //后台保存完成,下面需要发送rdb文件了,丫的够大的 struct redis_stat buf; if (bgsaveerr != REDIS_OK) { freeClient(slave); redisLog(REDIS_WARNING,"SYNC failed. BGSAVE child returned an error"); continue; } //打开这个rdb_filename,要准备给这个slave发送数据了。 if ((slave->repldbfd = open(server.rdb_filename,O_RDONLY)) == -1 || redis_fstat(slave->repldbfd,&buf) == -1) { freeClient(slave); redisLog(REDIS_WARNING,"SYNC failed. Can't open/stat DB after BGSAVE: %s", strerror(errno)); continue; } slave->repldboff = 0; slave->repldbsize = buf.st_size; //记住此时slave->repldbfd没有关闭,可写事件的时候就不需要打开了。 slave->replstate = REDIS_REPL_SEND_BULK; aeDeleteFileEvent(server.el,slave->fd,AE_WRITABLE);//删掉之前的可写回调,注册为sendBulkToSlave if (aeCreateFileEvent(server.el, slave->fd, AE_WRITABLE, sendBulkToSlave, slave) == AE_ERR) { freeClient(slave); continue; } } } if (startbgsave) {//悲剧,又有要sync的,还得保存一次。 if (rdbSaveBackground(server.rdb_filename) != REDIS_OK) { listIter li; listRewind(server.slaves,&li); redisLog(REDIS_WARNING,"SYNC failed. BGSAVE failed"); while((ln = listNext(&li))) { redisClient *slave = ln->value; //这下面似乎有问题,replstate已经在上面被设置为了_END。https://github.com/antirez/redis/issues/1308 if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) freeClient(slave); } } } }
/* This function is called at the end of every backgrond saving. * The argument bgsaveerr is REDIS_OK if the background saving succeeded * otherwise REDIS_ERR is passed to the function. * * The goal of this function is to handle slaves waiting for a successful * background saving in order to perform non-blocking synchronization. */ void updateSlavesWaitingBgsave(int bgsaveerr) { listNode *ln; int startbgsave = 0; listIter li; listRewind(server.slaves,&li); while((ln = listNext(&li))) { redisClient *slave = ln->value; if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) { startbgsave = 1; slave->replstate = REDIS_REPL_WAIT_BGSAVE_END; } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) { struct redis_stat buf; if (bgsaveerr != REDIS_OK) { freeClient(slave); redisLog(REDIS_WARNING,"SYNC failed. BGSAVE child returned an error"); continue; } if ((slave->repldbfd = open(server.dbfilename,O_RDONLY)) == -1 || redis_fstat(slave->repldbfd,&buf) == -1) { freeClient(slave); redisLog(REDIS_WARNING,"SYNC failed. Can't open/stat DB after BGSAVE: %s", strerror(errno)); continue; } slave->repldboff = 0; slave->repldbsize = buf.st_size; slave->replstate = REDIS_REPL_SEND_BULK; aeDeleteFileEvent(server.el,slave->fd,AE_WRITABLE); if (aeCreateFileEvent(server.el, slave->fd, AE_WRITABLE, sendBulkToSlave, slave) == AE_ERR) { freeClient(slave); continue; } } } if (startbgsave) { if (rdbSaveBackground(server.dbfilename) != REDIS_OK) { listIter li; listRewind(server.slaves,&li); redisLog(REDIS_WARNING,"SYNC failed. BGSAVE failed"); while((ln = listNext(&li))) { redisClient *slave = ln->value; if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) freeClient(slave); } } } }
/* * 发布一个消息至指定频道 */ int pubsubPublishMessage(robj *channel, robj *message) { int receivers = 0; dictEntry *de; listNode *ln; listIter li; /* Send to clients listening for that channel */ /* 发布给监听指定频道的客户端 */ de = dictFind(server.pubsub_channels,channel); if (de) { list *list = dictGetVal(de); listNode *ln; listIter li; listRewind(list,&li); while ((ln = listNext(&li)) != NULL) { redisClient *c = ln->value; addReply(c,shared.mbulkhdr[3]); addReply(c,shared.messagebulk); addReplyBulk(c,channel); addReplyBulk(c,message); receivers++; } } /* Send to clients listening to matching channels */ /* 发布给监听模式匹配的客户端 */ if (listLength(server.pubsub_patterns)) { listRewind(server.pubsub_patterns,&li); channel = getDecodedObject(channel); while ((ln = listNext(&li)) != NULL) { pubsubPattern *pat = ln->value; if (stringmatchlen((char*)pat->pattern->ptr, sdslen(pat->pattern->ptr), (char*)channel->ptr, sdslen(channel->ptr),0)) { addReply(pat->client,shared.mbulkhdr[4]); addReply(pat->client,shared.pmessagebulk); addReplyBulk(pat->client,pat->pattern); addReplyBulk(pat->client,channel); addReplyBulk(pat->client,message); receivers++; } } decrRefCount(channel); } return receivers; }
/* 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 */ wk = listNodeValue(ln); clients = dictFetchValue(wk->db->watched_keys, wk->key); redisAssert(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 */ listDelNode(c->watched_keys,ln); decrRefCount(wk->key); zfree(wk); } }
/* 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); /* Add the new key to the lits of keys watched by this client */ wk = zmalloc(sizeof(*wk)); wk->key = key; wk->db = c->db; incrRefCount(key); listAddNodeTail(c->watched_keys,wk); }
/* Unsubscribe from all the patterns. Return the number of patterns the * client was subscribed from. * * 退订客户端 c 订阅的所有模式。 * * 返回被退订模式的数量。 */ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify) { listNode *ln; listIter li; int count = 0; // 迭代客户端订阅模式的链表 listRewind(c->pubsub_patterns,&li); while ((ln = listNext(&li)) != NULL) { robj *pattern = ln->value; // 退订,并计算退订数 count += pubsubUnsubscribePattern(c,pattern,notify); } // 如果在执行这个函数时,客户端没有订阅任何模式, // 那么向客户端发送回复 if (notify && count == 0) { /* We were subscribed to nothing? Still reply to the client. */ addReply(c,shared.mbulkhdr[3]); addReply(c,shared.punsubscribebulk); addReply(c,shared.nullbulk); addReplyLongLong(c,dictSize(c->pubsub_channels)+ listLength(c->pubsub_patterns)); } // 退订总数 return count; }
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { listNode *ln; listIter li; int j; listRewind(slaves,&li); while((ln = listNext(&li))) { redisClient *slave = ln->value; /* Don't feed slaves that are still waiting for BGSAVE to start */ if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) continue; /* Feed slaves that are waiting for the initial SYNC (so these commands * are queued in the output buffer until the intial SYNC completes), * or are already in sync with the master. */ if (slave->slaveseldb != dictid) { robj *selectcmd; if (dictid >= 0 && dictid < REDIS_SHARED_SELECT_CMDS) { selectcmd = shared.select[dictid]; incrRefCount(selectcmd); } else { selectcmd = createObject(REDIS_STRING, sdscatprintf(sdsempty(),"select %d\r\n",dictid)); } addReply(slave,selectcmd); decrRefCount(selectcmd); slave->slaveseldb = dictid; } addReplyMultiBulkLen(slave,argc); for (j = 0; j < argc; j++) addReplyBulk(slave,argv[j]); } }
void *_masterWatch(void *t) { (void)t; ccache *c; listIter li; listNode *ln; int msecSleep = 1000; while (1) { master_numjob = 0; listRewind(slave_caches,&li); while ((ln = listNext(&li)) != NULL) { c = listNodeValue(ln); _masterProcessCacheNew(c); _masterProcessFinishedIO(); _masterProcessCacheOld(c); _masterProcessStatus(); } if(master_numjob < 1) msecSleep = 10000; else { printf("num job %d\n",master_numjob); msecSleep = 10; } /* TODO: flexible sleep time */ usleep(msecSleep); } pthread_exit(NULL); }
// 取消客户端对所有的键的监视,清理 EXEC dirty 标识状态由调用者决定 void unwatchAllKeys(client *c) { listIter li; listNode *ln; // 如果客户端没有监视key则直接返回 if (listLength(c->watched_keys) == 0) return; listRewind(c->watched_keys,&li); // 遍历客户端监视的key while((ln = listNext(&li))) { list *clients; watchedKey *wk; /* Lookup the watched key -> clients list and remove the client * from the list */ wk = listNodeValue(ln); // 从数据库中的watched_keys字典中查找出监视key的client clients = dictFetchValue(wk->db->watched_keys, wk->key); serverAssertWithInfo(c,NULL,clients != NULL); // 从client的链表中删除当前client节点 listDelNode(clients,listSearchKey(clients,c)); /* Kill the entry at all if this was the only client */ // 如果client链表为空,标识给key没有被监视 if (listLength(clients) == 0) // 从数据库的watched_keys中删除该key dictDelete(wk->db->watched_keys, wk->key); /* Remove this watched key from the client->watched list */ // 从客户端的watched_keys中删除该节点 listDelNode(c->watched_keys,ln); decrRefCount(wk->key); zfree(wk); } }
/* * “碰触”(touch)给定 key ,如果这个 key 正在被监视的话, * 让监视它的客户端在执行 EXEC 命令时失败。 * * T = O(N) */ void touchWatchedKey(redisDb *db, robj *key) { list *clients; listIter li; listNode *ln; // 如果数据库中没有任何 key 被监视,那么直接返回 if (dictSize(db->watched_keys) == 0) return; // 取出数据库中所有监视给定 key 的客户端 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 */ // 打开所有监视这个 key 的客户端的 REDIS_DIRTY_CAS 状态 // O(N) listRewind(clients,&li); while((ln = listNext(&li))) { redisClient *c = listNodeValue(ln); c->flags |= REDIS_DIRTY_CAS; } }
// 撤销对这个客户端的所有 WATCH // 清除 EXEC dirty FLAG 的任务由调用者完成 void unwatchAllKeys(redisClient *c) { listIter li; listNode *ln; // 没有 WATCHED KEY ,直接返回 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 */ // 将当前客户端从监视 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 */ // 如果监视 KEY 的只有这个客户端 // 那么将链表从字典中删除 if (listLength(clients) == 0) dictDelete(wk->db->watched_keys, wk->key); /* Remove this watched key from the client->watched list */ // 还需要将 KEY 从 client->watched_keys 链表中移除 listDelNode(c->watched_keys,ln); decrRefCount(wk->key); zfree(wk); } }
/* Remove the 'key' from the list of blocked keys for a given client. * * The function returns 1 when there are no longer blocking keys after * the current one was removed (and the client can be unblocked). */ int dontWaitForSwappedKey(redisClient *c, robj *key) { list *l; listNode *ln; listIter li; struct dictEntry *de; /* The key object might be destroyed when deleted from the c->io_keys * list (and the "key" argument is physically the same object as the * object inside the list), so we need to protect it. */ incrRefCount(key); /* Remove the key from the list of keys this client is waiting for. */ listRewind(c->io_keys,&li); while ((ln = listNext(&li)) != NULL) { if (equalStringObjects(ln->value,key)) { listDelNode(c->io_keys,ln); break; } } redisAssert(ln != NULL); /* Remove the client form the key => waiting clients map. */ de = dictFind(c->db->io_keys,key); redisAssert(de != NULL); l = dictGetEntryVal(de); ln = listSearchKey(l,c); redisAssert(ln != NULL); listDelNode(l,ln); if (listLength(l) == 0) dictDelete(c->db->io_keys,key); decrRefCount(key); return listLength(c->io_keys) == 0; }
/* 取消客户端所订阅的所有模式,最后返回退订的模式数量 */ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify) { listNode *ln; listIter li; int count = 0; // 获取c->pubsub_patterns链表迭代器 listRewind(c->pubsub_patterns,&li); // 遍历客户端订阅的模式链表,逐一退订 while ((ln = listNext(&li)) != NULL) { robj *pattern = ln->value; // 统计退订的模式数量 count += pubsubUnsubscribePattern(c,pattern,notify); } // 如果count == 0,说明客户端没有订阅任何模式,回复客户端 if (notify && count == 0) { /* We were subscribed to nothing? Still reply to the client. */ addReply(c,shared.mbulkhdr[3]); addReply(c,shared.punsubscribebulk); addReply(c,shared.nullbulk); addReplyLongLong(c,dictSize(c->pubsub_channels)+ listLength(c->pubsub_patterns)); } return count; }
/* Duplicate the whole list. On out of memory NULL is returned. * On success a copy of the original list is returned. * * The 'Dup' method set with listSetDupMethod() function is used * to copy the node value. Otherwise the same pointer value of * the original node is used as value of the copied node. * * The original list both on success or error is never modified. */ list *listDup(list *orig) { list *copy; listIter iter; listNode *node; if ((copy = listCreate()) == NULL) return NULL; copy->dup = orig->dup; copy->free = orig->free; copy->match = orig->match; listRewind(orig, &iter); while((node = listNext(&iter)) != NULL) { void *value; if (copy->dup) { value = copy->dup(node->value); if (value == NULL) { listRelease(copy); return NULL; } } else value = node->value; if (listAddNodeTail(copy, value) == NULL) { listRelease(copy); return NULL; } } return copy; }
void dictCounterDestructor(void *privdata, void *val) { DICT_NOTUSED(privdata); listNode *ln; listIter li; counter *cntr = val; pubsub *p; /* Unsubscribe everyone */ if (cntr->subscribers != NULL) { while (listLength(cntr->subscribers)) { ln = listFirst(cntr->subscribers); p = listNodeValue(ln); pubsubUnsubscribeCounter(p->c,cntr->name,1); } } /* Free all shards */ listRewind(cntr->shards,&li); while ((ln = listNext(&li)) != NULL) { shard *shrd = listNodeValue(ln); zfree(shrd); } listRelease(cntr->shards); if (cntr->want_acks) dictRelease(cntr->want_acks); /* cntr->name will be freed by the dict code. */ zfree(cntr); }
void countersSync(clusterNode *node) { dictIterator *it; dictEntry *de; it = dictGetIterator(server.counters); while ((de = dictNext(it)) != NULL) { counter *cntr; listNode *ln; listIter li; shard *shrd; cntr = dictGetVal(de); /* If we have our own shard make sure to send it's prediction * to the new node. */ if (cntr->myshard) { clusterSendShardToNode(cntr, node); } listRewind(cntr->shards,&li); while ((ln = listNext(&li)) != NULL) { shrd = listNodeValue(ln); if (memcmp(shrd->node_name, node->name, CLUSTER_NAMELEN) == 0) { shrd->node = node; } } } dictReleaseIterator(it); }
void counterPubSub(counter *cntr, mstime_t now) { listNode *ln; listIter li; if (cntr->subscribers == NULL) return; listRewind(cntr->subscribers,&li); while ((ln = listNext(&li)) != NULL) { pubsub *p = listNodeValue(ln); if (p->next > now) { continue; } if (p->lastvalue == cntr->value) { continue; } addReply(p->c,shared.mbulkhdr[3]); addReply(p->c,shared.messagebulk); addReplyBulkCBuffer(p->c,cntr->name,sdslen(cntr->name)); addReplyLongDouble(p->c, cntr->value); p->next = now + p->seconds*1000; p->lastvalue = cntr->value; } }
/* convert a linked list encoding to a list array encoding */ cowListArray *cowConvertListToArray(list *olist) { listIter li; listNode *ln; cowListArray *lar; listNode *lnNew; listNode *lnPrev; unsigned int ix = 0; lar = (cowListArray *)zmalloc(sizeof(cowListArray) + (sizeof(listNode) * olist->len)); /* add copy of each item from old list */ listRewind(olist,&li); lnNew = &lar->le[0]; lnPrev = NULL; while((ln = listNext(&li)) && ix < olist->len) { /* copy object value to array list Do not incr ref count. */ lnNew->value = listNodeValue(ln); lnNew->prev = lnPrev; if (lnPrev != NULL) { lnPrev->next = lnNew; } lnPrev = lnNew; lnNew++; ix++; } if (lnPrev != NULL) { lnPrev->next = NULL; } lar->numele = ix; return lar; }
void closeTimedoutClients(void) { redisClient *c; listNode *ln; time_t now = time(NULL); listIter li; listRewind(server.clients,&li); while ((ln = listNext(&li)) != NULL) { c = listNodeValue(ln); if (server.maxidletime && !(c->flags & REDIS_SLAVE) && /* no timeout for slaves */ !(c->flags & REDIS_MASTER) && /* no timeout for masters */ !(c->flags & REDIS_BLOCKED) && /* no timeout for BLPOP */ dictSize(c->pubsub_channels) == 0 && /* no timeout for pubsub */ listLength(c->pubsub_patterns) == 0 && (now - c->lastinteraction > server.maxidletime)) { redisLog(REDIS_VERBOSE,"Closing idle client"); freeClient(c); } else if (c->flags & REDIS_BLOCKED) { if (c->blockingto != 0 && c->blockingto < now) { addReply(c,shared.nullmultibulk); unblockClientWaitingData(c); } } } }
int closeTimedoutClients(aeEventLoop *el) { if(el->myid != 0) { httpClient *c; int deletedNodes = 0; time_t now = time(NULL); listIter li; listNode *ln; listRewind(el->clients,&li); while ((ln = listNext(&li)) != NULL) { c = listNodeValue(ln); if (el->maxidletime && (now - c->lastinteraction > el->maxidletime)) { /* the client is waiting for reply */ if (c->blocked) { /* This situation happens when request_handler time exceeds client timeout. * Client timeout is typically 30 seconds and * Request_handler rarely consumes more than 1 second. * This rare case has a very small role in overall performance. */ listNode *ln = listSearchKey(c->ceList,c); if(ln) listDelNode(c->ceList,ln); } freeClient(c); deletedNodes++; } else break; } return deletedNodes; } return 0; }
static void glueReplyBuffersIfNeeded(redisClient *c) { int copylen = 0; char buf[GLUEREPLY_UP_TO]; listNode *ln; listIter li; robj *o; listRewind(c->reply,&li); while((ln = listNext(&li))) { int objlen; o = ln->value; objlen = sdslen(o->ptr); if (copylen + objlen <= GLUEREPLY_UP_TO) { memcpy(buf+copylen,o->ptr,objlen); copylen += objlen; listDelNode(c->reply,ln); } else { if (copylen == 0) return; break; } } /* Now the output buffer is empty, add the new single element */ o = createObject(REDIS_STRING,sdsnewlen(buf,copylen)); listAddNodeHead(c->reply,o); }
void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc) { listNode *ln; listIter li; int j; sds cmdrepr = sdsnew("+"); robj *cmdobj; struct timeval tv; gettimeofday(&tv,NULL); cmdrepr = sdscatprintf(cmdrepr,"%ld.%06ld ",(long)tv.tv_sec,(long)tv.tv_usec); if (dictid != 0) cmdrepr = sdscatprintf(cmdrepr,"(db %d) ", dictid); for (j = 0; j < argc; j++) { if (argv[j]->encoding == REDIS_ENCODING_INT) { cmdrepr = sdscatprintf(cmdrepr, "\"%ld\"", (long)argv[j]->ptr); } else { cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr, sdslen(argv[j]->ptr)); } if (j != argc-1) cmdrepr = sdscatlen(cmdrepr," ",1); } cmdrepr = sdscatlen(cmdrepr,"\r\n",2); cmdobj = createObject(REDIS_STRING,cmdrepr); listRewind(monitors,&li); while((ln = listNext(&li))) { redisClient *monitor = ln->value; addReply(monitor,cmdobj); } decrRefCount(cmdobj); }
void countersUpdateValues(void) { dictIterator *it; dictEntry *de; mstime_t now = mstime(); it = dictGetIterator(server.counters); while ((de = dictNext(it)) != NULL) { long double elapsed, value = 0; counter *cntr; listNode *ln; listIter li; shard *shrd; cntr = dictGetVal(de); listRewind(cntr->shards,&li); while ((ln = listNext(&li)) != NULL) { shrd = listNodeValue(ln); /* Don't do a prediction with our own shard. */ if (shrd == cntr->myshard) { value += shrd->value; continue; } /* Don't update predictions for failing nodes. */ if (shrd->node == NULL || nodeFailed(shrd->node)) { /* Leave the prediction as it is. */ /* TODO: Since this function is called 10 times per second we can't really do any debug output here. */ /*serverLog(LL_DEBUG,"Counter %s not updating shard of %.40s", cntr->name, shrd->node_name);*/ } else if (shrd->predict_time > 0 && shrd->predict_value != 0) { elapsed = now - shrd->predict_time; shrd->value = shrd->predict_value + (elapsed * shrd->predict_change); /*serverLog(LL_DEBUG,"Counter %s new value %Lf for shard %.40s", cntr->name, shrd->value, shrd->node_name); } else { serverLog(LL_DEBUG,"Counter %s not using shard of %.40s %llu %Lf", cntr->name, shrd->node_name, shrd->predict_time, shrd->predict_value);*/ } value += shrd->value; } if (cntr->value != value) { cntr->value = value; /* Make sure the cached response gets recalculated. */ cntr->rlen = 0; } } dictReleaseIterator(it); }
/* Publish a message */ int smempubsubPublishMessage(robj *channel, robj *message) { int receivers = 0; dictEntry *de; listNode *ln; listIter li; int mem_id = -1; struct smem_t * smem_p = NULL; listNode *lnode = NULL; long long ll_var; /* Get the share memory id */ if(getLongLongFromObject(message,&ll_var) == C_OK){ //serverLog(LL_WARNING,"[smempubsubPublishMessage] get share memory id: %lld", ll_var); mem_id = ll_var; // get the item from list lnode = listSearchKey(server.smem_list_used, &mem_id); if(lnode){ smem_p = lnode->value; }else{ //serverLog(LL_WARNING,"[smempubsubPublishMessage] not found the id(%d) in list.", mem_id); mem_id = -1; } } /* Send to clients listening for that channel */ de = dictFind(server.smempubsub_channels,channel); if (de) { list *list = dictGetVal(de); listNode *ln; listIter li; listRewind(list,&li); while ((ln = listNext(&li)) != NULL) { client *c = ln->value; addReply(c,shared.mbulkhdr[3]); addReply(c,shared.messagebulk); addReplyBulk(c,channel); addReplyBulk(c,message); receivers++; // if message is share memory id, should increase use count if(lnode){ smem_p->cnt++; //serverLog(LL_WARNING,"[smempubsubPublishMessage] receivers:%d, smem_p->cnt=%d.", receivers, smem_p->cnt); } } } return receivers; }
void replicationCron(void) { /* Bulk transfer I/O timeout? */ if (server.masterhost && server.replstate == REDIS_REPL_TRANSFER && (time(NULL)-server.repl_transfer_lastio) > REDIS_REPL_TIMEOUT) { redisLog(REDIS_WARNING,"Timeout receiving bulk data from MASTER..."); replicationAbortSyncTransfer(); } /* Timed out master when we are an already connected slave? */ if (server.masterhost && server.replstate == REDIS_REPL_CONNECTED && (time(NULL)-server.master->lastinteraction) > REDIS_REPL_TIMEOUT) { redisLog(REDIS_WARNING,"MASTER time out: no data nor PING received..."); freeClient(server.master); } /* Check if we should connect to a MASTER */ if (server.replstate == REDIS_REPL_CONNECT) { redisLog(REDIS_NOTICE,"Connecting to MASTER..."); if (syncWithMaster() == REDIS_OK) { redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started: SYNC sent"); if (server.appendonly) rewriteAppendOnlyFileBackground(); } } /* If we have attached slaves, PING them from time to time. * So slaves can implement an explicit timeout to masters, and will * be able to detect a link disconnection even if the TCP connection * will not actually go down. */ if (!(server.cronloops % (REDIS_REPL_PING_SLAVE_PERIOD*10))) { listIter li; listNode *ln; listRewind(server.slaves,&li); while((ln = listNext(&li))) { redisClient *slave = ln->value; /* Don't ping slaves that are in the middle of a bulk transfer * with the master for first synchronization. */ if (slave->replstate == REDIS_REPL_SEND_BULK) continue; if (slave->replstate == REDIS_REPL_ONLINE) { /* If the slave is online send a normal ping */ addReplySds(slave,sdsnew("PING\r\n")); } else { /* Otherwise we are in the pre-synchronization stage. * Just a newline will do the work of refreshing the * connection last interaction time, and at the same time * we'll be sure that being a single char there are no * short-write problems. */ write(slave->fd, "\n", 1); } } } }