本文次要钻研一下claudb的scripting command

AbstractEvalCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/AbstractEvalCommand.java

abstract class AbstractEvalCommand implements DBCommand {  @Override  public RedisToken execute(Database db, Request request) {    return script(request).map(script -> execute(request, script))        .getOrElse(error("NOSCRIPT No matching script. Please use EVAL"));  }  private RedisToken execute(Request request, SafeString script) {    int numParams = parseInt(request.getParam(1).toString());    if (numParams + 2 > request.getLength()) {      return error("invalid number of arguments");    }    List<SafeString> params = request.getParams().stream().skip(2).collect(toList());    List<SafeString> keys = readParams(numParams, params);    List<SafeString> argv = readArguments(numParams, params);    return LuaInterpreter.buildFor(request).execute(script, keys, argv);  }  protected abstract Option<SafeString> script(Request request);  private List<SafeString> readParams(int numParams, List<SafeString> params) {    List<SafeString> keys = new LinkedList<>();    for (int i = 0; i < numParams; i++) {      keys.add(params.get(i));    }    return keys;  }  private List<SafeString> readArguments(int numParams, List<SafeString> params) {    List<SafeString> argv = new LinkedList<>();    for (int i = numParams; i < params.size(); i++) {      argv.add(params.get(i));    }    return argv;  }}
  • AbstractEvalCommand实现了DBCommand接口,其execute办法先通过子类实现的script办法获取SafeString,而后再外部的execute办法执行脚本;execute办法先解析keys、argv,而后通过LuaInterpreter.buildFor(request).execute(script, keys, argv)执行lua脚本

EvalCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/EvalCommand.java

@Command("eval")@ParamLength(2)public class EvalCommand extends AbstractEvalCommand {  @Override  protected Option<SafeString> script(Request request) {    return Option.some(request.getParam(0));  }}
  • EvalCommand继承了AbstractEvalCommand,其script办法取request.getParam(0)

EvalShaCommand

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/EvalShaCommand.java

@Command("evalsha")@ParamLength(2)public class EvalShaCommand extends AbstractEvalCommand {  @Override  protected Option<SafeString> script(Request request) {    DBServerState server = getServerState(request.getServerContext());    return server.getScript(request.getParam(0));  }}
  • EvalShaCommand继承了AbstractEvalCommand,其script办法先通过getServerState(request.getServerContext())获取server,再执行server.getScript(request.getParam(0))

ScriptCommands

claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/ScriptCommands.java

@ParamLength(1)@Command("script")public class ScriptCommands implements DBCommand {  @Override  public RedisToken execute(Database db, Request request) {    return Pattern1.<Request, RedisToken>build()        .when(isCommand("load"))          .then(this::load)        .when(isCommand("exists"))          .then(this::exists)        .when(isCommand("flush"))          .then(this::flush)        .otherwise()          .then(this::unknownCommand)        .apply(request);  }  private RedisToken unknownCommand(Request request) {    return RedisToken.error("Unknown SCRIPT subcommand: " + request.getParam(0));  }  private RedisToken load(Request request) {    SafeString script = request.getParam(1);    return Try.of(() -> digest(script)).map(sha1 -> {      DBServerState server = getServerState(request.getServerContext());      server.saveScript(safeString(sha1), script);      return RedisToken.string(sha1);    }).getOrElse(RedisToken.error("ERR cannot generate sha1 sum for script: " + script));  }  private RedisToken exists(Request request) {    DBServerState server = getServerState(request.getServerContext());    return integer(server.getScript(request.getParam(1)).isPresent());  }  private RedisToken flush(Request request) {    getServerState(request.getServerContext()).cleanScripts();    return RedisToken.responseOk();  }  private String digest(SafeString script) throws NoSuchAlgorithmException {    MessageDigest digest = MessageDigest.getInstance("SHA-1");    return new SafeString(digest.digest(script.getBytes())).toHexString();  }  private Matcher1<Request> isCommand(String command) {    return request -> request.getParam(0).toString().toLowerCase().equals(command);  }}
  • ScriptCommands实现了DBCommand接口,其execute办法反对load、exists、flush命令;其中load办法通过server.saveScript(safeString(sha1), script)来保留script;其exists办法通过server.getScript(request.getParam(1)).isPresent()来判断script是否存在;其flush办法执行getServerState(request.getServerContext()).cleanScripts()

小结

claudb scripting相干的command有EvalCommand、EvalShaCommand、ScriptCommands

doc

  • command/scripting