redis-aof-3

2021-12-05

在我们安装好redis之后,屏幕上会显示

image-20211206191641974

https://github.com/redis/redis/pull/9898

当我们替换dict.c的pr的时候,运行make test发现老是报错,说明代码是问题的,于是就根据make test进行一下定位。

第一次make test

image-20211206194834661

第二次make test

image-20211206195012915

第三次make test

image-20211206195057899

可以看到主要报错的日志就是Unknown subcommand or wrong number of arguments for ‘set’,但是出问题的命令是随机的。

可以看到redis中只有一个地方有这个日志打印

/* Add a suggestive error reply.
 * This function is typically invoked by from commands that support
 * subcommands in response to an unknown subcommand or argument error. */
void addReplySubcommandSyntaxError(client *c) {
    sds cmd = sdsnew((char*) c->argv[0]->ptr);
    sdstoupper(cmd);
    addReplyErrorFormat(c,
        "Unknown subcommand or wrong number of arguments for '%s'. Try %s HELP.",
        (char*)c->argv[1]->ptr,cmd);
    sdsfree(cmd);
}

image-20211206203515087

OBJECT IDLETIME1 name 1000

所以是lookupCommand为空,

struct redisCommand *lookupCommand(robj **argv, int argc) {
    return lookupCommandLogic(server.commands,argv,argc);
}

所以应该师server.commands中没有加载入正确的命令

有一个方法populateCommandTable在initServerConfig中加载命令,initServerConfig又在server.c的main方法中被调用。

/* Populates the Redis Command Table starting from the hard coded list
 * we have on top of server.c file. */
void populateCommandTable(void) {
    int j;
    int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);

    for (j = 0; j < numcommands; j++) {
        struct redisCommand *c = redisCommandTable+j;
        int retval1, retval2;

        /* Translate the command string flags description into an actual
         * set of flags. */
        parseCommandFlags(c,c->sflags);

        if (!(c->flags & CMD_SENTINEL) && server.sentinel_mode)
            continue;

        if (c->flags & CMD_ONLY_SENTINEL && !server.sentinel_mode)
            continue;

        populateCommandStructure(c);

        retval1 = dictAdd(server.commands, sdsnew(c->name), c);
        /* Populate an additional dictionary that will be unaffected
         * by rename-command statements in redis.conf. */
        retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
        serverAssert(retval1 == DICT_OK && retval2 == DICT_OK);
    }
}

我们在源代码中的dictfind中打断点,发现启动的时候没有调用到dictFind,所以问题不会出现在这里。于是可以判断问题还是在,后面传参或者查找的时候失败了

/* Now lookup the command and check ASAP about trivial error conditions
     * such as wrong arity, bad command name and so forth. */
    c->cmd = c->lastcmd = lookupCommand(c->argv,c->argc);
    if (!c->cmd) {
        if (lookupCommandBySds(c->argv[0]->ptr)) {
            /* If we can't find the command but argv[0] by itself is a command
             * it means we're dealing with an invalid subcommand. Print Help. */
            addReplySubcommandSyntaxError(c);
struct redisCommand *lookupCommand(robj **argv, int argc) {
    return lookupCommandLogic(server.commands,argv,argc);
}

struct redisCommand *lookupCommandLogic(dict *commands, robj **argv, int argc) {
    struct redisCommand *base_cmd = dictFetchValue(commands, argv[0]->ptr);
    int has_subcommands = base_cmd && base_cmd->subcommands_dict;
    if (argc == 1 || !has_subcommands) {
        /* Note: It is possible that base_cmd->proc==NULL (e.g. CONFIG) */
        return base_cmd;
    } else {
        /* Note: Currently we support just one level of subcommands */
        return dictFetchValue(base_cmd->subcommands_dict, argv[1]->ptr);
    }
}
struct redisCommand {
    /* Declarative data */
    char *name;
    redisCommandProc *proc;
    int arity;
    char *sflags;   /* Flags as string representation, one char per flag. */
    keySpec key_specs_static[STATIC_KEY_SPECS_NUM];
    /* Use a function to determine keys arguments in a command line.
     * Used for Redis Cluster redirect (may be NULL) */
    redisGetKeysProc *getkeys_proc;
    /* Array of subcommands (may be NULL) */
    struct redisCommand *subcommands;

    /* Runtime data */
    uint64_t flags; /* The actual flags, obtained from the 'sflags' field. */
    /* What keys should be loaded in background when calling this command? */
    long long microseconds, calls, rejected_calls, failed_calls;
    int id;     /* Command ID. This is a progressive ID starting from 0 that
                   is assigned at runtime, and is used in order to check
                   ACLs. A connection is able to execute a given command if
                   the user associated to the connection has this command
                   bit set in the bitmap of allowed commands. */
    keySpec *key_specs;
    keySpec legacy_range_key_spec; /* The legacy (first,last,step) key spec is
                                     * still maintained (if applicable) so that
                                     * we can still support the reply format of
                                     * COMMAND INFO and COMMAND GETKEYS */
    int key_specs_num;
    int key_specs_max;
    int movablekeys; /* See populateCommandMovableKeys */
    dict *subcommands_dict;
    struct redisCommand *parent;
};