mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Use the RSC to replicate EVALSHA unmodified.
This commit uses the Replication Script Cache in order to avoid translating EVALSHA into EVAL whenever possible for both the AOF and slaves.
This commit is contained in:
parent
94ec7db470
commit
f0bf5fd8c7
@ -998,6 +998,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
|||||||
* accumulated by the parent into server.aof_rewrite_buf will start
|
* accumulated by the parent into server.aof_rewrite_buf will start
|
||||||
* with a SELECT statement and it will be safe to merge. */
|
* with a SELECT statement and it will be safe to merge. */
|
||||||
server.aof_selected_db = -1;
|
server.aof_selected_db = -1;
|
||||||
|
replicationScriptCacheFlush();
|
||||||
return REDIS_OK;
|
return REDIS_OK;
|
||||||
}
|
}
|
||||||
return REDIS_OK; /* unreached */
|
return REDIS_OK; /* unreached */
|
||||||
|
@ -591,10 +591,10 @@ dictType migrateCacheDictType = {
|
|||||||
* Keys are sds SHA1 strings, while values are not used at all in the current
|
* Keys are sds SHA1 strings, while values are not used at all in the current
|
||||||
* implementation. */
|
* implementation. */
|
||||||
dictType replScriptCacheDictType = {
|
dictType replScriptCacheDictType = {
|
||||||
dictSdsHash, /* hash function */
|
dictSdsCaseHash, /* hash function */
|
||||||
NULL, /* key dup */
|
NULL, /* key dup */
|
||||||
NULL, /* val dup */
|
NULL, /* val dup */
|
||||||
dictSdsKeyCompare, /* key compare */
|
dictSdsKeyCaseCompare, /* key compare */
|
||||||
dictSdsDestructor, /* key destructor */
|
dictSdsDestructor, /* key destructor */
|
||||||
NULL /* val destructor */
|
NULL /* val destructor */
|
||||||
};
|
};
|
||||||
|
@ -1165,6 +1165,10 @@ void resizeReplicationBacklog(long long newsize);
|
|||||||
void replicationSetMaster(char *ip, int port);
|
void replicationSetMaster(char *ip, int port);
|
||||||
void replicationUnsetMaster(void);
|
void replicationUnsetMaster(void);
|
||||||
void refreshGoodSlavesCount(void);
|
void refreshGoodSlavesCount(void);
|
||||||
|
void replicationScriptCacheInit(void);
|
||||||
|
void replicationScriptCacheFlush(void);
|
||||||
|
void replicationScriptCacheAdd(sds sha1);
|
||||||
|
int replicationScriptCacheExists(sds sha1);
|
||||||
|
|
||||||
/* Generic persistence functions */
|
/* Generic persistence functions */
|
||||||
void startLoading(FILE *fp);
|
void startLoading(FILE *fp);
|
||||||
|
@ -546,6 +546,8 @@ void syncCommand(redisClient *c) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
|
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
|
||||||
|
/* Flush the script cache for the new slave. */
|
||||||
|
replicationScriptCacheFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.repl_disable_tcp_nodelay)
|
if (server.repl_disable_tcp_nodelay)
|
||||||
@ -711,6 +713,11 @@ void updateSlavesWaitingBgsave(int bgsaveerr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (startbgsave) {
|
if (startbgsave) {
|
||||||
|
/* Since we are starting a new background save for one or more slaves,
|
||||||
|
* we flush the Replication Script Cache to use EVAL to propagate every
|
||||||
|
* new EVALSHA for the first time, since all the new slaves don't know
|
||||||
|
* about previous scripts. */
|
||||||
|
replicationScriptCacheFlush();
|
||||||
if (rdbSaveBackground(server.rdb_filename) != REDIS_OK) {
|
if (rdbSaveBackground(server.rdb_filename) != REDIS_OK) {
|
||||||
listIter li;
|
listIter li;
|
||||||
|
|
||||||
@ -1471,10 +1478,16 @@ void replicationScriptCacheInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Empty the script cache. Should be called every time we are no longer sure
|
/* Empty the script cache. Should be called every time we are no longer sure
|
||||||
* that every slave knows about all the scripts in our set, for example
|
* that every slave knows about all the scripts in our set, or when the
|
||||||
* every time a new slave connects to this master and performs a full
|
* current AOF "context" is no longer aware of the script. In general we
|
||||||
* resynchronization. There is no need to flush the cache when a partial
|
* should flush the cache:
|
||||||
* resynchronization is performed. */
|
*
|
||||||
|
* 1) Every time a new slave reconnects to this master and performs a
|
||||||
|
* full SYNC (PSYNC does not require flushing).
|
||||||
|
* 2) Every time an AOF rewrite is performed.
|
||||||
|
* 3) Every time we are left without slaves at all, and AOF is off, in order
|
||||||
|
* to reclaim otherwise unused memory.
|
||||||
|
*/
|
||||||
void replicationScriptCacheFlush(void) {
|
void replicationScriptCacheFlush(void) {
|
||||||
dictEmpty(server.repl_scriptcache_dict);
|
dictEmpty(server.repl_scriptcache_dict);
|
||||||
listRelease(server.repl_scriptcache_fifo);
|
listRelease(server.repl_scriptcache_fifo);
|
||||||
@ -1507,7 +1520,7 @@ void replicationScriptCacheAdd(sds sha1) {
|
|||||||
/* Returns non-zero if the specified entry exists inside the cache, that is,
|
/* Returns non-zero if the specified entry exists inside the cache, that is,
|
||||||
* if all the slaves are aware of this script SHA1. */
|
* if all the slaves are aware of this script SHA1. */
|
||||||
int replicationScriptCacheExists(sds sha1) {
|
int replicationScriptCacheExists(sds sha1) {
|
||||||
return dictFetchValue(server.repl_scriptcache_dict,sha1) != NULL;
|
return dictFind(server.repl_scriptcache_dict,sha1) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------- REPLICATION CRON ----------------------------- */
|
/* --------------------------- REPLICATION CRON ----------------------------- */
|
||||||
@ -1624,6 +1637,16 @@ void replicationCron(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If AOF is disabled and we no longer have attached slaves, we can
|
||||||
|
* free our Replication Script Cache as there is no need to propagate
|
||||||
|
* EVALSHA at all. */
|
||||||
|
if (listLength(server.slaves) == 0 &&
|
||||||
|
server.aof_state == REDIS_AOF_OFF &&
|
||||||
|
listLength(server.repl_scriptcache_fifo) != 0)
|
||||||
|
{
|
||||||
|
replicationScriptCacheFlush();
|
||||||
|
}
|
||||||
|
|
||||||
/* Refresh the number of slaves with lag <= min-slaves-max-lag. */
|
/* Refresh the number of slaves with lag <= min-slaves-max-lag. */
|
||||||
refreshGoodSlavesCount();
|
refreshGoodSlavesCount();
|
||||||
}
|
}
|
||||||
|
@ -655,6 +655,10 @@ void scriptingInit(void) {
|
|||||||
* to global variables. */
|
* to global variables. */
|
||||||
scriptingEnableGlobalsProtection(lua);
|
scriptingEnableGlobalsProtection(lua);
|
||||||
|
|
||||||
|
/* Initialize the Replication Script Cache for EVALSHA propagation to
|
||||||
|
* slaves and AOF. */
|
||||||
|
replicationScriptCacheInit();
|
||||||
|
|
||||||
server.lua = lua;
|
server.lua = lua;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -931,23 +935,29 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
|||||||
luaReplyToRedisReply(c,lua);
|
luaReplyToRedisReply(c,lua);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we have slaves attached we want to replicate this command as
|
/* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless
|
||||||
* EVAL instead of EVALSHA. We do this also in the AOF as currently there
|
* we are sure that the script was already in the context of all the
|
||||||
* is no easy way to propagate a command in a different way in the AOF
|
* attached slaves *and* the current AOF file if enabled.
|
||||||
* and in the replication link.
|
|
||||||
*
|
*
|
||||||
* IMPROVEMENT POSSIBLE:
|
* To do so we use a cache of SHA1s of scripts that we already propagated
|
||||||
* 1) Replicate this command as EVALSHA in the AOF.
|
* as full EVAL, that's called the Replication Script Cache.
|
||||||
* 2) Remember what slave already received a given script, and replicate
|
*
|
||||||
* the EVALSHA against this slaves when possible.
|
* 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 (evalsha) {
|
||||||
robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr);
|
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);
|
||||||
|
|
||||||
redisAssertWithInfo(c,NULL,script != NULL);
|
replicationScriptCacheAdd(c->argv[1]->ptr);
|
||||||
rewriteClientCommandArgument(c,0,
|
redisAssertWithInfo(c,NULL,script != NULL);
|
||||||
resetRefCount(createStringObject("EVAL",4)));
|
rewriteClientCommandArgument(c,0,
|
||||||
rewriteClientCommandArgument(c,1,script);
|
resetRefCount(createStringObject("EVAL",4)));
|
||||||
|
rewriteClientCommandArgument(c,1,script);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user