/* 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;
            }
        }
    }
}
예제 #2
0
// 当数据库执行了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;
            }
        }
    }
}
예제 #3
0
/*
 * 为 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;
            }
        }
    }
}
예제 #4
0
/*	发布一则消息,即将消息发送给所有订阅了指定频道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;
}
예제 #5
0
파일: repl.c 프로젝트: fxding/rwcached
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);
				}
			}
		}
	}
}
예제 #6
0
/* 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);
            }
        }
    }
}
예제 #7
0
/* 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);
            }
        }
    }
}
예제 #8
0
/*
 * 发布一个消息至指定频道
 */
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;
}
예제 #9
0
파일: multi.c 프로젝트: AbsoluteTiger/redis
/* 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);
    }
}
예제 #10
0
파일: multi.c 프로젝트: AbsoluteTiger/redis
/* 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;
}
예제 #12
0
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]);
    }
}
예제 #13
0
파일: mcache.c 프로젝트: truongminh/ccache
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);
}
예제 #14
0
// 取消客户端对所有的键的监视,清理 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);
    }
}
예제 #15
0
/*
 * “碰触”(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;
    }
}
예제 #16
0
// 撤销对这个客户端的所有 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);
    }
}
예제 #17
0
파일: vm.c 프로젝트: ambakshi/redis
/* 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;
}
예제 #18
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;
}
예제 #19
0
파일: adlist.c 프로젝트: yangluoshen/FSM
/* 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;
}
예제 #20
0
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);
}
예제 #21
0
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);
}
예제 #22
0
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;
    }
}
예제 #23
0
파일: win32_cow.c 프로젝트: lrascao/redis
/* 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;
}
예제 #24
0
파일: networking.c 프로젝트: bcg/redis
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);
            }
        }
    }
}
예제 #25
0
파일: client.c 프로젝트: truongminh/ccache
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;
}
예제 #26
0
파일: networking.c 프로젝트: bcg/redis
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);
}
예제 #27
0
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);
}
예제 #28
0
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);
}
예제 #29
0
/* 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;
}
예제 #30
0
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);
            }
        }
    }
}