Redis source code analysis 16-blocking command

Source: Internet
Author: User
Redis currently only supports list blocking operations. The two commands are brpop and blpop. When the two commands have elements in the list, they are no different from ordinary pop. One element of the list is popped up and then returned. However, when the list has no elements, the REDIS_BLOCKED flag is set for the redisClient, and the client is blocked (set the REDIS_BLOCKED mark

Redis currently only supports list blocking operations. The two commands are brpop and blpop. When the two commands have elements in the list, they are no different from ordinary pop. One element of the list is popped up and then returned. However, when the list has no elements, the REDIS_BLOCKED flag is set for the redisClient, and the client is blocked (set the REDIS_BLOCKED mark

Redis currently only supports list blocking operations. The two commands are brpop and blpop.

When the two commands have elements in the list, they are no different from ordinary pop. One element of the list is popped up and then returned. However, when the list has no elements, the REDIS_BLOCKED flag is set for the redisClient, and the client is blocked (the redisClient with the REDIS_BLOCKED flag is blocked all the time, refer to the Command Processing Section ), it will not be returned until new elements are added (pushGenericCommand, the processing function of the push operation.

Both the brpopCommand and blpopCommand functions set by these two commands call blockingPopGenericCommand. This function will call a non-blocking popGenericCommand to pop up an element after checking that there are elements in the list. Otherwise, blockForKeys will be called to handle blocking.

/* Blocking RPOP/LPOP */static void blockingPopGenericCommand(redisClient *c, int where) {    robj *o;    long long lltimeout;    time_t timeout;    int j;    /* Make sure timeout is an integer value */    if (getLongLongFromObjectOrReply(c,c->argv[c->argc-1],&lltimeout,            "timeout is not an integer") != REDIS_OK) return;    /* Make sure the timeout is not negative */    if (lltimeout < 0) {        addReplySds(c,sdsnew("-ERR timeout is negative\r\n"));        return;    }    for (j = 1; j < c->argc-1; j++) {        o = lookupKeyWrite(c->db,c->argv[j]);        if (o != NULL) {            if (o->type != REDIS_LIST) {                addReply(c,shared.wrongtypeerr);                return;            } else {                list *list = o->ptr;                if (listLength(list) != 0) {                    /* If the list contains elements fall back to the usual                     * non-blocking POP operation */                    robj *argv[2], **orig_argv;                    int orig_argc;                    /* We need to alter the command arguments before to call                     * popGenericCommand() as the command takes a single key. */                    orig_argv = c->argv;                    orig_argc = c->argc;                    argv[1] = c->argv[j];                    c->argv = argv;                    c->argc = 2;                    /* Also the return value is different, we need to output                     * the multi bulk reply header and the key name. The                     * "real" command will add the last element (the value)                     * for us. If this souds like an hack to you it's just                     * because it is... */                    addReplySds(c,sdsnew("*2\r\n"));                    addReplyBulk(c,argv[1]);                    popGenericCommand(c,where);                    /* Fix the client structure with the original stuff */                    c->argv = orig_argv;                    c->argc = orig_argc;                    return;                }            }        }    }    /* If we are inside a MULTI/EXEC and the list is empty the only thing     * we can do is treating it as a timeout (even with timeout 0). */    if (c->flags & REDIS_MULTI) {        addReply(c,shared.nullmultibulk);        return;    }    /* If the list is empty or the key does not exists we must block */    timeout = lltimeout;    if (timeout > 0) timeout += time(NULL);    blockForKeys(c,c->argv+1,c->argc-2,timeout);}

BlockForKeys writes down the correspondence between the client and the waiting key in db-> blockingkeys, and then sets the REDIS_BLOCKED flag for the client, so that the client is blocked.

static void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout) {    dictEntry *de;    list *l;    int j;    ---    if (c->fd < 0) return;    c->blockingkeys = zmalloc(sizeof(robj*)*numkeys);    c->blockingkeysnum = numkeys;    c->blockingto = timeout;    for (j = 0; j < numkeys; j++) {        /* Add the key in the client structure, to map clients -> keys */        c->blockingkeys[j] = keys[j];        incrRefCount(keys[j]);        /* And in the other "side", to map keys -> clients */        de = dictFind(c->db->blockingkeys,keys[j]);        if (de == NULL) {            int retval;            /* For every key we take a list of clients blocked for it */            l = listCreate();            retval = dictAdd(c->db->blockingkeys,keys[j],l);            incrRefCount(keys[j]);            assert(retval == DICT_OK);        } else {            l = dictGetEntryVal(de);        }        listAddNodeTail(l,c);    }    /* Mark the client as a blocked client */    c->flags |= REDIS_BLOCKED;    server.blpop_blocked_clients++;}

The waiting client will be blocked until the push operation exists. At this time, unblockClientWaitingData will be called to remove the blocking of the client.

/* Unblock a client that's waiting in a blocking operation such as BLPOP * // reduce the reference to the blocked object static void unblockClientWaitingData (redisClient * c) {dictEntry * de; list * l; int j; assert (c-> blockingkeys! = NULL);/* The client may wait for multiple keys, so unblock it for every key. */for (j = 0; j <c-> blockingkeysnum; j ++) {/* Remove this client from the list of clients waiting for this key. */de = dictFind (c-> db-> blockingkeys, c-> blockingkeys [j]); assert (de! = NULL); l = dictGetEntryVal (de); listDelNode (l, listSearchKey (l, c )); /* If the list is empty we need to remove it to avoid wasting memory */if (listLength (l) = 0) dictDelete (c-> db-> blockingkeys, c-> blockingkeys [j]); decrRefCount (c-> blockingkeys [j]);}/* Cleanup the client structure */zfree (c-> blockingkeys ); c-> blockingkeys = NULL; c-> flags & = (~ REDIS_BLOCKED); server. blpop_blocked_clients --;/* We want to process data if there is some command waiting * in the input buffer. note that this is safe even if * unblockClientWaitingData () gets called from freeClient () because * freeClient () will be smart enough to call this function ** after * c-> querybuf was set to NULL. */if (c-> querybuf & sdslen (c-> querybuf)> 0) processInputBuffer (c );}

Original article address: redis source code analysis 16-blocking command, thanks to the original author for sharing.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.