/* Unblock a client that's waiting in a blocking operation such as BLPOP */ void unblockClientWaitingData(redisClient *c) { dictEntry *de; list *l; int j; redisAssert(c->blocking_keys != NULL); /* The client may wait for multiple keys, so unblock it for every key. */ for (j = 0; j < c->blocking_keys_num; j++) { /* Remove this client from the list of clients waiting for this key. */ de = dictFind(c->db->blocking_keys,c->blocking_keys[j]); redisAssert(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->blocking_keys,c->blocking_keys[j]); decrRefCount(c->blocking_keys[j]); } /* Cleanup the client structure */ zfree(c->blocking_keys); c->blocking_keys = 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); }
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { redisClient *c = (redisClient*) privdata; char buf[REDIS_IOBUF_LEN]; int nread; REDIS_NOTUSED(el); REDIS_NOTUSED(mask); nread = read(fd, buf, REDIS_IOBUF_LEN); if (nread == -1) { if (errno == EAGAIN) { nread = 0; } else { redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno)); freeClient(c); return; } } else if (nread == 0) { redisLog(REDIS_VERBOSE, "Client closed connection"); freeClient(c); return; } if (nread) { c->querybuf = sdscatlen(c->querybuf, buf, nread); c->lastinteraction = time(NULL); } else { return; } processInputBuffer(c); }
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { client *c = (client*) privdata; int nread, readlen; size_t qblen; UNUSED(el); UNUSED(mask); readlen = PROTO_IOBUF_LEN; /* If this is a multi bulk request, and we are processing a bulk reply * that is large enough, try to maximize the probability that the query * buffer contains exactly the SDS string representing the object, even * at the risk of requiring more read(2) calls. This way the function * processMultiBulkBuffer() can avoid copying buffers to create the * Redis Object representing the argument. */ if (c->reqtype == PROTO_REQ_MULTIBULK && c->multibulklen && c->bulklen != -1 && c->bulklen >= PROTO_MBULK_BIG_ARG) { int remaining = (unsigned)(c->bulklen+2)-sdslen(c->querybuf); if (remaining < readlen) readlen = remaining; } qblen = sdslen(c->querybuf); if (c->querybuf_peak < qblen) c->querybuf_peak = qblen; c->querybuf = sdsMakeRoomFor(c->querybuf, readlen); nread = read(fd, c->querybuf+qblen, readlen); if (nread == -1) { if (errno == EAGAIN) { return; } else { Log(LL_VERBOSE, "Reading from client: %s",strerror(errno)); freeClient(c); return; } } else if (nread == 0) { Log(LL_VERBOSE, "Client closed connection"); freeClient(c); return; } sdsIncrLen(c->querybuf,nread); c->lastinteraction = server.unixtime; server.stat_net_input_bytes += nread; if (sdslen(c->querybuf) > server.client_max_querybuf_len) { sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty(); bytes = sdscatrepr(bytes,c->querybuf,64); Log(LL_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes); sdsfree(ci); sdsfree(bytes); freeClient(c); return; } processInputBuffer(c); }
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { vuiClient *c = (vuiClient *) privdata; int nread, readlen; size_t qblen; /* If this is a multi bulk request, and we are processing a bulk reply * that is large enough, try to maximize the probability that the query * buffer contains exactly the SDS string representing the object, even * at the risk of requiring more read(2) calls. This way the function * processMultiBulkBuffer() can avoid copying buffers to create the * Redis Object representing the argument. */ if (c->prot.waiting) { readlen = c->prot.waiting; } else { readlen = 1024 * 2; } qblen = sdslen(c->querybuf); c->querybuf = sdsMakeRoomFor(c->querybuf, readlen); nread = read(fd, c->querybuf+qblen, readlen); if (nread == -1) { if (errno == EAGAIN) { nread = 0; } else { LogInfo("Reading from client: %s",strerror(errno)); freeClient(c); return; } } else if (nread == 0) { LogInfo("Client closed connection"); freeClient(c); return; } if (nread) { sdsIncrLen(c->querybuf,nread); } //TODO if (sdslen(c->querybuf) > 1024 * 1024) { LogWarn("Closing client that reached max query buffer length"); sdsclear(c->querybuf); freeClient(c); return; } processInputBuffer(c); }
/* This function is called in the beforeSleep() function of the event loop * in order to process the pending input buffer of clients that were * unblocked after a blocking operation. * 取消所有在 unblocked_clients 链表中的客户端的阻塞状态 * */ void processUnblockedClients(void) { listNode *ln; redisClient *c; while (listLength(server.unblocked_clients)) { ln = listFirst(server.unblocked_clients); redisAssert(ln != NULL); c = ln->value; listDelNode(server.unblocked_clients,ln); c->flags &= ~REDIS_UNBLOCKED; c->btype = REDIS_BLOCKED_NONE; /* Process remaining data in the input buffer. */ if (c->querybuf && sdslen(c->querybuf) > 0) { server.current_client = c; processInputBuffer(c); server.current_client = NULL; } } }
/* This function is called in the beforeSleep() function of the event loop * in order to process the pending input buffer of clients that were * unblocked after a blocking operation. */ void processUnblockedClients(void) { listNode *ln; client *c; while (listLength(server.unblocked_clients)) { ln = listFirst(server.unblocked_clients); serverAssert(ln != NULL); c = ln->value; listDelNode(server.unblocked_clients,ln); c->flags &= ~CLIENT_UNBLOCKED; /* Process remaining data in the input buffer, unless the client * is blocked again. Actually processInputBuffer() checks that the * client is not blocked before to proceed, but things may change and * the code is conceptually more correct this way. */ if (!(c->flags & CLIENT_BLOCKED)) { if (c->querybuf && sdslen(c->querybuf) > 0) { processInputBuffer(c); } } } }
/* This function is called in the beforeSleep() function of the event loop * in order to process the pending input buffer of clients that were * unblocked after a blocking operation. */ void processUnblockedClients(void) { listNode *ln; client *c; while (listLength(server.unblocked_clients)) { ln = listFirst(server.unblocked_clients); serverAssert(ln != NULL); c = ln->value; listDelNode(server.unblocked_clients,ln); c->flags &= ~CLIENT_UNBLOCKED; /* Note that the client may be blocked again at this point, since * a new blocking command was processed. In that case we just remove * it from the unblocked clients list without actually processing * its pending query buffer. */ if (!(c->flags & CLIENT_BLOCKED)) { c->btype = BLOCKED_NONE; /* Process remaining data in the input buffer. */ if (c->querybuf && sdslen(c->querybuf) > 0) { processInputBuffer(c); } } } }
/* sid = |flags(4byte)|client fd(4byte)| */ static int smrCbData (void *arg, long long seq, long long timestamp, short nid, int sid, int hash, smrData * smr_data, int size) { char *data = smr_data_get_data (smr_data); redisClient *c; short cmdflags; dlisth *node; /* Special commands */ if (size == 1 && data[0] == REDIS_SMR_CMD_CATCHUP_CHECK) { if (nid == smr_get_nid (server.smr_conn) && (server.smr_init_flags & SMR_INIT_CATCHUP_PHASE2)) checkSmrCatchup (); goto release_seq; } else if (size == 1 && data[0] == REDIS_SMR_CMD_DELIVER_OOM) { server.smr_oom_until = timestamp + REDIS_OOM_DURATION_MS; goto release_seq; } /* Normal command */ server.smr_mstime = timestamp; cmdflags = (0xFFFF0000 & sid) >> 16; /* Because we fixed the bug that causes timestamp be 0, * this warning log is not necessary and just an assert statement is enough after all. * But currently, there are nbase-arc clusters which have the bug. * So, we don't assert for now. */ if (timestamp == 0) { redisLog (REDIS_WARNING, "Timestamp of SMR callback is 0," "seq:%lld, nid:%d, sid:%d, hash:%d, size:%d", seq, nid, sid, hash, size); } if ((server.smr_init_flags & SMR_INIT_DONE) && nid == smr_get_nid (server.smr_conn)) { callbackInfo *cb; /* query from this server. get client from global callback list */ assert (!dlisth_is_empty (&server.global_callbacks)); /* global callback list shouldn't be empty */ node = server.global_callbacks.next; cb = (callbackInfo *) node; dlisth_delete (&cb->global_head); dlisth_delete (&cb->client_head); c = cb->client; assert (cb->hash == hash); assert (c->fd == -1 || c->fd == (0X0000FFFF & sid)); /* We already parsed querybuf because the query is requested from this * server before smr callback*/ c->argc = cb->argc; c->argv = cb->argv; cb->argc = 0; cb->argv = NULL; zfree (cb); server.current_client = c; /* fake client doesn't need to execute non-write query(read-only or admin) */ if (c->fd != -1 || cmdflags & REDIS_CMD_WRITE) { assert (!(c->flags & REDIS_CLOSE_AFTER_REPLY)); processCommand (c); } } else { /* replicated query from other servers, or catchup query during initialize */ c = server.smrlog_client; server.current_client = c; /* fake client doesn't need to execute non-write query(read-only or admin) */ if (cmdflags & REDIS_CMD_WRITE) { /* We need to parse querybuf because the query is from different server * or we are recovering without client */ sdsclear (c->querybuf); c->querybuf = sdsMakeRoomFor (c->querybuf, size); memcpy (c->querybuf, data, size); sdsIncrLen (c->querybuf, size); if (c->querybuf_peak < size) c->querybuf_peak = size; processInputBuffer (c); } } resetClient (c); zfree (c->argv); c->argv = NULL; server.current_client = NULL; release_seq: if (smr_release_seq_upto (server.smr_conn, seq + size) == -1) { redisLog (REDIS_WARNING, "smr_release_seq_upto error"); redisAssert (0); return REDIS_ERR; } server.smr_seqnum = seq + size; return REDIS_OK; }
void readFromMaster(aeEventLoop *el, int fd, void *privdata, int mask) { REDIS_NOTUSED(el); REDIS_NOTUSED(mask); int nread; char rdata[MAX_READ]; char *p = rdata; switch(state){ case SYNC_START_STATE:{ while(1){ nread = readData(el, fd, p,1); if(nread < 0){ return; } else if(nread == 0){ continue; } int wr = writeData(el, client_fd, p, 1); if(-1 == wr) { return; } if(*p == '\n' && p != rdata) break; if(*p != '\n') p++; } *p = '\0'; if(rdata[0] == '-'){ log4c_category_log(mycat, LOG4C_PRIORITY_ERROR, "sync with master failed at line %d in file %s", __LINE__, __FILE__); exit(1); } bulk_size = strtoull(rdata + 1, 0, 10); log4c_category_log(mycat, LOG4C_PRIORITY_INFO, "bulk size %llu, at line %d in file %s", bulk_size, __LINE__, __FILE__); state = SYNC_STATE; break; } case SYNC_STATE:{ long long int all_read =0; long long int all_write =0; while(bulk_size){ nread = readData(el, fd, rdata, (bulk_size > MAX_READ) ? MAX_READ:bulk_size); if(nread < 0 ) { return; } else if (nread == 0){ continue; } else{ all_read += nread; } int wr = writeData(el, client_fd, rdata,nread); if(-1 == wr) { log4c_category_log(mycat, LOG4C_PRIORITY_ERROR, "write to payload failed error %d, at line %d in file %s", errno, __LINE__, __FILE__); return; } else{ all_write += wr; } bulk_size -= nread; } state = RUNNING_STATE; log4c_category_log(mycat, LOG4C_PRIORITY_INFO, "sync done"); break; } default:{ nread = readData(el, fd, rdata, MAX_READ); if(nread > 0){ struct WData wdata; wdata.len = nread; wdata.data= rdata; if( state == RUNNING_STATE){ processInputBuffer(el, &wdata); int wr = writeData(el, client_fd, wdata.data, wdata.len); if(-1 == wr) { return; } } else{ if(nread > 9 && nread < 100) { if(!strncmp(rdata,"+CONTINUE",9) || !strncmp(rdata,"+FULLRESYNC",11)){ log4c_category_log(mycat, LOG4C_PRIORITY_INFO, "sync start"); state = SYNC_START_STATE; } } int wr = writeData(el, client_fd, wdata.data, wdata.len); if(-1 == wr) { return; } } } break; } } }
static void * clientThread (void *arg) { client_t *c = (client_t *) arg; fd_set rfds, wfds; int ret; c->querybuf = sdsMakeRoomFor (sdsempty (), DEFAULT_QUERY_BUF_SIZE); c->replybuf = sdsMakeRoomFor (sdsempty (), DEFAULT_QUERY_BUF_SIZE); c->argc = 0; c->argv = NULL; c->argvlen = NULL; c->reqtype = 0; c->multibulklen = 0; c->bulklen = -1; c->rqst = arc_create_request (); c->flags = 0; c->total_append_command = 0; FD_ZERO (&rfds); FD_ZERO (&wfds); while (1) { struct timeval timeout; FD_CLR (c->fd, &rfds); FD_CLR (c->fd, &wfds); if (!(c->flags & REDIS_CLOSE_AFTER_REPLY)) FD_SET (c->fd, &rfds); if (sdslen (c->replybuf) > 0) FD_SET (c->fd, &wfds); timeout.tv_sec = 1; timeout.tv_usec = 0; ret = select (c->fd + 1, &rfds, &wfds, NULL, &timeout); if (ret == -1) { perror ("select"); freeClient (c); } if (server.shutdown_signal) { c->flags |= REDIS_CLOSE_AFTER_REPLY; } /* readable */ if (FD_ISSET (c->fd, &rfds)) { int pos = sdslen (c->querybuf); int avail = sdsavail (c->querybuf); ssize_t nread; if (avail == 0) { c->querybuf = sdsMakeRoomFor (c->querybuf, sdslen (c->querybuf)); avail = sdsavail (c->querybuf); } nread = read (c->fd, c->querybuf + pos, avail); if (nread > 0) { sdsIncrLen (c->querybuf, nread); processInputBuffer (c); if (c->total_append_command) { int arc_errno, arc_be_errno, be_errno; arc_ref_t *arc_ref; arc_ref = acquire_arc_ref (); ret = arc_do_request (arc_ref->arc, c->rqst, server.query_timeout_millis, &be_errno); if (ret == -1) { arc_errno = errno; arc_be_errno = be_errno; } else { ret = processReply (c, &be_errno); if (ret == -1) { arc_errno = errno; arc_be_errno = be_errno; } } arc_free_request (c->rqst); release_arc_ref (arc_ref); c->rqst = arc_create_request (); if (ret == -1) { if (arc_errno == ARC_ERR_TIMEOUT || (arc_errno == ARC_ERR_BACKEND && arc_be_errno == ARC_ERR_TIMEOUT)) { addReplyStr (c, "-ERR Redis Timeout\r\n"); } else { addReplyStr (c, "-ERR Internal Error\r\n"); } c->flags |= REDIS_CLOSE_AFTER_REPLY; } } } else { if (nread == -1 && errno == EAGAIN) { /* Skip */ } else { freeClient (c); } } } /* writable */ if (FD_ISSET (c->fd, &wfds)) { int pos = 0; int avail = sdslen (c->replybuf); ssize_t nwritten; nwritten = write (c->fd, c->replybuf + pos, avail); if (nwritten > 0) { avail -= nwritten; pos += nwritten; sdsrange (c->replybuf, pos, -1); } else { if (nwritten == -1 && errno == EAGAIN) { /* Skip */ } else { freeClient (c); } } } if (sdslen (c->replybuf) == 0 && (c->flags & REDIS_CLOSE_AFTER_REPLY)) { freeClient (c); } } return NULL; }