void rpoplpushCommand(redisClient *c) { robj *sobj, *value; if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,sobj,REDIS_LIST)) return; if (listTypeLength(sobj) == 0) { addReply(c,shared.nullbulk); } else { robj *dobj = lookupKeyWrite(c->db,c->argv[2]); robj *touchedkey = c->argv[1]; if (dobj && checkType(c,dobj,REDIS_LIST)) return; value = listTypePop(sobj,REDIS_TAIL); /* We saved touched key, and protect it, since rpoplpushHandlePush * may change the client command argument vector. */ incrRefCount(touchedkey); rpoplpushHandlePush(c,c,c->argv[2],dobj,value); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); /* Delete the source list when it is empty */ if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey); signalModifiedKey(c->db,touchedkey); decrRefCount(touchedkey); server.dirty++; } }
void rpoplpushCommand(redisClient *c) { robj *sobj, *value; if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,sobj,REDIS_LIST)) return; if (listTypeLength(sobj) == 0) { /* This may only happen after loading very old RDB files. Recent * versions of Redis delete keys of empty lists. */ addReply(c,shared.nullbulk); } else { robj *dobj = lookupKeyWrite(c->db,c->argv[2]); robj *touchedkey = c->argv[1]; if (dobj && checkType(c,dobj,REDIS_LIST)) return; value = listTypePop(sobj,REDIS_TAIL); /* We saved touched key, and protect it, since rpoplpushHandlePush * may change the client command argument vector (it does not * currently). */ incrRefCount(touchedkey); rpoplpushHandlePush(c,c->argv[2],dobj,value); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); /* Delete the source list when it is empty */ if (listTypeLength(sobj) == 0) { dbDelete(c->db,touchedkey); } signalModifiedKey(c->db,touchedkey); decrRefCount(touchedkey); } }
/* This should be called from any function PUSHing into lists. * 'c' is the "pushing client", 'key' is the key it is pushing data against, * 'ele' is the element pushed. * * If the function returns 0 there was no client waiting for a list push * against this key. * * If the function returns 1 there was a client waiting for a list push * against this key, the element was passed to this client thus it's not * needed to actually add it to the list and the caller should return asap. */ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) { struct dictEntry *de; redisClient *receiver; int numclients; list *clients; listNode *ln; robj *dstkey, *dstobj; de = dictFind(c->db->blocking_keys,key); if (de == NULL) return 0; clients = dictGetVal(de); numclients = listLength(clients); /* Try to handle the push as long as there are clients waiting for a push. * Note that "numclients" is used because the list of clients waiting for a * push on "key" is deleted by unblockClient() when empty. * * This loop will have more than 1 iteration when there is a BRPOPLPUSH * that cannot push the target list because it does not contain a list. If * this happens, it simply tries the next client waiting for a push. */ while (numclients--) { ln = listFirst(clients); redisAssertWithInfo(c,key,ln != NULL); receiver = ln->value; dstkey = receiver->bpop.target; /* Protect receiver->bpop.target, that will be freed by * the next unblockClientWaitingData() call. */ if (dstkey) incrRefCount(dstkey); /* This should remove the first element of the "clients" list. */ unblockClientWaitingData(receiver); if (dstkey == NULL) { /* BRPOP/BLPOP */ addReplyMultiBulkLen(receiver,2); addReplyBulk(receiver,key); addReplyBulk(receiver,ele); return 1; /* Serve just the first client as in B[RL]POP semantics */ } else { /* BRPOPLPUSH, note that receiver->db is always equal to c->db. */ dstobj = lookupKeyWrite(receiver->db,dstkey); if (!(dstobj && checkType(receiver,dstobj,REDIS_LIST))) { rpoplpushHandlePush(c,receiver,dstkey,dstobj,ele); decrRefCount(dstkey); return 1; } decrRefCount(dstkey); } } return 0; }
// RPOPLPUSH source destination // RPOPLPUSH命令的实现 void rpoplpushCommand(client *c) { robj *sobj, *value; //以写操作读取source对象的值,并且检查数据类型是否为OBJ_LIST if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,sobj,OBJ_LIST)) return; //如果列表长度为0,没有元素,直接发送空信息 if (listTypeLength(sobj) == 0) { /* This may only happen after loading very old RDB files. Recent * versions of Redis delete keys of empty lists. */ addReply(c,shared.nullbulk); } else { //以写操作读取destination对象的值 robj *dobj = lookupKeyWrite(c->db,c->argv[2]); robj *touchedkey = c->argv[1]; //将source键备份 //如果目标对象类型是否是列表类型 if (dobj && checkType(c,dobj,OBJ_LIST)) return; //从source尾部弹出一个元素 value = listTypePop(sobj,LIST_TAIL); /* We saved touched key, and protect it, since rpoplpushHandlePush * may change the client command argument vector (it does not * currently). */ //备份一份source键,因为rpoplpushHandlePush可能会更改client命令行参数 incrRefCount(touchedkey); //将一个value推入到destination列表头部,如果destination列表不存在,则新创建一个 rpoplpushHandlePush(c,c->argv[2],dobj,value); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); //将弹出的value释放 /* Delete the source list when it is empty */ //发送"rpop"时间通知 notifyKeyspaceEvent(NOTIFY_LIST,"rpop",touchedkey,c->db->id); //如果source列表为空了,则删除key if (listTypeLength(sobj) == 0) { //删除之前备份的key键 dbDelete(c->db,touchedkey); //发送"rpop"时间通知 notifyKeyspaceEvent(NOTIFY_GENERIC,"del", touchedkey,c->db->id); } //当数据库的键被改动,则会调用该函数发送信号 signalModifiedKey(c->db,touchedkey); //释放备份的source键 decrRefCount(touchedkey); //脏键加1 server.dirty++; } }
/* This is a helper function for handleClientsBlockedOnLists(). It's work * is to serve a specific client (receiver) that is blocked on 'key' * in the context of the specified 'db', doing the following: * * 1) Provide the client with the 'value' element. * 2) If the dstkey is not NULL (we are serving a BRPOPLPUSH) also push the * 'value' element on the destination list (the LPUSH side of the command). * 3) Propagate the resulting BRPOP, BLPOP and additional LPUSH if any into * the AOF and replication channel. * * The argument 'where' is REDIS_TAIL or REDIS_HEAD, and indicates if the * 'value' element was popped fron the head (BLPOP) or tail (BRPOP) so that * we can propagate the command properly. * * The function returns REDIS_OK if we are able to serve the client, otherwise * REDIS_ERR is returned to signal the caller that the list POP operation * should be undone as the client was not served: This only happens for * BRPOPLPUSH that fails to push the value to the destination key as it is * of the wrong type. */ int serveClientBlockedOnList(redisClient *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where) { robj *argv[3]; if (dstkey == NULL) { /* Propagate the [LR]POP operation. */ argv[0] = (where == REDIS_HEAD) ? shared.lpop : shared.rpop; argv[1] = key; propagate((where == REDIS_HEAD) ? server.lpopCommand : server.rpopCommand, db->id,argv,2,REDIS_PROPAGATE_AOF|REDIS_PROPAGATE_REPL); /* BRPOP/BLPOP */ addReplyMultiBulkLen(receiver,2); addReplyBulk(receiver,key); addReplyBulk(receiver,value); } else { /* BRPOPLPUSH */ robj *dstobj = lookupKeyWrite(receiver->db,dstkey); if (!(dstobj && checkType(receiver,dstobj,REDIS_LIST))) { /* Propagate the RPOP operation. */ argv[0] = shared.rpop; argv[1] = key; propagate(server.rpopCommand, db->id,argv,2, REDIS_PROPAGATE_AOF| REDIS_PROPAGATE_REPL); rpoplpushHandlePush(receiver,dstkey,dstobj, value); /* Propagate the LPUSH operation. */ argv[0] = shared.lpush; argv[1] = dstkey; argv[2] = value; propagate(server.lpushCommand, db->id,argv,3, REDIS_PROPAGATE_AOF| REDIS_PROPAGATE_REPL); } else { /* BRPOPLPUSH failed because of wrong * destination type. */ return REDIS_ERR; } } return REDIS_OK; }
void rpoplpushCommand(redisClient *c) { robj *sobj, *value; if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,sobj,REDIS_LIST)) return; if (listTypeLength(sobj) == 0) { addReply(c,shared.nullbulk); } else { robj *dobj = lookupKeyWrite(c->db,c->argv[2]); if (dobj && checkType(c,dobj,REDIS_LIST)) return; value = listTypePop(sobj,REDIS_TAIL); rpoplpushHandlePush(c,c->argv[2],dobj,value); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); /* Delete the source list when it is empty */ if (listTypeLength(sobj) == 0) dbDelete(c->db,c->argv[1]); touchWatchedKey(c->db,c->argv[1]); server.dirty++; } }
void rpoplpushCommand(redisClient *c) { robj *sobj, *value; if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,sobj,REDIS_LIST)) return; if (listTypeLength(sobj) == 0) { /* This may only happen after loading very old RDB files. Recent * versions of Redis delete keys of empty lists. */ addReply(c,shared.nullbulk); } else { robj *dobj = lookupKeyWrite(c->db,c->argv[2]); robj *touchedkey = c->argv[1]; if (dobj && checkType(c,dobj,REDIS_LIST)) return; value = listTypePop(sobj,REDIS_TAIL); /* We saved touched key, and protect it, since rpoplpushHandlePush * may change the client command argument vector. */ incrRefCount(touchedkey); rpoplpushHandlePush(c,c,c->argv[2],dobj,value); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); /* Delete the source list when it is empty */ if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey); signalModifiedKey(c->db,touchedkey); decrRefCount(touchedkey); server.dirty++; /* Replicate this as a simple RPOP since the LPUSH side is replicated * by rpoplpushHandlePush() call if needed (it may not be needed * if a client is blocking wait a push against the list). */ rewriteClientCommandVector(c,2, resetRefCount(createStringObject("RPOP",4)), c->argv[1]); } }
//receiver是被阻塞的客户端,key是造成阻塞的键,db是key所在的数据库,value是被提供给客户端的值 //如果dstkey不为空,则将value推入到dstkey中 int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where) { robj *argv[3]; //如果dstkey为空,则执行的是BLPOP或BRPOP if (dstkey == NULL) { /* Propagate the [LR]POP operation. */ //根据where判断出是LPOP还是RPOP命令 argv[0] = (where == LIST_HEAD) ? shared.lpop : shared.rpop; //弹出元素的key argv[1] = key; //将[LR]POP命令传播到AOF和REPL propagate((where == LIST_HEAD) ? server.lpopCommand : server.rpopCommand, db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL); /* BRPOP/BLPOP */ //发送回复信息 addReplyMultiBulkLen(receiver,2); addReplyBulk(receiver,key); addReplyBulk(receiver,value); //dstkey不为空,执行BRPOPLPUSH命令 } else { /* BRPOPLPUSH */ //以读操作将dstkey对象的值读出来 robj *dstobj = lookupKeyWrite(receiver->db,dstkey); //如果dstobj对象是列表类型,将BRPOPLPUSH命令分为RPOP和LPUSH分别处理 if (!(dstobj && checkType(receiver,dstobj,OBJ_LIST))) { /* Propagate the RPOP operation. */ //保存RPOP命令和被弹出元素的键 argv[0] = shared.rpop; argv[1] = key; //将RPOP命令传播到AOF和REPL propagate(server.rpopCommand, db->id,argv,2, PROPAGATE_AOF| PROPAGATE_REPL); //将一个value推入到目标列表dstobj头部 rpoplpushHandlePush(receiver,dstkey,dstobj, value); /* Propagate the LPUSH operation. */ //保存LPOP命令和目标键和弹出元素的键 argv[0] = shared.lpush; argv[1] = dstkey; argv[2] = value; //将LPUSH命令传播到AOF和REPL propagate(server.lpushCommand, db->id,argv,3, PROPAGATE_AOF| PROPAGATE_REPL); } else { /* BRPOPLPUSH failed because of wrong * destination type. */ return C_ERR; } } return C_OK; }