Example #1
0
static void* _timer_instance(void *arg)
{
        int fatal;
        static int fatal_count = 0;
        struct timeval timeout;
        kv_timer_event_t *ev;
        kv_timer_t *timer = (kv_timer_t*)arg;

        assert(timer->state == KV_TIMER_STATE_LOADING);
        timer->state = KV_TIMER_STATE_RUNNING;
        for (;;) {
                timeout.tv_sec = 0;
                timeout.tv_usec = timer->interval;
                /** interval is very short, ignore signal interrupt */
                fatal = select(0, NULL, NULL, NULL, &timeout);
                if (fatal == -1) {
                        fatal_count++;
                        if (fatal_count >= 3) abort();
                        continue;
                }
                fatal_count = 0;

                ev = timer->list;
                while(ev) {
                        if (ustime() - ev->elapse_base >= ev->timeout) {
                                ev->elapse_base = ustime(); /** restart */
                                ev->proc();
                        }
                        ev = ev->next;
                }
        }
}
Example #2
0
void busysleep(int usec) {
  int64_t t = ustime() + usec;

  while (t > ustime()) {
    ; // Nothing.
  }
}
Example #3
0
int do_delete_event(struct redisClient *c,sds funcname)
{

    redisSrand48(0);
    server.lua_random_dirty = 0;
    server.lua_write_dirty = 0;
    lua_getglobal(server.lua,(char *)funcname);



    if (lua_isnil(server.lua,1)) {
        addReplyError(c,"no funcname triggle_scipts in lua");
        return 0;
    }
    luaTriggleSetGlobalArray(server.lua,"KEYS",c->argv+1,c->argc-1);

    redisLog(REDIS_NOTICE,"stack: %d",lua_gettop(server.lua));


#ifdef BRIDGE_DEBUG
    for(int i=0;i<c->argc-1;i++){
        redisLog(REDIS_NOTICE,"%s",(c->argv+1)[i]->ptr);
    }
#endif


    selectDb(server.lua_client,c->db->id);


    server.lua_time_start = ustime()/1000;
    server.lua_kill = 0;

    if (server.lua_time_limit > 0) {
        lua_sethook(server.lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        server.lua_time_start = ustime()/1000;
    } else {
        lua_sethook(server.lua,luaMaskCountHook,0,0);
    }

    if (lua_pcall(server.lua,0,1,0)) {
        selectDb(c,server.lua_client->db->id); 
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
                (char*)funcname, lua_tostring(server.lua,-1));
        lua_pop(server.lua,1);
        lua_gc(server.lua,LUA_GCCOLLECT,0);
        return -1;
    }
    selectDb(c,server.lua_client->db->id); 
    // luaReplyToRedisReply(c,server.lua);
    server.lua_timedout = 0;
    server.lua_caller = NULL;
    lua_gc(server.lua,LUA_GCSTEP,1);

    //for slaves
    //

    return 0; 
}
// get web from redis and calculate pagerank
void pagerank_redis(redisContext **conts, float v_quadratic_error, web* w) {
	redisContext *c = conts[0];
	redisReply *reply;
	long long t_start;
	long long t_end;
	unsigned int i;
	float q = 1;
	
	printf("Start PageRank from redis: \n");
	printf("Damping factor: %f\n", g_damping_factor);
	printf("Quadratic error range: %f\n", v_quadratic_error);
	printf("----------------------------------\n");

	reply = redisCommand(c, "GET next.uid");
	if (reply->str == NULL)
		die("Cannot not find data in redis!");
	unsigned long user_num = atol(reply->str) + 1;
	printf("user num: %ld\n", user_num);
	freeReplyObject(reply);
	
	t_start = ustime();
	if (w->size == 0) {
		for (i = 0; i < g_n_sharded; ++i) {
			reply = redisCommand(conts[i], "DEL newly.edited.linkto");
			freeReplyObject(reply);
			reply = redisCommand(conts[i], "DEL newly.edited.linkfrom");
			freeReplyObject(reply);
		}
		web_init(w, user_num);
		gen_web_sharded_redis(conts, w, 0);
	} else {
		size_t origin_size = w->size;
		web_init_from_oldweb(w, user_num);
		printf("web expansion fin...");
		update_web_sharded_redis(conts, w);
		printf("web update fin...\n");
		//printf("origin dangpage size: %zd\n", w->dangling_pages->size);
		update_dangling_pages(w, origin_size);
		//printf("updated dangpage size: %zd\n", w->dangling_pages->size);
	}
	
	printf("dangling user size: %zd\n", w->dangling_pages->size);
	t_end = ustime();
	printf("init: %lf seconds\n", getruntime(t_end, t_start));

	t_start = ustime();
	for (i = 1; q > v_quadratic_error; ++i) {
		q = calculate_pagerank_parallel(w);
		//printf("iteration %d deviate value: %f\n", i, q);
		//web_display_pagerank(w);
	}
	t_end = ustime();
	printf("calculate: %lf seconds(%d iterations)\n", getruntime(t_end, t_start), i - 1);
}
Example #5
0
/* Call() is the core of  execution of a command */
static void call(ugClient *c, int flags) {
    long long start = ustime(), duration;

    /* Call the command. */
    c->cmd->proc(c);

    duration = ustime()-start;

    c->cmd->microseconds += duration;
    c->cmd->calls++;
}
Example #6
0
File: aof.c Project: kamparo/tweet
/* This is how rewriting of the append only file in background works:
 *
 * 1) The user calls BGREWRITEAOF
 * 2) Redis calls this function, that forks():
 *    2a) the child rewrite the append only file in a temp file.
 *    2b) the parent accumulates differences in server.aof_rewrite_buf.
 * 3) When the child finished '2a' exists.
 * 4) The parent will trap the exit code, if it's OK, will append the
 *    data accumulated into server.aof_rewrite_buf into the temp file, and
 *    finally will rename(2) the temp file in the actual file name.
 *    The the new file is reopened as the new append only file. Profit!
 */
int rewriteAppendOnlyFileBackground(void) {
    pid_t childpid;
    long long start;

    if (server.aof_child_pid != -1) return REDIS_ERR;
    start = ustime();
    if ((childpid = fork()) == 0) {
        char tmpfile[256];

        /* Child */
        closeListeningSockets(0);
        redisSetProcTitle("redis-aof-rewrite");
        snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
        if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
            size_t private_dirty = zmalloc_get_private_dirty();

            if (private_dirty) {
                redisLog(REDIS_NOTICE,
                    "AOF rewrite: %zu MB of memory used by copy-on-write",
                    private_dirty/(1024*1024));
            }
            exitFromChild(0);
        } else {
            exitFromChild(1);
        }
    } else {
        /* Parent */
        server.stat_fork_time = ustime()-start;
        server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
        latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
        if (childpid == -1) {
            redisLog(REDIS_WARNING,
                "Can't rewrite append only file in background: fork: %s",
                strerror(errno));
            return REDIS_ERR;
        }
        redisLog(REDIS_NOTICE,
            "Background append only file rewriting started by pid %d",childpid);
        server.aof_rewrite_scheduled = 0;
        server.aof_rewrite_time_start = time(NULL);
        server.aof_child_pid = childpid;
        updateDictResizePolicy();
        /* We set appendseldb to -1 in order to force the next call to the
         * feedAppendOnlyFile() to issue a SELECT command, so the differences
         * accumulated by the parent into server.aof_rewrite_buf will start
         * with a SELECT statement and it will be safe to merge. */
        server.aof_selected_db = -1;
        replicationScriptCacheFlush();
        return REDIS_OK;
    }
    return REDIS_OK; /* unreached */
}
Example #7
0
/*
 * 脚本超时钩子
 */
void luaMaskCountHook(lua_State *lua, lua_Debug *ar)
{
    long long elapsed;
    REDIS_NOTUSED(ar);
    REDIS_NOTUSED(lua);

    // 计算已执行时间
    elapsed = (ustime()/1000) - server.lua_time_start;
    if (elapsed >= server.lua_time_limit && server.lua_timedout == 0)
    {
        redisLog(REDIS_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.",elapsed);
        // 已超时
        server.lua_timedout = 1;
        /* Once the script timeouts we reenter the event loop to permit others
         * to call SCRIPT KILL or SHUTDOWN NOSAVE if needed. For this reason
         * we need to mask the client executing the script from the event loop.
         * If we don't do that the client may disconnect and could no longer be
         * here when the EVAL command will return. */
        // 当脚本运行超时时,将正在执行的客户端从读事件中移除
        // 并允许其他客户端执行 SCRIPT KILL 或者 SHUTDOWN NOSAVE
        aeDeleteFileEvent(server.el, server.lua_caller->fd, AE_READABLE);
    }
    if (server.lua_timedout)
        // 在脚本上下文中,启动文件事件处理(等待 SCRIPT KILL 或 SHUTDOWN NOSAVE)
        aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
    if (server.lua_kill)
    {
        // 杀死脚本
        redisLog(REDIS_WARNING,"Lua script killed by user with SCRIPT KILL.");
        lua_pushstring(lua,"Script killed by user with SCRIPT KILL...");
        lua_error(lua);
    }
}
Example #8
0
static void write_handler(ae_event_loop *el, int fd, void *priv, int mask) {
    client *c = (client *)priv;

    /* Initialize request when nothing was written. */
    if (c->written == 0) {
        if (conf.requests_issued++ >= conf.requests) {
            free_client(c);
            return;
        }

        c->start = ustime();
        c->latency = -1;
    }

    if (sdslen(c->obuf) > c->written) {
        char *ptr = c->obuf + c->written;
        int nwritten = write(c->fd, ptr, sdslen(c->obuf) - c->written);
        if (nwritten == -1) {
            if (errno != EPIPE) {
                fprintf(stderr, "write failed:%s\n", strerror(errno));
            }
            free_client(c);
            return;
        }
        c->written += nwritten;

        if (sdslen(c->obuf) == c->written) {
            ae_delete_file_event(conf.el, c->fd, AE_WRITABLE);
            ae_create_file_event(conf.el, c->fd, 
                    AE_READABLE, read_handler, c);
        }
    }

}
Example #9
0
// 判断超时,并作超时的处理
void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
    long long elapsed;
    REDIS_NOTUSED(ar);
    REDIS_NOTUSED(lua);

    // 计算已经超时的时间
    elapsed = (ustime()/1000) - server.lua_time_start;
    if (elapsed >= server.lua_time_limit && server.lua_timedout == 0) {
        redisLog(REDIS_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.",elapsed);
        server.lua_timedout = 1;

        // 超时了,关闭监听发送 lua 脚本命令的客户端
        /* Once the script timeouts we reenter the event loop to permit others
         * to call SCRIPT KILL or SHUTDOWN NOSAVE if needed. For this reason
         * we need to mask the client executing the script from the event loop.
         * If we don't do that the client may disconnect and could no longer be
         * here when the EVAL command will return. */
         aeDeleteFileEvent(server.el, server.lua_caller->fd, AE_READABLE);
    }

    // lua 脚本执行超时,redis 会检测对否有其他客户端会发送 SCRIPT KILL 命令
    // 尝试终结这个脚本的执行
    if (server.lua_timedout)
        aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);

    // 如果 lua 脚本被停止了,强制产生错误,结束 lua
    if (server.lua_kill) {
        redisLog(REDIS_WARNING,"Lua script killed by user with SCRIPT KILL.");
        lua_pushstring(lua,"Script killed by user with SCRIPT KILL...");
        lua_error(lua);
    }
}
Example #10
0
static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    client c = privdata;
    void *reply = NULL;
    REDIS_NOTUSED(el);
    REDIS_NOTUSED(fd);
    REDIS_NOTUSED(mask);

    /* Calculate latency only for the first read event. This means that the
     * server already sent the reply and we need to parse it. Parsing overhead
     * is not part of the latency, so calculate it only once, here. */
    if (c->latency < 0) c->latency = ustime()-(c->start);

    if (redisBufferRead(c->context) != REDIS_OK) {
        fprintf(stderr,"Error: %s\n",c->context->errstr);
        exit(1);
    } else {
        if (redisGetReply(c->context,&reply) != REDIS_OK) {
            fprintf(stderr,"Error: %s\n",c->context->errstr);
            exit(1);
        }
        if (reply != NULL) {
            if (reply == (void*)REDIS_REPLY_ERROR) {
                fprintf(stderr,"Unexpected error reply, exiting...\n");
                exit(1);
            }

            if (config.requests_finished < config.requests)
                config.latency[config.requests_finished++] = c->latency;
            clientDone(c);
        }
    }
}
Example #11
0
/* convert a linked list encoding to a list array encoding */
robj *cowListCopy(robj *val) {
    long long sttime;
    robj *newval;
    sttime = ustime();
    if (val->encoding == REDIS_ENCODING_ZIPLIST) {
        size_t bytes;
        redisLog(REDIS_NOTICE, "cowListCopy REDIS_ENCODING_ZIPLIST");
        newval = createZiplistObject();
        /* do raw memory copy */
        bytes = ziplistBlobLen(val->ptr);
        newval->ptr = zrealloc(newval->ptr, bytes);
        memcpy(newval->ptr, val->ptr, bytes);

        return newval;
    } else if (val->encoding == REDIS_ENCODING_LINKEDLIST) {
        list *list = val->ptr;
        cowListArray *lar;

        redisLog(REDIS_NOTICE, "cowListCopy REDIS_ENCODING_LINKEDLIST");
        lar = cowConvertListToArray(list);
        newval = createObject(REDIS_LIST, lar);
        newval->encoding = REDIS_ENCODING_LINKEDLISTARRAY;

        return newval;
    } else {
        /* error. unexpected encoding */
        return NULL;
    }
}
Example #12
0
/* if copy on write active, then ensure there is a
   copy of the value that is safe to modify or delete,
   and update DB dict entry to refer to this value*/
void cowEnsureExpiresCopy(redisDb *db) {
    long long sttime;

    if (server.isBackgroundSaving == 0 ||
        server.cowDictCopied == NULL ||
        server.cowSaveDb[db->id].expires == NULL) {
        /* no copy needed */
        return;
    } else {
        /* ensure DB expires is copied */
        if (server.cowSaveDb[db->id].expires == server.db[db->id].expires) {
            sttime = ustime();
            server.db[db->id].expires = copyonwrite_dictobj(server.cowSaveDb[db->id].expires, NULL);
            redisLog(REDIS_NOTICE, "elapsed COW DB expires time %d", (unsigned int)(ustime() - sttime));
        }
    }
}
Example #13
0
static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    client c = privdata;
    void *reply = NULL;
    REDIS_NOTUSED(el);
    REDIS_NOTUSED(fd);
    REDIS_NOTUSED(mask);

    /* Calculate latency only for the first read event. This means that the
     * server already sent the reply and we need to parse it. Parsing overhead
     * is not part of the latency, so calculate it only once, here. */
    if (c->latency < 0) c->latency = ustime()-(c->start);

    if (redisBufferRead(c->context) != REDIS_OK) {
        fprintf(stderr,"Error: %s\n",c->context->errstr);
        exit(1);
    } else {
        while(c->pending) {
            if (redisGetReply(c->context,&reply) != REDIS_OK) {
                fprintf(stderr,"Error: %s\n",c->context->errstr);
                exit(1);
            }
            if (reply != NULL) {
                if (reply == (void*)REDIS_REPLY_ERROR) {
                    fprintf(stderr,"Unexpected error reply, exiting...\n");
                    exit(1);
                }

                freeReplyObject(reply);
                /* This is an OK for prefix commands such as auth and select.*/
                if (c->prefix_pending > 0) {
                    c->prefix_pending--;
                    c->pending--;
                    /* Discard prefix commands on first response.*/
                    if (c->prefixlen > 0) {
                        size_t j;
                        sdsrange(c->obuf, c->prefixlen, -1);
                        /* We also need to fix the pointers to the strings
                        * we need to randomize. */
                        for (j = 0; j < c->randlen; j++)
                            c->randptr[j] -= c->prefixlen;
                        c->prefixlen = 0;
                    }
                    continue;                
                }

                if (config.requests_finished < config.requests)
                    config.latency[config.requests_finished++] = c->latency;
                c->pending--;
                if (c->pending == 0) {
                    clientDone(c);
                    break;
                }
            } else {
                break;
            }
        }
    }
}
Example #14
0
static void
init_per_lcore() {
    lcore_conf_t *qconf;
    unsigned lcore_id = rte_lcore_id();
    qconf = &sk.lcore_conf[lcore_id];
    qconf->tsc_hz = rte_get_tsc_hz();
    qconf->start_us = (uint64_t )ustime();
    qconf->start_tsc = rte_rdtsc();
}
Example #15
0
/* This is how rewriting of the append only file in background works:
 *
 * 1) The user calls BGREWRITEAOF
 * 2) Redis calls this function, that forks():
 *    2a) the child rewrite the append only file in a temp file.
 *    2b) the parent accumulates differences in server.bgrewritebuf.
 * 3) When the child finished '2a' exists.
 * 4) The parent will trap the exit code, if it's OK, will append the
 *    data accumulated into server.bgrewritebuf into the temp file, and
 *    finally will rename(2) the temp file in the actual file name.
 *    The the new file is reopened as the new append only file. Profit!
 */
int rewriteAppendOnlyFileBackground(void) {
    pid_t childpid;
    long long start;

    if (server.bgrewritechildpid != -1) return REDIS_ERR;
    if (server.vm_enabled) waitEmptyIOJobsQueue();
    start = ustime();
    if ((childpid = fork()) == 0) {
        char tmpfile[256];

        /* Child */
        if (server.vm_enabled) vmReopenSwapFile();
        if (server.ipfd > 0) close(server.ipfd);
        if (server.sofd > 0) close(server.sofd);
        snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
        if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
            _exit(0);
        } else {
            _exit(1);
        }
    } else {
        /* Parent */
        server.stat_fork_time = ustime()-start;
        if (childpid == -1) {
            redisLog(REDIS_WARNING,
                "Can't rewrite append only file in background: fork: %s",
                strerror(errno));
            return REDIS_ERR;
        }
        redisLog(REDIS_NOTICE,
            "Background append only file rewriting started by pid %d",childpid);
        server.aofrewrite_scheduled = 0;
        server.bgrewritechildpid = childpid;
        updateDictResizePolicy();
        /* We set appendseldb to -1 in order to force the next call to the
         * feedAppendOnlyFile() to issue a SELECT command, so the differences
         * accumulated by the parent into server.bgrewritebuf will start
         * with a SELECT statement and it will be safe to merge. */
        server.appendseldb = -1;
        return REDIS_OK;
    }
    return REDIS_OK; /* unreached */
}
Example #16
0
void kv_timer_add(kv_timer_t *timer, long long usec, timer_proc proc)
{
        kv_timer_event_t *te;

        te = zmalloc(sizeof(*te));
        te->timeout = usec < 0 ? -usec : usec;
        te->elapse_base = ustime();
        te->proc = proc;
        te->next = 0;
        
        if (!timer->list) {
                timer->list = te;
        } else {
                te->next = timer->list;
                timer->list = te;
        }
}
Example #17
0
static void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    client c = privdata;
    REDIS_NOTUSED(el);
    REDIS_NOTUSED(fd);
    REDIS_NOTUSED(mask);

    /* Initialize request when nothing was written. */
    if (c->written == 0) {
        /* Enforce upper bound to number of requests. */
        if (config.requests_issued++ >= config.requests) {
            freeClient(c);
            return;
        }

        /* Really initialize: randomize keys and set start time. */
        if (config.randomkeys) randomizeClientKey(c);
        c->start = ustime();
        c->latency = -1;
    }

    if (sdslen(c->obuf) > c->written) {
        void *ptr = c->obuf+c->written;
#ifdef _WIN32
        int nwritten = send(c->context->fd,ptr,sdslen(c->obuf)-c->written, 0);
#else
        int nwritten = write(c->context->fd,ptr,sdslen(c->obuf)-c->written);
#endif
        if (nwritten == -1) {
            if (errno != EPIPE)
                fprintf(stderr, "Writing to socket: %s\n", strerror(errno));
            freeClient(c);
            return;
        }
        c->written += nwritten;
        if (sdslen(c->obuf) == c->written) {
            aeDeleteFileEvent(config.el,c->context->fd,AE_WRITABLE);
            aeCreateFileEvent(config.el,c->context->fd,AE_READABLE,readHandler,c);
        }
    }
}
Example #18
0
static void read_handler(ae_event_loop *el, int fd, void *priv, int mask) {
    client *c = (client *)priv;
    int nread;
    char buffer[4096];

    nread = read(fd, buffer, 4096);
    if (nread == -1) {
        if (errno == EAGAIN) {
            return;
        }
        fprintf(stderr, "Error: %s\n", strerror(errno));
        exit(1);
    } else if (nread == 0) {
        fprintf(stderr, "Error: %s\n", "Server close connection.");
        exit(1);
    }
    
    c->read += nread;
    if (c->read == sdslen(c->obuf)) {
        c->latency = ustime() - c->start;
        ++conf.requests_finished;
        client_done(c);
    }
}
Example #19
0
/* A background append only file rewriting (BGREWRITEAOF) terminated its work.
 * Handle this. */
void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
    if (!bysignal && exitcode == 0) {
        int newfd, oldfd;
        char tmpfile[256];
        long long now = ustime();

        redisLog(REDIS_NOTICE,
            "Background AOF rewrite terminated with success");

        /* Flush the differences accumulated by the parent to the
         * rewritten AOF. */
        snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",
            (int)server.aof_child_pid);
        newfd = open(tmpfile,O_WRONLY|O_APPEND);
        if (newfd == -1) {
            redisLog(REDIS_WARNING,
                "Unable to open the temporary AOF produced by the child: %s", strerror(errno));
            goto cleanup;
        }

        if (aofRewriteBufferWrite(newfd) == -1) {
            redisLog(REDIS_WARNING,
                "Error trying to flush the parent diff to the rewritten AOF: %s", strerror(errno));
            close(newfd);
            goto cleanup;
        }

        redisLog(REDIS_NOTICE,
            "Parent diff successfully flushed to the rewritten AOF (%lu bytes)", aofRewriteBufferSize());

        /* The only remaining thing to do is to rename the temporary file to
         * the configured file and switch the file descriptor used to do AOF
         * writes. We don't want close(2) or rename(2) calls to block the
         * server on old file deletion.
         *
         * There are two possible scenarios:
         *
         * 1) AOF is DISABLED and this was a one time rewrite. The temporary
         * file will be renamed to the configured file. When this file already
         * exists, it will be unlinked, which may block the server.
         *
         * 2) AOF is ENABLED and the rewritten AOF will immediately start
         * receiving writes. After the temporary file is renamed to the
         * configured file, the original AOF file descriptor will be closed.
         * Since this will be the last reference to that file, closing it
         * causes the underlying file to be unlinked, which may block the
         * server.
         *
         * To mitigate the blocking effect of the unlink operation (either
         * caused by rename(2) in scenario 1, or by close(2) in scenario 2), we
         * use a background thread to take care of this. First, we
         * make scenario 1 identical to scenario 2 by opening the target file
         * when it exists. The unlink operation after the rename(2) will then
         * be executed upon calling close(2) for its descriptor. Everything to
         * guarantee atomicity for this switch has already happened by then, so
         * we don't care what the outcome or duration of that close operation
         * is, as long as the file descriptor is released again. */
        if (server.aof_fd == -1) {
            /* AOF disabled */

             /* Don't care if this fails: oldfd will be -1 and we handle that.
              * One notable case of -1 return is if the old file does
              * not exist. */
             oldfd = open(server.aof_filename,O_RDONLY|O_NONBLOCK);
        } else {
            /* AOF enabled */
            oldfd = -1; /* We'll set this to the current AOF filedes later. */
        }

        /* Rename the temporary file. This will not unlink the target file if
         * it exists, because we reference it with "oldfd". */
        if (rename(tmpfile,server.aof_filename) == -1) {
            redisLog(REDIS_WARNING,
                "Error trying to rename the temporary AOF file: %s", strerror(errno));
            close(newfd);
            if (oldfd != -1) close(oldfd);
            goto cleanup;
        }

        if (server.aof_fd == -1) {
            /* AOF disabled, we don't need to set the AOF file descriptor
             * to this new file, so we can close it. */
            close(newfd);
        } else {
            /* AOF enabled, replace the old fd with the new one. */
            oldfd = server.aof_fd;
            server.aof_fd = newfd;
            if (server.aof_fsync == AOF_FSYNC_ALWAYS)
                aof_fsync(newfd);
            else if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
                aof_background_fsync(newfd);
            server.aof_selected_db = -1; /* Make sure SELECT is re-issued */
            aofUpdateCurrentSize();
            server.aof_rewrite_base_size = server.aof_current_size;

            /* Clear regular AOF buffer since its contents was just written to
             * the new AOF from the background rewrite buffer. */
            sdsfree(server.aof_buf);
            server.aof_buf = sdsempty();
        }

        server.aof_lastbgrewrite_status = REDIS_OK;

        redisLog(REDIS_NOTICE, "Background AOF rewrite finished successfully");
        /* Change state from WAIT_REWRITE to ON if needed */
        if (server.aof_state == REDIS_AOF_WAIT_REWRITE)
            server.aof_state = REDIS_AOF_ON;

        /* Asynchronously close the overwritten AOF. */
        if (oldfd != -1) bioCreateBackgroundJob(REDIS_BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);

        redisLog(REDIS_VERBOSE,
            "Background AOF rewrite signal handler took %lldus", ustime()-now);
    } else if (!bysignal && exitcode != 0) {
        server.aof_lastbgrewrite_status = REDIS_ERR;

        redisLog(REDIS_WARNING,
            "Background AOF rewrite terminated with error");
    } else {
        server.aof_lastbgrewrite_status = REDIS_ERR;

        redisLog(REDIS_WARNING,
            "Background AOF rewrite terminated by signal %d", bysignal);
    }

cleanup:
    aofRewriteBufferReset();
    aofRemoveTempFile(server.aof_child_pid);
    server.aof_child_pid = -1;
    server.aof_rewrite_time_last = time(NULL)-server.aof_rewrite_time_start;
    server.aof_rewrite_time_start = -1;
    /* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */
    if (server.aof_state == REDIS_AOF_WAIT_REWRITE)
        server.aof_rewrite_scheduled = 1;
}
Example #20
0
static void test_splinlock_speed() {
  spinlock_init(&simple_spin);
  ticketlock_init(&simple_ticket);

  pthread_t threads[10];

  printf("spinlock speed test (lock):\n");

  for (int num = 1; num <= 16; num *= 2) {
    printf("%2d threads: ", num);
    fflush(stdout);

    spinlock_lock(&simple_spin);

    for (int t = 0; t < num; ++t) {
      pthread_create(&threads[t], 0, simple_spinlock, 0);
    }
    
    int64_t start = ustime();

    spinlock_unlock(&simple_spin);

    for (int t = 0; t < num; ++t) {
      pthread_join(threads[t], 0);
    }

    int64_t end = ustime();

    printf("%6ld milliseconds\n", (end - start) / 1000);
  }
  
  printf("spinlock speed test (yieldock):\n");
  
  for (int num = 1; num <= 16; num *= 2) {
    printf("%2d threads: ", num);
    fflush(stdout);

    spinlock_lock(&simple_spin);

    for (int t = 0; t < num; ++t) {
      pthread_create(&threads[t], 0, simple_spinlock_yield, 0);
    }
    
    int64_t start = ustime();

    spinlock_unlock(&simple_spin);

    for (int t = 0; t < num; ++t) {
      pthread_join(threads[t], 0);
    }

    int64_t end = ustime();

    printf("%6ld milliseconds\n", (end - start) / 1000);
  }
  
  printf("ticketlock speed test (lock):\n");
  
  for (int num = 1; num <= 8; num *= 2) {
    printf("%2d threads: ", num);
    fflush(stdout);

    ticketlock_lock(&simple_ticket);

    for (int t = 0; t < num; ++t) {
      pthread_create(&threads[t], 0, simple_ticketlock, 0);
    }
    
    int64_t start = ustime();

    ticketlock_unlock(&simple_ticket);

    for (int t = 0; t < num; ++t) {
      pthread_join(threads[t], 0);
    }

    int64_t end = ustime();

    printf("%6ld milliseconds\n", (end - start) / 1000);
  }
  
  printf("ticketlock speed test (yieldlock):\n");
  
  for (int num = 1; num <= 8; num *= 2) {
    printf("%2d threads: ", num);
    fflush(stdout);

    ticketlock_lock(&simple_ticket);

    for (int t = 0; t < num; ++t) {
      pthread_create(&threads[t], 0, simple_ticketlock_yield, 0);
    }
    
    int64_t start = ustime();

    ticketlock_unlock(&simple_ticket);

    for (int t = 0; t < num; ++t) {
      pthread_join(threads[t], 0);
    }

    int64_t end = ustime();

    printf("%6ld milliseconds\n", (end - start) / 1000);
  }
}
Example #21
0
/* if copy on write active, then ensure there is a
   copy of the value that is safe to modify or delete,
   and update DB dict entry to refer to this value*/
robj *cowEnsureWriteCopy(redisDb *db, robj *key, robj *val) {
    long long sttime;

    if (server.isBackgroundSaving == 0 ||
        server.cowDictCopied == NULL) {
        /* no copy needed */
        return val;
    } else {
        int added = 0;
        sds keyname;
        robj *newval = NULL;

        sttime = ustime();
        /* first ensure DB dict readonly copy exists */
        cowLock();
        if (server.cowSaveDbExt[db->id].dictArray == NULL) {
            /* make clone with modified cow destructors for db dict */
            server.cowSaveDbExt[db->id].dictArray = copyReadonly_dictobj(server.db[db->id].dict,
                                                        &server.cowSaveDbExt[db->id]);

            /* migrate iterator */
            roDBMigrateIterator(server.db[db->id].dict, server.cowSaveDbExt[db->id].dictArray);
        }
        cowUnlock();

        if (val == NULL || key == NULL) {
            return NULL;
        }
        if (dictFind(server.cowDictCopied, (sds)key->ptr) != NULL) {
            /* already copied */
            return val;
        }

        /* need to duplicate object, add key to cowDictCopied,
           add original to deferred delete list, and update db entry */
        cowLock();
        switch (val->type) {
        case REDIS_STRING:
            /* updates always duplicate, original uses defered delete destructor */
            break;
        case REDIS_LIST:
            newval = cowListCopy(val);
            break;
        case REDIS_SET:
            newval = cowSetCopy(val);
            break;
        case REDIS_ZSET:
            newval = cowZSetCopy(val);
            break;
        case REDIS_HASH:
            newval = cowHashCopy(val);
            break;
        default:
            break;
        }
        cowUnlock();

        if (newval == NULL) {
            /* no duplicate needed. return original */
            return val;
        }

        /* add key to copied dictionary to avoid extra copies */
        keyname = sdsdup((sds)key->ptr);
        dictAdd(server.cowDictCopied, keyname, NULL);

        /* rewitten iterators are added to converted for lookup
         * during save. For other objects, replace DB entry */
        if (newval->encoding == REDIS_ENCODING_HTARRAY ||
            newval->encoding == REDIS_ENCODING_LINKEDLISTARRAY ||
            newval->encoding == REDIS_ENCODING_HTZARRAY) {
            cowLock();
            /* add value to converted dictionary for iterator lookup */
            dictAdd(server.cowDictConverted, keyname, newval);

            /* migrate current iterator */
            if (newval->encoding == REDIS_ENCODING_HTARRAY) {
                roDictMigrateIterator((dict *)val->ptr, (cowDictArray *)newval->ptr);
            } else if (newval->encoding == REDIS_ENCODING_LINKEDLISTARRAY) {
                roListMigrateIterator((list *)val->ptr, (cowListArray *)newval->ptr);
            } else if (newval->encoding == REDIS_ENCODING_HTZARRAY) {
                roZDictMigrateIterator((dict *)val->ptr, (cowDictZArray *)newval->ptr);
            }
            cowUnlock();
        } else {
            /* replace active DB entry */
            dbOverwrite(db, key, newval);
            val = newval;
        }

        redisLog(REDIS_NOTICE, "elapsed COW time %d", (unsigned int)(ustime() - sttime));
        return val;
    }
}
Example #22
0
int triggle_event(struct redisClient *c,sds funcname)
{

    redisSrand48(0);
    server.lua_random_dirty = 0;
    server.lua_write_dirty = 0;
    
    //redisLog(REDIS_NOTICE,"step into stack: %d",lua_gettop(server.lua));
    lua_getglobal(server.lua,funcname);



    if (lua_isnil(server.lua,1)) {
        lua_pop(server.lua,1); /* remove the nil from the stack */

        redisLog(REDIS_NOTICE,"no funcname triggle_scipts in lua");

        struct dictEntry *de = dictFind(server.bridge_db.triggle_scipts[c->db->id],funcname);
        if(de)
        {

            struct bridge_db_triggle_t * tmptrg=dictGetVal(de);
            if (luatriggleCreateFunction(server.lua,funcname,tmptrg->lua_scripts) == REDIS_ERR) return -1;
            /* Now the following is guaranteed to return non nil */
            lua_getglobal(server.lua, funcname);
            redisAssert(!lua_isnil(server.lua,1));


        }
        else
        {
            redisLog(REDIS_WARNING,"triggle not found");
            return -1;
        }


    }
    luaTriggleSetGlobalArray(server.lua,"KEYS",c->argv,c->argc);

   // redisLog(REDIS_NOTICE,"stack: %d",lua_gettop(server.lua));


/*    for(int i=0;i<c->argc;i++){
        redisLog(REDIS_NOTICE,"%s",c->argv[i]->ptr);
    }
*/

    /* Select the right DB in the context of the Lua client */
    selectDb(server.lua_client,c->db->id);


    server.lua_time_start = ustime()/1000;
    server.lua_kill = 0;

    if (server.lua_time_limit > 0) {
        lua_sethook(server.lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        server.lua_time_start = ustime()/1000;
    } else {
        lua_sethook(server.lua,luaMaskCountHook,0,0);
    }

    /* At this point whatever this script was never seen before or if it was
     * already defined, we can call it. We have zero arguments and expect
     * a single return value. */
    if (lua_pcall(server.lua,0,1,0)) {
        selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
        redisLog(REDIS_WARNING,"Error running script (call to %s): %s\n",
                (char*)funcname, lua_tostring(server.lua,-1));
        lua_pop(server.lua,1);
        lua_gc(server.lua,LUA_GCCOLLECT,0);
        return -1;
    }
    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
    // luaReplyToRedisReply(c,server.lua);
    lua_pop(server.lua,1);
    server.lua_timedout = 0;
    server.lua_caller = NULL;
    lua_gc(server.lua,LUA_GCSTEP,1);

 //   redisLog(REDIS_NOTICE,"after stack: %d",lua_gettop(server.lua));
    //for slaves
    //

    return 0; 
}
Example #23
0
int triggle_expire_event(redisDb *db,sds funcname,robj *key)
{

    redisSrand48(0);
    server.lua_random_dirty = 0;
    server.lua_write_dirty = 0;
    lua_getglobal(server.lua,funcname);



    if (lua_isnil(server.lua,1)) {
        lua_pop(server.lua,1); /* remove the nil from the stack */

        redisLog(REDIS_NOTICE,"no funcname triggle_scipts in lua");

        struct dictEntry *de = dictFind(server.bridge_db.triggle_scipts[db->id],funcname);
        if(de)
        {

            struct bridge_db_triggle_t * tmptrg=dictGetVal(de);
            if (luatriggleCreateFunction(server.lua,funcname,tmptrg->lua_scripts) == REDIS_ERR) return -1;
            /* Now the following is guaranteed to return non nil */
            lua_getglobal(server.lua, funcname);
            redisAssert(!lua_isnil(server.lua,1));


        }
        else
        {
            redisLog(REDIS_WARNING,"triggle not found");
            return -1;
        }

    }
    luaTriggleSetGlobalArray(server.lua,"KEYS",&key,1);




    server.lua_time_start = ustime()/1000;
    server.lua_kill = 0;

    if (server.lua_time_limit > 0) {
        lua_sethook(server.lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        server.lua_time_start = ustime()/1000;
    } else {
        lua_sethook(server.lua,luaMaskCountHook,0,0);
    }

    /* At this point whatever this script was never seen before or if it was
     * already defined, we can call it. We have zero arguments and expect
     * a single return value. */
    if (lua_pcall(server.lua,0,1,0)) {
        //selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */

        redisLog(REDIS_WARNING,"exec the script error:%s",lua_tostring(server.lua,-1));
        lua_pop(server.lua,1);
        lua_gc(server.lua,LUA_GCCOLLECT,0);
        return -1;
    }
    //selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
    server.lua_timedout = 0;
    server.lua_caller = NULL;
    
    lua_pop(server.lua,1);
    lua_gc(server.lua,LUA_GCSTEP,1);


    return 0; 
}
Example #24
0
void evalGenericCommand(redisClient *c, int evalsha)
{
    lua_State *lua = server.lua;
    char funcname[43];
    long long numkeys;
    int delhook = 0;

    /* We want the same PRNG sequence at every call so that our PRNG is
     * not affected by external state.
     *
     * 在每次执行 EVAL 时重置随机 seed ,从而保证可以生成相同的随机序列。
     */
    redisSrand48(0);

    /* We set this flag to zero to remember that so far no random command
     * was called. This way we can allow the user to call commands like
     * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command
     * is called (otherwise the replication and AOF would end with non
     * deterministic sequences).
     *
     * Thanks to this flag we'll raise an error every time a write command
     * is called after a random command was used.
     *
     * 用两个变量,对命令进行检查
     * 确保在调用随机命令之后,再调用写命令将出现错误
     */
    server.lua_random_dirty = 0;
    server.lua_write_dirty = 0;

    /* Get the number of arguments that are keys */
    // 获取输入键的数量
    if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK)
        return;
    // 对键的正确性做一个快速检查
    if (numkeys > (c->argc - 3))
    {
        addReplyError(c,"Number of keys can't be greater than number of args");
        return;
    }

    /* We obtain the script SHA1, then check if this function is already
     * defined into the Lua state */
    // 组合出函数的名字,例如 f_282297a0228f48cd3fc6a55de6316f31422f5d17
    funcname[0] = 'f';
    funcname[1] = '_';
    if (!evalsha)
    {
        /* Hash the code if this is an EVAL call */
        // 如果执行的是 EVAL 命令,那么计算脚本的 SHA1 校验和
        sha1hex(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));
    }
    else
    {
        // 如果执行的是 EVALSHA 命令,直接使用传入的 SHA1 值
        /* We already have the SHA if it is a EVALSHA */
        int j;
        char *sha = c->argv[1]->ptr;

        for (j = 0; j < 40; j++)
            funcname[j+2] = tolower(sha[j]);
        funcname[42] = '\0';
    }

    /* Try to lookup the Lua function */
    // 按名查找函数
    lua_getglobal(lua, funcname);
    if (lua_isnil(lua,1))
    {

        // 没找到脚本相应的脚本

        lua_pop(lua,1); /* remove the nil from the stack */
        /* Function not defined... let's define it if we have the
         * body of the funciton. If this is an EVALSHA call we can just
         * return an error. */
        if (evalsha)
        {
            // 如果执行的是 EVALSHA ,返回脚本未找到错误
            addReply(c, shared.noscripterr);
            return;
        }
        // 如果执行的是 EVAL ,那么创建并执行新函数,然后将代码添加到脚本字典中
        if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) return;
        /* Now the following is guaranteed to return non nil */
        lua_getglobal(lua, funcname);
        redisAssert(!lua_isnil(lua,1));
    }

    /* Populate the argv and keys table accordingly to the arguments that
     * EVAL received. */
    // 设置 KEYS 和 ARGV 全局变量到 Lua 环境
    luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys);
    luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys);

    /* Select the right DB in the context of the Lua client */
    // 为 Lua 所属的(伪)客户端设置数据库
    selectDb(server.lua_client,c->db->id);

    /* Set an hook in order to be able to stop the script execution if it
     * is running for too much time.
     *
     * 设置一个钩子,用于在运行时间过长时停止脚本的运作。
     *
     * We set the hook only if the time limit is enabled as the hook will
     * make the Lua script execution slower.
     *
     * 只在开启了时间限制选项时使用钩子,因为它会拖慢脚本的运行速度。
     */
    // 调用客户端
    server.lua_caller = c;
    // 脚本开始时间
    server.lua_time_start = ustime()/1000;
    // 是否杀死脚本
    server.lua_kill = 0;
    // 只在开启时间限制时使用钩子
    if (server.lua_time_limit > 0 && server.masterhost == NULL)
    {
        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        delhook = 1;
    }

    /* At this point whatever this script was never seen before or if it was
     * already defined, we can call it. We have zero arguments and expect
     * a single return value. */
    // 执行脚本(所属的函数)
    if (lua_pcall(lua,0,1,0))
    {

        // 以下是脚本执行出错的代码。。。

        // 删除钩子
        if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */

        // 脚本执行已超时
        if (server.lua_timedout)
        {
            // 清除超时 FLAG
            server.lua_timedout = 0;
            /* Restore the readable handler that was unregistered when the
             * script timeout was detected. */
            // 将超时钩子里删除的读事件重新加上
            aeCreateFileEvent(server.el,c->fd,AE_READABLE,
                              readQueryFromClient,c);
        }

        // 清空调用者
        server.lua_caller = NULL;
        // 更新目标数据库
        selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
                            funcname, lua_tostring(lua,-1));
        // 弹出函数
        lua_pop(lua,1);
        // 执行完整的废料回首循环(full garbage-collection cycle)
        lua_gc(lua,LUA_GCCOLLECT,0);
        // 返回
        return;
    }

    // 以下是脚本执行成功时执行的代码。。。

    // 删除钩子
    if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */

    // 清空超时 FLAG
    server.lua_timedout = 0;

    // 清空调用者
    server.lua_caller = NULL;

    // 更新 DB
    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */

    // 将 Lua 回复转换成 Redis 回复
    luaReplyToRedisReply(c,lua);

    // 执行 1 步渐进式 GC
    lua_gc(lua,LUA_GCSTEP,1);

    /* If we have slaves attached we want to replicate this command as
     * EVAL instead of EVALSHA. We do this also in the AOF as currently there
     * is no easy way to propagate a command in a different way in the AOF
     * and in the replication link.
     *
     * 如果有附属节点,那么使用 EVAL 而不是 EVALSHA 来传播脚本
     * 因为目前还没有代码可以检测脚本是否已经传送到附属节点中
     *
     * IMPROVEMENT POSSIBLE:
     * 1) Replicate this command as EVALSHA in the AOF.
     * 2) Remember what slave already received a given script, and replicate
     *    the EVALSHA against this slaves when possible.
     */
    // 如果执行的是 EVALSHA 命令
    if (evalsha)
    {
        // 取出脚本代码体(body)
        robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);

        redisAssertWithInfo(c,NULL,script != NULL);
        // 重写客户端命令为 EVAL
        rewriteClientCommandArgument(c,0,
                                     resetRefCount(createStringObject("EVAL",4)));
        rewriteClientCommandArgument(c,1,script);
    }
}
Example #25
0
 void update() {
     this->t = ustime();
 }
Example #26
0
 uint64_t tick() {
     uint64_t ret = this->t;
     this->t = ustime();
     return this->t - ret;
 }
Example #27
0
void evalGenericCommand(redisClient *c, int evalsha) {
    lua_State *lua = server.lua;
    char funcname[43];
    long long numkeys;
    int delhook = 0, err;

    /* We want the same PRNG sequence at every call so that our PRNG is
     * not affected by external state. */
    redisSrand48(0);

    /* We set this flag to zero to remember that so far no random command
     * was called. This way we can allow the user to call commands like
     * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command
     * is called (otherwise the replication and AOF would end with non
     * deterministic sequences).
     *
     * Thanks to this flag we'll raise an error every time a write command
     * is called after a random command was used. */
    server.lua_random_dirty = 0;
    server.lua_write_dirty = 0;

    /* Get the number of arguments that are keys */
    if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK)
        return;
    if (numkeys > (c->argc - 3)) {
        addReplyError(c,"Number of keys can't be greater than number of args");
        return;
    }

    /* We obtain the script SHA1, then check if this function is already
     * defined into the Lua state */
    funcname[0] = 'f';
    funcname[1] = '_';
    if (!evalsha) {
        /* Hash the code if this is an EVAL call */
        sha1hex(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));
    } else {
        /* We already have the SHA if it is a EVALSHA */
        int j;
        char *sha = c->argv[1]->ptr;

        for (j = 0; j < 40; j++)
            funcname[j+2] = tolower(sha[j]);
        funcname[42] = '\0';
    }

    /* Push the pcall error handler function on the stack. */
    lua_getglobal(lua, "__redis__err__handler");

    /* Try to lookup the Lua function */
    lua_getglobal(lua, funcname);
    if (lua_isnil(lua,-1)) {
        lua_pop(lua,1); /* remove the nil from the stack */
        /* Function not defined... let's define it if we have the
         * body of the function. If this is an EVALSHA call we can just
         * return an error. */
        if (evalsha) {
            lua_pop(lua,1); /* remove the error handler from the stack. */
            addReply(c, shared.noscripterr);
            return;
        }
        if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) return;
        /* Now the following is guaranteed to return non nil */
        lua_getglobal(lua, funcname);
        redisAssert(!lua_isnil(lua,-1));
    }

    /* Populate the argv and keys table accordingly to the arguments that
     * EVAL received. */
    luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys);
    luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys);

    /* Select the right DB in the context of the Lua client */
    selectDb(server.lua_client,c->db->id);
    
    /* Set an hook in order to be able to stop the script execution if it
     * is running for too much time.
     * We set the hook only if the time limit is enabled as the hook will
     * make the Lua script execution slower. */
    server.lua_caller = c;
    server.lua_time_start = ustime()/1000;
    server.lua_kill = 0;
    if (server.lua_time_limit > 0 && server.masterhost == NULL) {
        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        delhook = 1;
    }

    /* At this point whether this script was never seen before or if it was
     * already defined, we can call it. We have zero arguments and expect
     * a single return value. */

    err = lua_pcall(lua,0,1,-2);

    /* Perform some cleanup that we need to do both on error and success. */
    if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
    if (server.lua_timedout) {
        server.lua_timedout = 0;
        /* Restore the readable handler that was unregistered when the
         * script timeout was detected. */
        aeCreateFileEvent(server.el,c->fd,AE_READABLE,
                          readQueryFromClient,c);
    }
    server.lua_caller = NULL;
    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
    lua_gc(lua,LUA_GCSTEP,1);

    if (err) {
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
            funcname, lua_tostring(lua,-1));
        lua_pop(lua,1); /* Consume the Lua reply. */
    } else {
        /* On success convert the Lua return value into Redis protocol, and
         * send it to * the client. */
        luaReplyToRedisReply(c,lua);
    }

    /* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless
     * we are sure that the script was already in the context of all the
     * attached slaves *and* the current AOF file if enabled.
     *
     * To do so we use a cache of SHA1s of scripts that we already propagated
     * as full EVAL, that's called the Replication Script Cache.
     *
     * For repliation, everytime a new slave attaches to the master, we need to
     * flush our cache of scripts that can be replicated as EVALSHA, while
     * for AOF we need to do so every time we rewrite the AOF file. */
    if (evalsha) {
        if (!replicationScriptCacheExists(c->argv[1]->ptr)) {
            /* This script is not in our script cache, replicate it as
             * EVAL, then add it into the script cache, as from now on
             * slaves and AOF know about it. */
            robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);

            replicationScriptCacheAdd(c->argv[1]->ptr);
            redisAssertWithInfo(c,NULL,script != NULL);
            rewriteClientCommandArgument(c,0,
                resetRefCount(createStringObject("EVAL",4)));
            rewriteClientCommandArgument(c,1,script);
        }
    }
}
Example #28
0
/* Return the UNIX time in milliseconds */
long long mstime(void) {
    return ustime()/1000;
}
Example #29
0
int main(int argc, char *argv[]) {
    printf("Second:%d, Millisecond:%lld, Microsecond:%lld\n",
        time(NULL), mstime(), ustime());
    exit(0);
}
Example #30
0
void evalGenericCommand(redisClient *c, int evalsha) {
    lua_State *lua = server.lua;
    char funcname[43];
    long long numkeys;
    int delhook = 0, err;

    // 随机数的种子,在产生哈希值的时候会用到
    /* We want the same PRNG sequence at every call so that our PRNG is
     * not affected by external state. */
    redisSrand48(0);

    /* We set this flag to zero to remember that so far no random command
     * was called. This way we can allow the user to call commands like
     * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command
     * is called (otherwise the replication and AOF would end with non
     * deterministic sequences).
     *
     * Thanks to this flag we'll raise an error every time a write command
     * is called after a random command was used. */
    server.lua_random_dirty = 0;
    server.lua_write_dirty = 0;

    // 检查参数的有效性
    /* Get the number of arguments that are keys */
    if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK)
        return;
    if (numkeys > (c->argc - 3)) {
        addReplyError(c,"Number of keys can't be greater than number of args");
        return;
    }

    // 函数名以 f_ 开头
    /* We obtain the script SHA1, then check if this function is already
     * defined into the Lua state */
    funcname[0] = 'f';
    funcname[1] = '_';

    // 如果没有哈希值,需要计算 lua 脚本的哈希值
    if (!evalsha) {
        // 计算哈希值,会放入到 SHA1 -> lua_script 哈希表中
        // c->argv[1]->ptr 是用户指定的 lua 脚本
        // sha1hex() 产生的哈希值存在 funcname 中
        /* Hash the code if this is an EVAL call */
        sha1hex(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));
    } else {
        // 用户自己指定了哈希值
        /* We already have the SHA if it is a EVALSHA */
        int j;
        char *sha = c->argv[1]->ptr;

        for (j = 0; j < 40; j++)
            funcname[j+2] = tolower(sha[j]);
        funcname[42] = '\0';
    }

    // 将错误处理函数入栈
    // lua_getglobal() 会将读取指定的全局变量,且将其入栈
    /* Push the pcall error handler function on the stack. */
    lua_getglobal(lua, "__redis__err__handler");

    /* Try to lookup the Lua function */
    // 在 lua 中查找是否注册了此函数。这一句尝试将 funcname 入栈
    lua_getglobal(lua, funcname);
    if (lua_isnil(lua,-1)) { // funcname 在 lua 中不存在

        // 将 nil 出栈
        lua_pop(lua,1); /* remove the nil from the stack */

        // 已经确定 funcname 在 lua 中没有定义,需要创建
        /* Function not defined... let's define it if we have the
         * body of the function. If this is an EVALSHA call we can just
         * return an error. */
        if (evalsha) {
            lua_pop(lua,1); /* remove the error handler from the stack. */
            addReply(c, shared.noscripterr);
            return;
        }

        // 创建 lua 函数 funcname
        // c->argv[1] 指向用户指定的 lua 脚本
        if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) {
            lua_pop(lua,1); /* remove the error handler from the stack. */
            /* The error is sent to the client by luaCreateFunction()
             * itself when it returns REDIS_ERR. */
            return;
        }

        // 现在 lua 中已经有 funcname 这个全局变量了,将其读取并入栈,
        // 准备调用
        /* Now the following is guaranteed to return non nil */
        lua_getglobal(lua, funcname);
        redisAssert(!lua_isnil(lua,-1));
    }

    // 设置参数,包括键和值
    /* Populate the argv and keys table accordingly to the arguments that
     * EVAL received. */
    luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys);
    luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys);

    // 选择数据集,lua_client 有专用的数据集
    /* Select the right DB in the context of the Lua client */
    selectDb(server.lua_client,c->db->id);

    // 设置超时回调函数,以在 lua 脚本执行过长时间的时候停止脚本的运行
    /* Set a hook in order to be able to stop the script execution if it
     * is running for too much time.
     * We set the hook only if the time limit is enabled as the hook will
     * make the Lua script execution slower. */
    server.lua_caller = c;
    server.lua_time_start = ustime()/1000;
    server.lua_kill = 0;
    if (server.lua_time_limit > 0 && server.masterhost == NULL) {
        // 当 lua 解释器执行了 100000,luaMaskCountHook() 会被调用
        lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
        delhook = 1;
    }

    // 现在,我们确定函数已经注册成功了.可以直接调用 lua 脚本
    /* At this point whether this script was never seen before or if it was
     * already defined, we can call it. We have zero arguments and expect
     * a single return value. */
    err = lua_pcall(lua,0,1,-2);

    // 删除超时回调函数
    /* Perform some cleanup that we need to do both on error and success. */
    if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */

    // 如果已经超时了,说明 lua 脚本已在超时后背 SCRPIT KILL 终结了
    // 恢复监听发送 lua 脚本命令的客户端
    if (server.lua_timedout) {
        server.lua_timedout = 0;
        /* Restore the readable handler that was unregistered when the
         * script timeout was detected. */
        aeCreateFileEvent(server.el,c->fd,AE_READABLE,
                          readQueryFromClient,c);
    }
    // lua_caller 置空
    server.lua_caller = NULL;

    // 执行 lua 脚本用的是 lua 脚本执行专用的数据集。现在恢复原有的数据集
    selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */

    // Garbage collection 垃圾回收
    lua_gc(lua,LUA_GCSTEP,1);

    // 处理执行 lua 脚本的错误
    if (err) {
        // 告知客户端
        addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
            funcname, lua_tostring(lua,-1));
        lua_pop(lua,2); /* Consume the Lua reply and remove error handler. */

    // 成功了
    } else {
        /* On success convert the Lua return value into Redis protocol, and
         * send it to * the client. */
        luaReplyToRedisReply(c,lua); /* Convert and consume the reply. */
        lua_pop(lua,1); /* Remove the error handler. */
    }

    // 将 lua 脚本发布到主从复制上,并写入 AOF 文件
    /* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless
     * we are sure that the script was already in the context of all the
     * attached slaves *and* the current AOF file if enabled.
     *
     * To do so we use a cache of SHA1s of scripts that we already propagated
     * as full EVAL, that's called the Replication Script Cache.
     *
     * For repliation, everytime a new slave attaches to the master, we need to
     * flush our cache of scripts that can be replicated as EVALSHA, while
     * for AOF we need to do so every time we rewrite the AOF file. */
    if (evalsha) {
        if (!replicationScriptCacheExists(c->argv[1]->ptr)) {
            /* This script is not in our script cache, replicate it as
             * EVAL, then add it into the script cache, as from now on
             * slaves and AOF know about it. */

            // 从 server.lua_scripts 获取 lua 脚本
            // c->argv[1]->ptr 是 SHA1
            robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);

            // 添加到主从复制专用的脚本缓存中
            replicationScriptCacheAdd(c->argv[1]->ptr);
            redisAssertWithInfo(c,NULL,script != NULL);

            // 重写命令
            // 参数 1 为:EVAL
            // 参数 2 为:lua_script
            // 如此一来在执行 AOF 持久化和主从复制的时候,lua 脚本就能得到传播
            rewriteClientCommandArgument(c,0,
                resetRefCount(createStringObject("EVAL",4)));
            rewriteClientCommandArgument(c,1,script);
        }
    }
}