static int rl_key_get_hash_ignore_expire(struct rlite *db, unsigned char digest[20], unsigned char *type, long *string_page, long *value_page, unsigned long long *expires, long *version, int ignore_expire) { int retval; rl_btree *btree; rl_key *key_obj; RL_CALL(rl_get_key_btree, RL_OK, db, &btree, 0); void *tmp = NULL; retval = rl_btree_find_score(db, btree, digest, &tmp, NULL, NULL); if (retval == RL_FOUND) { key_obj = tmp; if (ignore_expire == 0 && key_obj->expires != 0 && key_obj->expires <= rl_mstime()) { retval = RL_DELETED; goto cleanup; } else { if (type) { *type = key_obj->type; } if (string_page) { *string_page = key_obj->string_page; } if (value_page) { *value_page = key_obj->value_page; } if (expires) { *expires = key_obj->expires; } if (version) { *version = key_obj->version; } } } cleanup: return retval; }
int rl_key_delete_with_value(struct rlite *db, const unsigned char *key, long keylen) { int retval; unsigned char identifier; long value_page; unsigned long long expires; RL_CALL(rl_key_get_ignore_expire, RL_FOUND, db, key, keylen, &identifier, NULL, &value_page, &expires, NULL, 1); RL_CALL(rl_key_delete_value, RL_OK, db, identifier, value_page); RL_CALL(rl_key_delete, RL_OK, db, key, keylen); retval = expires != 0 && expires <= rl_mstime() ? RL_NOT_FOUND : RL_OK; cleanup: return retval; }
void luaMaskCountHook(lua_State *lua, lua_Debug *UNUSED(ar)) { unsigned long long elapsed; elapsed = rl_mstime() - lua_time_start; if (elapsed >= lua_time_limit && lua_timedout == 0) { rliteLog(RLITE_WARNING,"Lua slow script detected: still in execution after %lld milliseconds. You can try killing the script using the SCRIPT KILL command.",elapsed); lua_timedout = 1; } if (lua_kill) { rliteLog(RLITE_WARNING, "Lua script killed by user with SCRIPT KILL."); lua_pushstring(lua, "Script killed by user with SCRIPT KILL..."); lua_error(lua); } }
void evalGenericCommand(rliteClient *c, int evalsha) { scriptingInit(); lua_client->context = c->context; 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. */ rliteSrand48(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. */ lua_random_dirty = 0; lua_write_dirty = 0; /* Get the number of arguments that are keys */ if (getLongLongFromObjectOrReply(c,c->argv[2],c->argvlen[2],&numkeys,NULL) != RLITE_OK) return; if (numkeys > (c->argc - 3)) { c->reply = createErrorObject("Number of keys can't be greater than number of args"); return; } else if (numkeys < 0) { c->reply = createErrorObject("Number of keys can't be negative"); 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],c->argvlen[1]); } else { /* We already have the SHA if it is a EVALSHA */ int j; char *sha = c->argv[1]; /* Convert to lowercase. We don't use tolower since the function * managed to always show up in the profiler output consuming * a non trivial amount of time. */ for (j = 0; j < 40; j++) funcname[j+2] = (sha[j] >= 'A' && sha[j] <= 'Z') ? sha[j]+('a'-'A') : sha[j]; funcname[42] = '\0'; char *body; long bodylen; int retval = getScript(c, funcname + 2, &body, &bodylen); if (retval != RL_OK) { c->reply = createErrorObject(RLITE_NOSCRIPTERR); return; } luaCreateFunction(c, lua, funcname, body, bodylen); rl_free(body); } /* Push the pcall error handler function on the stack. */ lua_getglobal(lua, "__rlite__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. */ c->reply = createErrorObject(RLITE_NOSCRIPTERR); return; } if (luaCreateFunction(c,lua,funcname,c->argv[1], c->argvlen[1]) == RLITE_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 RLITE_ERR. */ return; } /* Now the following is guaranteed to return non nil */ lua_getglobal(lua, funcname); if (lua_isnil(lua,-1)) { // TODO: panic return; } } /* Populate the argv and keys table accordingly to the arguments that * EVAL received. */ luaSetGlobalArray(lua,"KEYS",c->argv+3,c->argvlen+3,numkeys); luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argvlen+3+numkeys,c->argc-3-numkeys); /* 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. */ lua_caller = c; lua_time_start = rl_mstime(); lua_kill = 0; if (lua_time_limit > 0) { 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 (lua_timedout) { lua_timedout = 0; /* Restore the readable handler that was unregistered when the * script timeout was detected. */ } lua_caller = NULL; /* Call the Lua garbage collector from time to time to avoid a * full cycle performed by Lua, which adds too latency. * * The call is performed every LUA_GC_CYCLE_PERIOD executed commands * (and for LUA_GC_CYCLE_PERIOD collection steps) because calling it * for every command uses too much CPU. */ #define LUA_GC_CYCLE_PERIOD 50 { static long gc_count = 0; gc_count++; if (gc_count == LUA_GC_CYCLE_PERIOD) { lua_gc(lua,LUA_GCSTEP,LUA_GC_CYCLE_PERIOD); gc_count = 0; } } if (err) { char err[1024]; snprintf(err, 1024, "Error running script (call to %s): %s\n", funcname, lua_tostring(lua,-1)); c->reply = createErrorObject(err); 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. */ } }