void processIntegerReply(redisReply *reply, void **buf){ long long integer=reply->integer; sds num=sdsfromlonglong(integer); *buf=sdscatlen(*buf,":",1); *buf=sdscatlen(*buf,num,sdslen(num)); *buf=sdscatlen(*buf,"\\r\\n",4); }
/* * 根据传入的整数值,创建一个字符串对象 * * 这个字符串的对象保存的可以是 INT 编码的 long 值, * 也可以是 RAW 编码的、被转换成字符串的 long long 值。 */ robj *createStringObjectFromLongLong(long long value) { robj *o; // value 的大小符合 REDIS 共享整数的范围 // 那么返回一个共享对象 if (value >= 0 && value < REDIS_SHARED_INTEGERS) { incrRefCount(shared.integers[value]); o = shared.integers[value]; // 不符合共享范围,创建一个新的整数对象 } else { // 值可以用 long 类型保存, // 创建一个 REDIS_ENCODING_INT 编码的字符串对象 if (value >= LONG_MIN && value <= LONG_MAX) { o = createObject(REDIS_STRING, NULL); o->encoding = REDIS_ENCODING_INT; o->ptr = (void*)((long)value); // 值不能用 long 类型保存(long long 类型),将值转换为字符串, // 并创建一个 REDIS_ENCODING_RAW 的字符串对象来保存值 } else { o = createObject(REDIS_STRING,sdsfromlonglong(value)); } } return o; }
/* Convert the set to specified encoding. The resulting dict (when converting * to a hash table) is presized to hold the number of elements in the original * set. */ void setTypeConvert(robj *setobj, int enc) { setTypeIterator *si; serverAssertWithInfo(NULL,setobj,setobj->type == OBJ_SET && setobj->encoding == OBJ_ENCODING_INTSET); if (enc == OBJ_ENCODING_HT) { int64_t intele; dict *d = dictCreate(&setDictType,NULL); sds element; /* Presize the dict to avoid rehashing */ dictExpand(d,intsetLen(setobj->ptr)); /* To add the elements we extract integers and create redis objects */ si = setTypeInitIterator(setobj); while (setTypeNext(si,&element,&intele) != -1) { element = sdsfromlonglong(intele); serverAssert(dictAdd(d,element,NULL) == DICT_OK); } setTypeReleaseIterator(si); setobj->encoding = OBJ_ENCODING_HT; zfree(setobj->ptr); setobj->ptr = d; } else { serverPanic("Unsupported set conversion"); } }
void processStringReply(redisReply *reply,void **buf){ int len=reply->len; *buf=sdscatlen(*buf,"$",1); sds num=sdsfromlonglong(len); *buf=sdscatlen(*buf,num,sdslen(num)); *buf=sdscatlen(*buf,"\\r\\n",4); *buf=sdscatlen(*buf,reply->str,len); *buf=sdscatlen(*buf,"\\r\\n",4); }
value_t *createValueFromLongLong(long long v) { value_t *val; if (v >= LONG_MIN && v <= LONG_MAX) { val = createValue(ENCODING_INT, NULL); val->ptr = (void*) ((long) v); } else { val = createValue(ENCODING_RAW, sdsfromlonglong(v)); } return val; }
value_t *toStringValue(value_t *val) { if (val->encoding == ENCODING_RAW) return val; long long v = (long) val->ptr; sds *p = sdsfromlonglong(v); if (p == NULL) return NULL; val->ptr = p; return val; }
robj *createStringObjectFromLongLong(long long value) { robj *o; if (value >= 0 && value < OBJ_SHARED_INTEGERS) { o = shared.integers[value]; } else { if (value >= LONG_MIN && value <= LONG_MAX) { o = createObject(OBJ_STRING, NULL); o->encoding = OBJ_ENCODING_INT; o->ptr = (void*)((long)value); } else { o = createObject(OBJ_STRING,sdsfromlonglong(value)); } } return o; }
/* ----------------------------------------------------------------------*/ robj *createStringObjectFromLongLong(long long value) { robj *o; if (value >= 0 && value < REDIS_SHARED_INTEGERS) { //共享范围内,使用共享对象 incrRefCount(shared.integers[value]); o = shared.integers[value]; } else { if (value >= LONG_MIN && value <= LONG_MAX) { //数值范围内,采用数值编码 o = createObject(REDIS_STRING, NULL); o->encoding = REDIS_ENCODING_INT; o->ptr = (void*)((long)value); } else { o = createObject(REDIS_STRING,sdsfromlonglong(value)); //原始编码 } } return o; }
robj *createStringObjectFromLongLong(long long value) { robj *o; if (value >= 0 && value < REDIS_SHARED_INTEGERS && pthread_equal(pthread_self(),server.mainthread)) { incrRefCount(shared.integers[value]); o = shared.integers[value]; } else { if (value >= LONG_MIN && value <= LONG_MAX) { o = createObject(REDIS_STRING, NULL); o->encoding = REDIS_ENCODING_INT; o->ptr = (void*)((long)value); } else { o = createObject(REDIS_STRING,sdsfromlonglong(value)); } } return o; }
static void* loadSetIntsetObject(unsigned char* sl, unsigned int *rlen) { int64_t intele; unsigned int i = 0, len; setTypeIterator *si; len = ((intset*)sl)->length; *rlen = len; si = setTypeInitIterator(sl); sds *results = zmalloc(len * sizeof(sds)); while (setTypeNext(si,&intele) != -1) { results[i++] = sdsfromlonglong(intele); } setTypeReleaseIterator(si); return results; }
/* The not copy on write friendly version but easy to use version * of setTypeNext() is setTypeNextObject(), returning new SDS * strings. So if you don't retain a pointer to this object you should call * sdsfree() against it. * * This function is the way to go for write operations where COW is not * an issue. */ sds setTypeNextObject(setTypeIterator *si) { int64_t intele; sds sdsele; int encoding; encoding = setTypeNext(si,&sdsele,&intele); switch(encoding) { case -1: return NULL; case OBJ_ENCODING_INTSET: return sdsfromlonglong(intele); case OBJ_ENCODING_HT: return sdsdup(sdsele); default: serverPanic("Unsupported encoding"); } return NULL; /* just to suppress warnings */ }
/// 从long long 类型的value创建一个STRING obj,编码为RAW或INT robj *createStringObjectFromLongLong(long long value) { robj *o; /// 小于10000,用共享的小INT节省内存 if (value >= 0 && value < REDIS_SHARED_INTEGERS) { incrRefCount(shared.integers[value]); o = shared.integers[value]; } else { if (value >= LONG_MIN && value <= LONG_MAX) { o = createObject(REDIS_STRING, NULL); o->encoding = REDIS_ENCODING_INT; /// 直接保存在指针里面,注意,指针指向的内存无效 o->ptr = (void*)((long)value); } else { /// 将long long保存在sds里面 o = createObject(REDIS_STRING,sdsfromlonglong(value)); } } return o; }
void incrDecrCommand(redisClient *c, long long incr) { sds data; char *value; int64_t val, recore; size_t val_len; char *err = NULL; err = NULL; val_len = 0; value = leveldb_get(server.ds_db, server.roptions, c->argv[1]->ptr, sdslen((sds) c->argv[1]->ptr), &val_len, &err); if (err != NULL) { if (val_len > 0) leveldb_free(value); addReplyError(c, err); leveldb_free(err); return; } else if (val_len < 1) { val = 0; } else { val = strtoll(value, NULL, 10); } err = NULL; recore = val + incr; data = sdsfromlonglong(recore); leveldb_put(server.ds_db, server.woptions, c->argv[1]->ptr, sdslen((sds) c->argv[1]->ptr), data, sdslen(data), &err); if (err != NULL) { addReplyError(c, err); leveldb_free(err); } else { addReplyLongLong(c, recore); server.dirty++; signalModifiedKey(c->db, c->argv[1]); } sdsfree(data); leveldb_free(value); return; }
//创建字符串对象,根据整数值 robj *createStringObjectFromLongLong(long long value) { robj *o; //redis中[0, 10000)内的整数是共享的 if (value >= 0 && value < OBJ_SHARED_INTEGERS) { //如果value属于redis共享整数的范围 incrRefCount(shared.integers[value]); //引用计数加1 o = shared.integers[value]; //返回一个编码类型为OBJ_ENCODING_INT的字符串对象 //如果不在共享整数的范围 } else { if (value >= LONG_MIN && value <= LONG_MAX) { //value在long类型所表示的范围内 o = createObject(OBJ_STRING, NULL); //创建对象 o->encoding = OBJ_ENCODING_INT; //编码类型为OBJ_ENCODING_INT o->ptr = (void*)((long)value); //指向这个value值 } else { //value不在long类型所表示的范围内,将long long类型的整数转换为字符串 //编码类型为OBJ_ENCODING_RAW o = createObject(OBJ_STRING,sdsfromlonglong(value)); } } return o; }
int processMultiBulkReply(redisReply *reply, void **buf){ int elements=reply->elements; *buf=sdscatlen(*buf,"*",1); sds num=sdsfromlonglong(elements); *buf=sdscatlen(*buf,num,sdslen(num)); *buf=sdscatlen(*buf,"\\r\\n",4); int i=0; for(;i<elements;i++){ redisReply *tmpReply=reply->element[i]; switch(tmpReply->type){ case REDIS_REPLY_STRING: case REDIS_REPLY_ERROR: case REDIS_REPLY_INTEGER: case REDIS_REPLY_NIL: case REDIS_REPLY_STATUS: processLineReply(tmpReply,buf); break; case REDIS_REPLY_ARRAY: processMultiBulkReply(tmpReply,buf); break; } } }
static void *loadZsetZiplistObject(unsigned char* zl, unsigned int *rlen) { unsigned int i = 0, len; unsigned char *eptr, *sptr; unsigned char *vstr; unsigned int vlen; int buf_len; long long vlong; double score; char buf[128]; sds ele; len = ziplistLen (zl); eptr = ziplistIndex(zl,0); sptr = ziplistNext(zl,eptr); if(rdb_version < 2) { *rlen = len * 2; } else { *rlen = len; } sds *results = zmalloc(*rlen * sizeof(sds)); while (eptr != NULL) { score = zzlGetScore(sptr); ziplistGet(eptr,&vstr,&vlen,&vlong); if (vstr == NULL) ele = sdsfromlonglong(vlong); else ele = sdsnewlen((char*)vstr,vlen); results[i] = ele; buf_len = snprintf(buf, 128, "%f", score); results[i+1] = sdsnewlen(buf, buf_len); i += 2; zzlNext(zl,&eptr,&sptr); } return results; }
static sds rdbLoadIntegerObject(FILE *fp, int enctype, int encode) { unsigned char enc[4]; long long val; encode = -1; /* unsed */ if (enctype == REDIS_RDB_ENC_INT8) { if (freadCheck(enc,1,1,fp) == 0) return NULL; val = (signed char)enc[0]; } else if (enctype == REDIS_RDB_ENC_INT16) { uint16_t v; if (freadCheck(enc,2,1,fp) == 0) return NULL; v = enc[0]|(enc[1]<<8); val = (int16_t)v; } else if (enctype == REDIS_RDB_ENC_INT32) { uint32_t v; if (freadCheck(enc,4,1,fp) == 0) return NULL; v = enc[0]|(enc[1]<<8)|(enc[2]<<16)|(enc[3]<<24); val = (int32_t)v; } else { val = 0; /* anti-warning */ parsePanic("Unknown RDB integer encoding type"); } return sdsfromlonglong(val); }
/* Returns number of consumed options. */ int parseOptions(int argc, const char **argv) { int i; int lastarg; int exit_status = 1; for (i = 1; i < argc; i++) { lastarg = (i == (argc-1)); if (!strcmp(argv[i],"-c")) { if (lastarg) goto invalid; config.numclients = atoi(argv[++i]); } else if (!strcmp(argv[i],"-n")) { if (lastarg) goto invalid; config.requests = atoi(argv[++i]); } else if (!strcmp(argv[i],"-k")) { if (lastarg) goto invalid; config.keepalive = atoi(argv[++i]); } else if (!strcmp(argv[i],"-h")) { if (lastarg) goto invalid; config.hostip = strdup(argv[++i]); } else if (!strcmp(argv[i],"-p")) { if (lastarg) goto invalid; config.hostport = atoi(argv[++i]); } else if (!strcmp(argv[i],"-s")) { if (lastarg) goto invalid; config.hostsocket = strdup(argv[++i]); } else if (!strcmp(argv[i],"-d")) { if (lastarg) goto invalid; config.datasize = atoi(argv[++i]); if (config.datasize < 1) config.datasize=1; if (config.datasize > 1024*1024*1024) config.datasize = 1024*1024*1024; } else if (!strcmp(argv[i],"-P")) { if (lastarg) goto invalid; config.pipeline = atoi(argv[++i]); if (config.pipeline <= 0) config.pipeline=1; } else if (!strcmp(argv[i],"-r")) { if (lastarg) goto invalid; config.randomkeys = 1; config.randomkeys_keyspacelen = atoi(argv[++i]); if (config.randomkeys_keyspacelen < 0) config.randomkeys_keyspacelen = 0; } else if (!strcmp(argv[i],"-q")) { config.quiet = 1; } else if (!strcmp(argv[i],"--csv")) { config.csv = 1; } else if (!strcmp(argv[i],"-l")) { config.loop = 1; } else if (!strcmp(argv[i],"-I")) { config.idlemode = 1; } else if (!strcmp(argv[i],"-t")) { if (lastarg) goto invalid; /* We get the list of tests to run as a string in the form * get,set,lrange,...,test_N. Then we add a comma before and * after the string in order to make sure that searching * for ",testname," will always get a match if the test is * enabled. */ config.tests = sdsnew(","); config.tests = sdscat(config.tests,(char*)argv[++i]); config.tests = sdscat(config.tests,","); sdstolower(config.tests); } else if (!strcmp(argv[i],"--dbnum")) { if (lastarg) goto invalid; config.dbnum = atoi(argv[++i]); config.dbnumstr = sdsfromlonglong(config.dbnum); } else if (!strcmp(argv[i],"--help")) { exit_status = 0; goto usage; } else { /* Assume the user meant to provide an option when the arg starts * with a dash. We're done otherwise and should use the remainder * as the command and arguments for running the benchmark. */ if (argv[i][0] == '-') goto invalid; return i; } } return i; invalid: printf("Invalid option \"%s\" or option argument missing\n\n",argv[i]); usage: printf( "Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>]\n\n" " -h <hostname> Server hostname (default 127.0.0.1)\n" " -p <port> Server port (default 6379)\n" " -s <socket> Server socket (overrides host and port)\n" " -c <clients> Number of parallel connections (default 50)\n" " -n <requests> Total number of requests (default 10000)\n" " -d <size> Data size of SET/GET value in bytes (default 2)\n" " -dbnum <db> SELECT the specified db number (default 0)\n" " -k <boolean> 1=keep alive 0=reconnect (default 1)\n" " -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD\n" " Using this option the benchmark will get/set keys\n" " in the form mykey_rand:000000012456 instead of constant\n" " keys, the <keyspacelen> argument determines the max\n" " number of values for the random number. For instance\n" " if set to 10 only rand:000000000000 - rand:000000000009\n" " range will be allowed.\n" " -P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline).\n" " -q Quiet. Just show query/sec values\n" " --csv Output in CSV format\n" " -l Loop. Run the tests forever\n" " -t <tests> Only run the comma separated list of tests. The test\n" " names are the same as the ones produced as output.\n" " -I Idle mode. Just open N idle connections and wait.\n\n" "Examples:\n\n" " Run the benchmark with the default configuration against 127.0.0.1:6379:\n" " $ redis-benchmark\n\n" " Use 20 parallel clients, for a total of 100k requests, against 192.168.1.1:\n" " $ redis-benchmark -h 192.168.1.1 -p 6379 -n 100000 -c 20\n\n" " Fill 127.0.0.1:6379 with about 1 million keys only using the SET test:\n" " $ redis-benchmark -t set -n 1000000 -r 100000000\n\n" " Benchmark 127.0.0.1:6379 for a few commands producing CSV output:\n" " $ redis-benchmark -t ping,set,get -n 100000 --csv\n\n" " Benchmark a specific command line:\n" " $ redis-benchmark -r 10000 -n 10000 eval 'return redis.call(\"ping\")' 0\n\n" " Fill a list with 10000 random elements:\n" " $ redis-benchmark -r 10000 -n 10000 lpush mylist __rand_int__\n\n" " On user specified command lines __rand_int__ is replaced with a random integer\n" " with a range of values selected by the -r option.\n" ); exit(exit_status); }
void spopWithCountCommand(client *c) { long l; unsigned long count, size; robj *set; /* Get the count argument */ if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return; if (l >= 0) { count = (unsigned) l; } else { addReply(c,shared.outofrangeerr); return; } /* Make sure a key with the name inputted exists, and that it's type is * indeed a set. Otherwise, return nil */ if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL || checkType(c,set,OBJ_SET)) return; /* If count is zero, serve an empty multibulk ASAP to avoid special * cases later. */ if (count == 0) { addReply(c,shared.emptymultibulk); return; } size = setTypeSize(set); /* Generate an SPOP keyspace notification */ notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id); server.dirty += count; /* CASE 1: * The number of requested elements is greater than or equal to * the number of elements inside the set: simply return the whole set. */ if (count >= size) { /* We just return the entire set */ sunionDiffGenericCommand(c,c->argv+1,1,NULL,SET_OP_UNION); /* Delete the set as it is now empty */ dbDelete(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); /* Propagate this command as an DEL operation */ rewriteClientCommandVector(c,2,shared.del,c->argv[1]); signalModifiedKey(c->db,c->argv[1]); server.dirty++; return; } /* Case 2 and 3 require to replicate SPOP as a set of SREM commands. * Prepare our replication argument vector. Also send the array length * which is common to both the code paths. */ robj *propargv[3]; propargv[0] = createStringObject("SREM",4); propargv[1] = c->argv[1]; addReplyMultiBulkLen(c,count); /* Common iteration vars. */ sds sdsele; robj *objele; int encoding; int64_t llele; unsigned long remaining = size-count; /* Elements left after SPOP. */ /* If we are here, the number of requested elements is less than the * number of elements inside the set. Also we are sure that count < size. * Use two different strategies. * * CASE 2: The number of elements to return is small compared to the * set size. We can just extract random elements and return them to * the set. */ if (remaining*SPOP_MOVE_STRATEGY_MUL > count) { while(count--) { /* Emit and remove. */ encoding = setTypeRandomElement(set,&sdsele,&llele); if (encoding == OBJ_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); objele = createStringObjectFromLongLong(llele); set->ptr = intsetRemove(set->ptr,llele,NULL); } else { addReplyBulkCBuffer(c,sdsele,sdslen(sdsele)); objele = createStringObject(sdsele,sdslen(sdsele)); setTypeRemove(set,sdsele); } /* Replicate/AOF this command as an SREM operation */ propargv[2] = objele; alsoPropagate(server.sremCommand,c->db->id,propargv,3, PROPAGATE_AOF|PROPAGATE_REPL); decrRefCount(objele); } } else { /* CASE 3: The number of elements to return is very big, approaching * the size of the set itself. After some time extracting random elements * from such a set becomes computationally expensive, so we use * a different strategy, we extract random elements that we don't * want to return (the elements that will remain part of the set), * creating a new set as we do this (that will be stored as the original * set). Then we return the elements left in the original set and * release it. */ robj *newset = NULL; /* Create a new set with just the remaining elements. */ while(remaining--) { encoding = setTypeRandomElement(set,&sdsele,&llele); if (encoding == OBJ_ENCODING_INTSET) { sdsele = sdsfromlonglong(llele); } else { sdsele = sdsdup(sdsele); } if (!newset) newset = setTypeCreate(sdsele); setTypeAdd(newset,sdsele); setTypeRemove(set,sdsele); sdsfree(sdsele); } /* Assign the new set as the key value. */ incrRefCount(set); /* Protect the old set value. */ dbOverwrite(c->db,c->argv[1],newset); /* Tranfer the old set to the client and release it. */ setTypeIterator *si; si = setTypeInitIterator(set); while((encoding = setTypeNext(si,&sdsele,&llele)) != -1) { if (encoding == OBJ_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); objele = createStringObjectFromLongLong(llele); } else { addReplyBulkCBuffer(c,sdsele,sdslen(sdsele)); objele = createStringObject(sdsele,sdslen(sdsele)); } /* Replicate/AOF this command as an SREM operation */ propargv[2] = objele; alsoPropagate(server.sremCommand,c->db->id,propargv,3, PROPAGATE_AOF|PROPAGATE_REPL); decrRefCount(objele); } setTypeReleaseIterator(si); decrRefCount(set); } /* Don't propagate the command itself even if we incremented the * dirty counter. We don't want to propagate an SPOP command since * we propagated the command as a set of SREMs operations using * the alsoPropagate() API. */ decrRefCount(propargv[0]); preventCommandPropagation(c); signalModifiedKey(c->db,c->argv[1]); server.dirty++; }
void sinterGenericCommand(client *c, robj **setkeys, unsigned long setnum, robj *dstkey) { robj **sets = zmalloc(sizeof(robj*)*setnum); setTypeIterator *si; robj *dstset = NULL; sds elesds; int64_t intobj; void *replylen = NULL; unsigned long j, cardinality = 0; int encoding; for (j = 0; j < setnum; j++) { robj *setobj = dstkey ? lookupKeyWrite(c->db,setkeys[j]) : lookupKeyRead(c->db,setkeys[j]); if (!setobj) { zfree(sets); if (dstkey) { if (dbDelete(c->db,dstkey)) { signalModifiedKey(c->db,dstkey); server.dirty++; } addReply(c,shared.czero); } else { addReply(c,shared.emptymultibulk); } return; } if (checkType(c,setobj,OBJ_SET)) { zfree(sets); return; } sets[j] = setobj; } /* Sort sets from the smallest to largest, this will improve our * algorithm's performance */ qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality); /* The first thing we should output is the total number of elements... * since this is a multi-bulk write, but at this stage we don't know * the intersection set size, so we use a trick, append an empty object * to the output list and save the pointer to later modify it with the * right length */ if (!dstkey) { replylen = addDeferredMultiBulkLength(c); } else { /* If we have a target key where to store the resulting set * create this key with an empty set inside */ dstset = createIntsetObject(); } /* Iterate all the elements of the first (smallest) set, and test * the element against all the other sets, if at least one set does * not include the element it is discarded */ si = setTypeInitIterator(sets[0]); while((encoding = setTypeNext(si,&elesds,&intobj)) != -1) { for (j = 1; j < setnum; j++) { if (sets[j] == sets[0]) continue; if (encoding == OBJ_ENCODING_INTSET) { /* intset with intset is simple... and fast */ if (sets[j]->encoding == OBJ_ENCODING_INTSET && !intsetFind((intset*)sets[j]->ptr,intobj)) { break; /* in order to compare an integer with an object we * have to use the generic function, creating an object * for this */ } else if (sets[j]->encoding == OBJ_ENCODING_HT) { elesds = sdsfromlonglong(intobj); if (!setTypeIsMember(sets[j],elesds)) { sdsfree(elesds); break; } sdsfree(elesds); } } else if (encoding == OBJ_ENCODING_HT) { if (!setTypeIsMember(sets[j],elesds)) { break; } } } /* Only take action when all sets contain the member */ if (j == setnum) { if (!dstkey) { if (encoding == OBJ_ENCODING_HT) addReplyBulkCBuffer(c,elesds,sdslen(elesds)); else addReplyBulkLongLong(c,intobj); cardinality++; } else { if (encoding == OBJ_ENCODING_INTSET) { elesds = sdsfromlonglong(intobj); setTypeAdd(dstset,elesds); sdsfree(elesds); } else { setTypeAdd(dstset,elesds); } } } } setTypeReleaseIterator(si); if (dstkey) { /* Store the resulting set into the target, if the intersection * is not an empty set. */ int deleted = dbDelete(c->db,dstkey); if (setTypeSize(dstset) > 0) { dbAdd(c->db,dstkey,dstset); addReplyLongLong(c,setTypeSize(dstset)); notifyKeyspaceEvent(NOTIFY_SET,"sinterstore", dstkey,c->db->id); } else { decrRefCount(dstset); addReply(c,shared.czero); if (deleted) notifyKeyspaceEvent(NOTIFY_GENERIC,"del", dstkey,c->db->id); } signalModifiedKey(c->db,dstkey); server.dirty++; } else { setDeferredMultiBulkLength(c,replylen,cardinality); } zfree(sets); }
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) { char tmpfile[256], *err; int dfd, maxtries = 5; int sockerr = 0; socklen_t errlen = sizeof(sockerr); REDIS_NOTUSED(el); REDIS_NOTUSED(privdata); REDIS_NOTUSED(mask); /* If this event fired after the user turned the instance into a master * with SLAVEOF NO ONE we must just return ASAP. */ if (server.repl_state == REDIS_REPL_NONE) { close(fd); return; } /* Check for errors in the socket. */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1) sockerr = errno; if (sockerr) { aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE); redisLog(REDIS_WARNING,"Error condition on socket for SYNC: %s", strerror(sockerr)); goto error; } /* If we were connecting, it's time to send a non blocking PING, we want to * make sure the master is able to reply before going into the actual * replication process where we have long timeouts in the order of * seconds (in the meantime the slave would block). */ if (server.repl_state == REDIS_REPL_CONNECTING) { redisLog(REDIS_NOTICE,"Non blocking connect for SYNC fired the event."); /* Delete the writable event so that the readable event remains * registered and we can wait for the PONG reply. */ aeDeleteFileEvent(server.el,fd,AE_WRITABLE); server.repl_state = REDIS_REPL_RECEIVE_PONG; /* Send the PING, don't check for errors at all, we have the timeout * that will take care about this. */ syncWrite(fd,"PING\r\n",6,100); return; } /* Receive the PONG command. */ if (server.repl_state == REDIS_REPL_RECEIVE_PONG) { char buf[1024]; /* Delete the readable event, we no longer need it now that there is * the PING reply to read. */ aeDeleteFileEvent(server.el,fd,AE_READABLE); /* Read the reply with explicit timeout. */ buf[0] = '\0'; if (syncReadLine(fd,buf,sizeof(buf), server.repl_syncio_timeout*1000) == -1) { redisLog(REDIS_WARNING, "I/O error reading PING reply from master: %s", strerror(errno)); goto error; } /* We don't care about the reply, it can be +PONG or an error since * the server requires AUTH. As long as it replies correctly, it's * fine from our point of view. */ if (buf[0] != '-' && buf[0] != '+') { redisLog(REDIS_WARNING,"Unexpected reply to PING from master."); goto error; } else { redisLog(REDIS_NOTICE, "Master replied to PING, replication can continue..."); } } /* AUTH with the master if required. */ if(server.masterauth) { err = sendSynchronousCommand(fd,"AUTH",server.masterauth,NULL); if (err) { redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s",err); sdsfree(err); goto error; } } /* Set the slave port, so that Master's INFO command can list the * slave listening port correctly. */ { sds port = sdsfromlonglong(server.port); err = sendSynchronousCommand(fd,"REPLCONF","listening-port",port, NULL); sdsfree(port); /* Ignore the error if any, not all the Redis versions support * REPLCONF listening-port. */ if (err) { redisLog(REDIS_NOTICE,"(non critical): Master does not understand REPLCONF listening-port: %s", err); sdsfree(err); } } /* Issue the SYNC command */ if (syncWrite(fd,"SYNC\r\n",6,server.repl_syncio_timeout*1000) == -1) { redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s", strerror(errno)); goto error; } /* Prepare a suitable temp file for bulk transfer */ while(maxtries--) { snprintf(tmpfile,256, "temp-%d.%ld.rdb",(int)server.unixtime,(long int)getpid()); dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644); if (dfd != -1) break; sleep(1); } if (dfd == -1) { redisLog(REDIS_WARNING,"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s",strerror(errno)); goto error; } /* Setup the non blocking download of the bulk file. */ if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL) == AE_ERR) { redisLog(REDIS_WARNING, "Can't create readable event for SYNC: %s (fd=%d)", strerror(errno),fd); goto error; } server.repl_state = REDIS_REPL_TRANSFER; server.repl_transfer_size = -1; server.repl_transfer_read = 0; server.repl_transfer_last_fsync_off = 0; server.repl_transfer_fd = dfd; server.repl_transfer_lastio = server.unixtime; server.repl_transfer_tmpfile = zstrdup(tmpfile); return; error: close(fd); server.repl_transfer_s = -1; server.repl_state = REDIS_REPL_CONNECT; return; }
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) { char tmpfile[256], *err; int dfd, maxtries = 5; int sockerr = 0; socklen_t errlen = sizeof(sockerr); REDIS_NOTUSED(el); REDIS_NOTUSED(privdata); REDIS_NOTUSED(mask); /* If this event fired after the user turned the instance into a master * with SLAVEOF NO ONE we must just return ASAP. */ if (server.repl_state == REDIS_REPL_NONE) { close(fd); return; } /* Check for errors in the socket. */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1) sockerr = errno; if (sockerr) { aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE); redisLog(REDIS_WARNING,"Error condition on socket for SYNC: %s", strerror(sockerr)); goto error; } /* If we were connecting, it's time to send a non blocking PING, we want to * make sure the master is able to reply before going into the actual * replication process where we have long timeouts in the order of * seconds (in the meantime the slave would block). */ //如果之前正在REDIS_REPL_CONNECTING,现在有可读可写事件了,说明连接成功了,下一步就是需要发送PING请求 if (server.repl_state == REDIS_REPL_CONNECTING) { redisLog(REDIS_NOTICE,"Non blocking connect for SYNC fired the event."); /* Delete the writable event so that the readable event remains * registered and we can wait for the PONG reply. */ aeDeleteFileEvent(server.el,fd,AE_WRITABLE); server.repl_state = REDIS_REPL_RECEIVE_PONG; /* Send the PING, don't check for errors at all, we have the timeout * that will take care about this. */ syncWrite(fd,"PING\r\n",6,100);//同步阻塞发送PING命令,这样服务端会返回"+PONG\r\n"字符串的 //这里实现的比较简单,就是发送完PING后,当连接可读可写时,再进syncWithMaster这个函数的时候 //下面的代码会判断PONG这个动作,然后就会阻塞去读取一行回复,看他是不是成功了。 return; } /* Receive the PONG command. */ if (server.repl_state == REDIS_REPL_RECEIVE_PONG) { char buf[1024]; //上面在REDIS_REPL_CONNECTING状态的时候给master发送了一行PING指令,这样master会返回"+PONG\r\n"的, //现在连接可读或者可写了,所以我们阻塞读取这么多数据,判断返回是否OK。 /* Delete the readable event, we no longer need it now that there is * the PING reply to read. */ aeDeleteFileEvent(server.el,fd,AE_READABLE); /* Read the reply with explicit timeout. */ buf[0] = '\0'; if (syncReadLine(fd,buf,sizeof(buf), server.repl_syncio_timeout*1000) == -1) { redisLog(REDIS_WARNING, "I/O error reading PING reply from master: %s", strerror(errno)); goto error; } /* We accept only two replies as valid, a positive +PONG reply * (we just check for "+") or an authentication error. * Note that older versions of Redis replied with "operation not * permitted" instead of using a proper error code, so we test * both. */ if (buf[0] != '+' && strncmp(buf,"-NOAUTH",7) != 0 && strncmp(buf,"-ERR operation not permitted",28) != 0) { redisLog(REDIS_WARNING,"Error reply to PING from master: '%s'",buf); goto error; } else { redisLog(REDIS_NOTICE, "Master replied to PING, replication can continue..."); } } /* AUTH with the master if required. */ if(server.masterauth) { err = sendSynchronousCommand(fd,"AUTH",server.masterauth,NULL); if (err) { redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s",err); sdsfree(err); goto error; } } /* Set the slave port, so that Master's INFO command can list the * slave listening port correctly. */ { sds port = sdsfromlonglong(server.port); err = sendSynchronousCommand(fd,"REPLCONF","listening-port",port, NULL); sdsfree(port); /* Ignore the error if any, not all the Redis versions support * REPLCONF listening-port. */ if (err) { redisLog(REDIS_NOTICE,"(non critical): Master does not understand REPLCONF listening-port: %s", err); sdsfree(err); } } //到这里的话,连接肯定成功了,而且PING指令也都收到了回复。所以果断发送SYNC指令 /* Issue the SYNC command */ if (syncWrite(fd,"SYNC\r\n",6,server.repl_syncio_timeout*1000) == -1) { redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s", strerror(errno)); goto error; } /* Prepare a suitable temp file for bulk transfer */ while(maxtries--) { snprintf(tmpfile,256, "temp-%d.%ld.rdb",(int)server.unixtime,(long int)getpid()); dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644); if (dfd != -1) break; sleep(1); } if (dfd == -1) { redisLog(REDIS_WARNING,"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s",strerror(errno)); goto error; } //SYNC命令已经发送了,以后的可读可写事件就依靠readSyncBulkPayload来读取解析了。 /* Setup the non blocking download of the bulk file. */ if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL) == AE_ERR) { redisLog(REDIS_WARNING, "Can't create readable event for SYNC: %s (fd=%d)", strerror(errno),fd); goto error; } server.repl_state = REDIS_REPL_TRANSFER; server.repl_transfer_size = -1; server.repl_transfer_read = 0; server.repl_transfer_last_fsync_off = 0; server.repl_transfer_fd = dfd; server.repl_transfer_lastio = server.unixtime; server.repl_transfer_tmpfile = zstrdup(tmpfile); return; error: close(fd); server.repl_transfer_s = -1; server.repl_state = REDIS_REPL_CONNECT; return; }