Redisソース分析(十二)---redis-check-dumpローカルデータベース検出


このファイルは私が今日分析して勉強している間に、ずっと分からないような感じがして、コード量700+のコード、最後にシステムに開放したのはprocess()方法です.ここで言うデータベース検出は、keyに対する検出であり、以下に述べる構造体に用いられる.
/* Data type to hold opcode with optional key name an success status */
/*   key      ,          entry    */
typedef struct {
	//key   
    char* key;
    //  
    int type;
    //       
    char success;
} entry;

その後に関わる多くのAPIはこの構造体に関連しており、このコードが最終的に検出するのはdumpである.rdbのファイルは、検出の後にサイクル冗長検査CRC 64を加える.以下にAPIを点灯します.
int checkType(unsigned char t) /*         obj   ,             */
int readBytes(void *target, long num) /*               num      */
int processHeader(void) /*          ,               */
int loadType(entry *e) /*  entry  obj Type */
int peekType() /*       */
int processTime(int type) /*             */
uint32_t loadLength(int *isencoded) /*  type     */
char *loadIntegerObject(int enctype) /*            ,    ,        */
char* loadLzfStringObject() /*           */
char* loadStringObject() /*               */
int processStringObject(char** store) /*                */
double* loadDoubleValue() /*      double    */
int processDoubleValue(double** store) /*  double          */
int loadPair(entry *e) /*       */
entry loadEntry() /*   entry key    */
void printCentered(int indent, int width, char* body) /*           */
void printValid(uint64_t ops, uint64_t bytes) /*        */
void printSkipped(uint64_t bytes, uint64_t offset) /*   Skipped  bytes     */
void printErrorStack(entry *e) /*          */
void process(void) /* process             */

方法の中にはloadXXX()の方法がたくさんあります.このいくつかのloadの方法は確かに役に立ちます.この検出ファイルでは、作成者はerrorの構造体を人間的に構築し、エラー情報スタックの出力をシミュレートします.
/* Hold a stack of errors */
/*         */
typedef struct {
	//          
    char error[16][1024];
    //     
    size_t offset[16];
    //      
    size_t level;
} errors_t;
static errors_t errors;

異なるレベルは、異なるエラー情報に対応します.APIには比較的重要な方法があり、loadEntryはkey関連の構造体を取得する.
/*   entry key    */
entry loadEntry() {
    entry e = { NULL, -1, 0 };
    uint32_t length, offset[4];

    /* reset error container */
    errors.level = 0;

    offset[0] = CURR_OFFSET;
    //    type
    if (!loadType(&e)) {
        return e;
    }

    offset[1] = CURR_OFFSET;
    if (e.type == REDIS_SELECTDB) {
        if ((length = loadLength(NULL)) == REDIS_RDB_LENERR) {
            SHIFT_ERROR(offset[1], "Error reading database number");
            return e;
        }
        if (length > 63) {
            SHIFT_ERROR(offset[1], "Database number out of range (%d)", length);
            return e;
        }
    } else if (e.type == REDIS_EOF) {
        if (positions[level].offset < positions[level].size) {
            SHIFT_ERROR(offset[0], "Unexpected EOF");
        } else {
            e.success = 1;
        }
        return e;
    } else {
        /* optionally consume expire */
        if (e.type == REDIS_EXPIRETIME ||
            e.type == REDIS_EXPIRETIME_MS) {
            if (!processTime(e.type)) return e;
            if (!loadType(&e)) return e;
        }

        offset[1] = CURR_OFFSET;
        //  loadPair Entry  key
        if (!loadPair(&e)) {
            SHIFT_ERROR(offset[1], "Error for type %s", types[e.type]);
            return e;
        }
    }

    /* all entries are followed by a valid type:
     * e.g. a new entry, SELECTDB, EXPIRE, EOF */
    offset[2] = CURR_OFFSET;
    if (peekType() == -1) {
        SHIFT_ERROR(offset[2], "Followed by invalid type");
        SHIFT_ERROR(offset[0], "Error for type %s", types[e.type]);
        e.success = 0;
    } else {
        e.success = 1;
    }

    return e;
}

その中の肝心な賦値key、valueはloadPair()の方法にあります:
/*       */
int loadPair(entry *e) {
    uint32_t offset = CURR_OFFSET;
    uint32_t i;

    /* read key first */
    //        key 
    char *key;
    if (processStringObject(&key)) {
        e->key = key;
    } else {
        SHIFT_ERROR(offset, "Error reading entry key");
        return 0;
    }

    uint32_t length = 0;
    if (e->type == REDIS_LIST ||
        e->type == REDIS_SET  ||
        e->type == REDIS_ZSET ||
        e->type == REDIS_HASH) {
        if ((length = loadLength(NULL)) == REDIS_RDB_LENERR) {
            SHIFT_ERROR(offset, "Error reading %s length", types[e->type]);
            return 0;
        }
    }

	//  key      value 
    switch(e->type) {
    case REDIS_STRING:
    case REDIS_HASH_ZIPMAP:
    case REDIS_LIST_ZIPLIST:
    case REDIS_SET_INTSET:
    case REDIS_ZSET_ZIPLIST:
    case REDIS_HASH_ZIPLIST:
    	//    ziplist,zipmap                      ,       
        if (!processStringObject(NULL)) {
            SHIFT_ERROR(offset, "Error reading entry value");
            return 0;
        }
    break;
    case REDIS_LIST:
    case REDIS_SET:
        //    2       ,      
        for (i = 0; i < length; i++) {
            offset = CURR_OFFSET;
            if (!processStringObject(NULL)) {
                SHIFT_ERROR(offset, "Error reading element at index %d (length: %d)", i, length);
                return 0;
            }
        }
    break;
    case REDIS_ZSET:
        for (i = 0; i < length; i++) {
            offset = CURR_OFFSET;
            if (!processStringObject(NULL)) {
                SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
                return 0;
            }
            offset = CURR_OFFSET;
            if (!processDoubleValue(NULL)) {
                SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
                return 0;
            }
        }
    break;
    case REDIS_HASH:
        for (i = 0; i < length; i++) {
            offset = CURR_OFFSET;
            if (!processStringObject(NULL)) {
                SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
                return 0;
            }
            offset = CURR_OFFSET;
            if (!processStringObject(NULL)) {
                SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
                return 0;
            }
        }
    break;
    default:
        SHIFT_ERROR(offset, "Type not implemented");
        return 0;
    }
    /* because we're done, we assume success */
    //      ,        
    e->success = 1;
    return 1;
}

e-success=1であれば、このkeyの検出がクリアされたことを示します.なぜかというと、主検出方法process()方法を見てみましょう.
/* process             */
void process(void) {
    uint64_t num_errors = 0, num_valid_ops = 0, num_valid_bytes = 0;
    entry entry;
    //               
    int dump_version = processHeader();

    /* Exclude the final checksum for RDB >= 5. Will be checked at the end. */
    if (dump_version >= 5) {
        if (positions[0].size < 8) {
            printf("RDB version >= 5 but no room for checksum.
"); exit(1); } positions[0].size -= 8; } level = 1; while(positions[0].offset < positions[0].size) { positions[1] = positions[0]; entry = loadEntry(); if (!entry.success) { // Entry printValid(num_valid_ops, num_valid_bytes); printErrorStack(&entry); num_errors++; num_valid_ops = 0; num_valid_bytes = 0; /* search for next valid entry */ uint64_t offset = positions[0].offset + 1; int i = 0; // 3 entries while (!entry.success && offset < positions[0].size) { positions[1].offset = offset; /* find 3 consecutive valid entries */ // 3 entries for (i = 0; i < 3; i++) { entry = loadEntry(); if (!entry.success) break; } /* check if we found 3 consecutive valid entries */ if (i < 3) { offset++; } } /* print how many bytes we have skipped to find a new valid opcode */ if (offset < positions[0].size) { printSkipped(offset - positions[0].offset, offset); } positions[0].offset = offset; } else { num_valid_ops++; num_valid_bytes += positions[1].offset - positions[0].offset; /* advance position */ positions[0] = positions[1]; } free(entry.key); } /* because there is another potential error, * print how many valid ops we have processed */ printValid(num_valid_ops, num_valid_bytes); /* expect an eof */ if (entry.type != REDIS_EOF) { /* last byte should be EOF, add error */ errors.level = 0; SHIFT_ERROR(positions[0].offset, "Expected EOF, got %s", types[entry.type]); /* this is an EOF error so reset type */ entry.type = -1; printErrorStack(&entry); num_errors++; } /* Verify checksum */ // >=5 , if (dump_version >= 5) { uint64_t crc = crc64(0,positions[0].data,positions[0].size); uint64_t crc2; unsigned char *p = (unsigned char*)positions[0].data+positions[0].size; crc2 = ((uint64_t)p[0] << 0) | ((uint64_t)p[1] << 8) | ((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 24) | ((uint64_t)p[4] << 32) | ((uint64_t)p[5] << 40) | ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56); if (crc != crc2) { SHIFT_ERROR(positions[0].offset, "RDB CRC64 does not match."); } else { printf("CRC64 checksum is OK
"); } } /* print summary on errors */ if (num_errors) { printf("
"); printf("Total unprocessable opcodes: %llu
", (unsigned long long) num_errors); } }

検出の詳細原理を知りたければdump.rdbのファイルコンテンツ構造は私たちにまた役立つかもしれません.