package org.swisspush.redisques.lua; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; import io.vertx.redis.RedisClient; import org.apache.commons.codec.digest.DigestUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * Holds the state of a lua script. */ public class LuaScriptState { private LuaScript luaScriptType; /** the script itself */ private String script; /** the sha, over which the script can be accessed in redis */ private String sha; private RedisClient redisClient; private Logger log = LoggerFactory.getLogger(LuaScriptState.class); public LuaScriptState(LuaScript luaScriptType, RedisClient redisClient) { this.luaScriptType = luaScriptType; this.redisClient = redisClient; this.composeLuaScript(luaScriptType); this.loadLuaScript(new RedisCommandDoNothing(), 0); } /** * Reads the script from the classpath and removes logging output if logoutput is false. * The script is stored in the class member script. * @param luaScriptType */ private void composeLuaScript(LuaScript luaScriptType) { log.info("read the lua script for script type: " + luaScriptType); this.script = readLuaScriptFromClasspath(luaScriptType); this.sha = DigestUtils.sha1Hex(this.script); } private String readLuaScriptFromClasspath(LuaScript luaScriptType) { BufferedReader in = new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream(luaScriptType.getFile()))); StringBuilder sb; try { sb = new StringBuilder(); String line; while ((line = in.readLine()) != null) { sb.append(line).append("\n"); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { in.close(); } catch (IOException e) { // Ignore } } return sb.toString(); } /** * Rereads the lua script, eg. if the loglevel changed. */ public void recomposeLuaScript() { this.composeLuaScript(luaScriptType); } /** * Load the get script into redis and store the sha in the class member sha. * @param redisCommand the redis command that should be executed, after the script is loaded. * @param executionCounter a counter to control recursion depth */ public void loadLuaScript(final RedisCommand redisCommand, int executionCounter) { final int executionCounterIncr = ++executionCounter; // check first if the lua script already exists in the store redisClient.scriptExists(this.sha, resultArray -> { if(resultArray.failed()){ log.error("Error checking whether lua script exists", resultArray.cause()); return; } Long exists = resultArray.result().getLong(0); // if script already if(Long.valueOf(1).equals(exists)) { log.debug("RedisStorage script already exists in redis cache: " + luaScriptType); redisCommand.exec(executionCounterIncr); } else { log.info("load lua script for script type: " + luaScriptType); redisClient.scriptLoad(script, stringAsyncResult -> { String newSha = stringAsyncResult.result(); log.info("got sha from redis for lua script: " + luaScriptType + ": " + newSha); if(!newSha.equals(sha)) { log.warn("the sha calculated by myself: " + sha + " doesn't match with the sha from redis: " + newSha + ". We use the sha from redis"); } sha = newSha; log.info("execute redis command for script type: " + luaScriptType + " with new sha: " + sha); redisCommand.exec(executionCounterIncr); }); } }); } public String getScript() { return script; } public void setScript(String script) { this.script = script; } public String getSha() { return sha; } public void setSha(String sha) { this.sha = sha; } }