mirror of
https://github.com/fluencelabs/redis
synced 2025-03-19 09:00:51 +00:00
In Redis RDB check: better error reporting.
This commit is contained in:
parent
e97fadb045
commit
e9f31ba9c2
17
src/rdb.c
17
src/rdb.c
@ -43,11 +43,20 @@
|
|||||||
|
|
||||||
#define rdbExitReportCorruptRDB(reason) rdbCheckThenExit(reason, __LINE__);
|
#define rdbExitReportCorruptRDB(reason) rdbCheckThenExit(reason, __LINE__);
|
||||||
|
|
||||||
|
extern int rdbCheckMode;
|
||||||
|
void rdbCheckError(const char *fmt, ...);
|
||||||
|
|
||||||
void rdbCheckThenExit(char *reason, int where) {
|
void rdbCheckThenExit(char *reason, int where) {
|
||||||
serverLog(LL_WARNING, "Corrupt RDB detected at rdb.c:%d (%s). "
|
if (!rdbCheckMode) {
|
||||||
"Running 'redis-check-rdb %s'",
|
serverLog(LL_WARNING, "Corrupt RDB detected at rdb.c:%d (%s). "
|
||||||
where, reason, server.rdb_filename);
|
"Running 'redis-check-rdb %s'",
|
||||||
redis_check_rdb(server.rdb_filename);
|
where, reason, server.rdb_filename);
|
||||||
|
char *argv[2] = {"",server.rdb_filename};
|
||||||
|
redis_check_rdb_main(2,argv);
|
||||||
|
} else {
|
||||||
|
rdbCheckError("Internal error in RDB reading function at rdb.c:%d (%s)",
|
||||||
|
where, reason);
|
||||||
|
}
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +32,10 @@
|
|||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
void createSharedObjects(void);
|
||||||
void rdbLoadProgressCallback(rio *r, const void *buf, size_t len);
|
void rdbLoadProgressCallback(rio *r, const void *buf, size_t len);
|
||||||
long long rdbLoadMillisecondTime(rio *rdb);
|
long long rdbLoadMillisecondTime(rio *rdb);
|
||||||
|
int rdbCheckMode = 0;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
rio *rio;
|
rio *rio;
|
||||||
@ -44,10 +46,34 @@ struct {
|
|||||||
int doing; /* The state while reading the RDB. */
|
int doing; /* The state while reading the RDB. */
|
||||||
} rdbstate;
|
} rdbstate;
|
||||||
|
|
||||||
|
/* At every loading step try to remember what we were about to do, so that
|
||||||
|
* we can log this information when an error is encountered. */
|
||||||
#define RDB_CHECK_DOING_START 0
|
#define RDB_CHECK_DOING_START 0
|
||||||
#define RDB_CHECK_DOING_READ_EXPIRE 1
|
#define RDB_CHECK_DOING_READ_TYPE 1
|
||||||
#define RDB_CHECK_DOING_READ_KEY 2
|
#define RDB_CHECK_DOING_READ_EXPIRE 2
|
||||||
#define RDB_CHECK_DOING_READ_VALUE 3
|
#define RDB_CHECK_DOING_READ_KEY 3
|
||||||
|
#define RDB_CHECK_DOING_READ_OBJECT_VALUE 4
|
||||||
|
#define RDB_CHECK_DOING_CHECK_SUM 5
|
||||||
|
#define RDB_CHECK_DOING_READ_LEN 6
|
||||||
|
#define RDB_CHECK_DOING_READ_AUX 7
|
||||||
|
|
||||||
|
char *rdb_check_doing_string[] = {
|
||||||
|
"start",
|
||||||
|
"read-type",
|
||||||
|
"read-expire",
|
||||||
|
"read-key",
|
||||||
|
"read-object-value",
|
||||||
|
"check-sum",
|
||||||
|
"read-len",
|
||||||
|
"read-aux"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Show a few stats collected into 'rdbstate' */
|
||||||
|
void rdbShowGenericInfo(void) {
|
||||||
|
printf("[info] %lu keys read\n", rdbstate.keys);
|
||||||
|
printf("[info] %lu expires\n", rdbstate.expires);
|
||||||
|
printf("[info] %lu already expired\n", rdbstate.already_expired);
|
||||||
|
}
|
||||||
|
|
||||||
/* Called on RDB errors. Provides details about the RDB and the offset
|
/* Called on RDB errors. Provides details about the RDB and the offset
|
||||||
* we were when the error was detected. */
|
* we were when the error was detected. */
|
||||||
@ -59,12 +85,16 @@ void rdbCheckError(const char *fmt, ...) {
|
|||||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
printf("*** RDB CHECK FAILED: %s ***\n", msg);
|
printf("--- RDB ERROR DETECTED ---\n");
|
||||||
printf("AT RDB OFFSET: %llu\n",
|
printf("[offset %llu] %s\n",
|
||||||
(unsigned long long) (rdbstate.rio ?
|
(unsigned long long) (rdbstate.rio ?
|
||||||
rdbstate.rio->processed_bytes : 0));
|
rdbstate.rio->processed_bytes : 0), msg);
|
||||||
|
printf("[additional info] While doing: %s\n",
|
||||||
|
rdb_check_doing_string[rdbstate.doing]);
|
||||||
if (rdbstate.key)
|
if (rdbstate.key)
|
||||||
printf("READING KEY: %s\n", (char*)rdbstate.key->ptr);
|
printf("[additional info] Reading key '%s'\n",
|
||||||
|
(char*)rdbstate.key->ptr);
|
||||||
|
rdbShowGenericInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print informations during RDB checking. */
|
/* Print informations during RDB checking. */
|
||||||
@ -138,15 +168,18 @@ int redis_check_rdb(char *rdbfilename) {
|
|||||||
expiretime = -1;
|
expiretime = -1;
|
||||||
|
|
||||||
/* Read type. */
|
/* Read type. */
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;
|
||||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||||
|
|
||||||
/* Handle special types. */
|
/* Handle special types. */
|
||||||
if (type == RDB_OPCODE_EXPIRETIME) {
|
if (type == RDB_OPCODE_EXPIRETIME) {
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;
|
||||||
/* EXPIRETIME: load an expire associated with the next key
|
/* EXPIRETIME: load an expire associated with the next key
|
||||||
* to load. Note that after loading an expire we need to
|
* to load. Note that after loading an expire we need to
|
||||||
* load the actual type, and continue. */
|
* load the actual type, and continue. */
|
||||||
if ((expiretime = rdbLoadTime(&rdb)) == -1) goto eoferr;
|
if ((expiretime = rdbLoadTime(&rdb)) == -1) goto eoferr;
|
||||||
/* We read the time so we need to read the object type again. */
|
/* We read the time so we need to read the object type again. */
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;
|
||||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||||
/* the EXPIRETIME opcode specifies time in seconds, so convert
|
/* the EXPIRETIME opcode specifies time in seconds, so convert
|
||||||
* into milliseconds. */
|
* into milliseconds. */
|
||||||
@ -154,14 +187,17 @@ int redis_check_rdb(char *rdbfilename) {
|
|||||||
} else if (type == RDB_OPCODE_EXPIRETIME_MS) {
|
} else if (type == RDB_OPCODE_EXPIRETIME_MS) {
|
||||||
/* EXPIRETIME_MS: milliseconds precision expire times introduced
|
/* EXPIRETIME_MS: milliseconds precision expire times introduced
|
||||||
* with RDB v3. Like EXPIRETIME but no with more precision. */
|
* with RDB v3. Like EXPIRETIME but no with more precision. */
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;
|
||||||
if ((expiretime = rdbLoadMillisecondTime(&rdb)) == -1) goto eoferr;
|
if ((expiretime = rdbLoadMillisecondTime(&rdb)) == -1) goto eoferr;
|
||||||
/* We read the time so we need to read the object type again. */
|
/* We read the time so we need to read the object type again. */
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;
|
||||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||||
} else if (type == RDB_OPCODE_EOF) {
|
} else if (type == RDB_OPCODE_EOF) {
|
||||||
/* EOF: End of file, exit the main loop. */
|
/* EOF: End of file, exit the main loop. */
|
||||||
break;
|
break;
|
||||||
} else if (type == RDB_OPCODE_SELECTDB) {
|
} else if (type == RDB_OPCODE_SELECTDB) {
|
||||||
/* SELECTDB: Select the specified database. */
|
/* SELECTDB: Select the specified database. */
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_LEN;
|
||||||
if ((dbid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
if ((dbid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
||||||
goto eoferr;
|
goto eoferr;
|
||||||
rdbCheckInfo("Selecting DB ID %d", dbid);
|
rdbCheckInfo("Selecting DB ID %d", dbid);
|
||||||
@ -170,6 +206,7 @@ int redis_check_rdb(char *rdbfilename) {
|
|||||||
/* RESIZEDB: Hint about the size of the keys in the currently
|
/* RESIZEDB: Hint about the size of the keys in the currently
|
||||||
* selected data base, in order to avoid useless rehashing. */
|
* selected data base, in order to avoid useless rehashing. */
|
||||||
uint64_t db_size, expires_size;
|
uint64_t db_size, expires_size;
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_LEN;
|
||||||
if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
||||||
goto eoferr;
|
goto eoferr;
|
||||||
if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
||||||
@ -182,6 +219,7 @@ int redis_check_rdb(char *rdbfilename) {
|
|||||||
*
|
*
|
||||||
* An AUX field is composed of two strings: key and value. */
|
* An AUX field is composed of two strings: key and value. */
|
||||||
robj *auxkey, *auxval;
|
robj *auxkey, *auxval;
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_AUX;
|
||||||
if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
||||||
if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
||||||
|
|
||||||
@ -189,13 +227,20 @@ int redis_check_rdb(char *rdbfilename) {
|
|||||||
decrRefCount(auxkey);
|
decrRefCount(auxkey);
|
||||||
decrRefCount(auxval);
|
decrRefCount(auxval);
|
||||||
continue; /* Read type again. */
|
continue; /* Read type again. */
|
||||||
|
} else {
|
||||||
|
if (!rdbIsObjectType(type)) {
|
||||||
|
rdbCheckError("Invalid object type: %d", type);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read key */
|
/* Read key */
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_KEY;
|
||||||
if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
if ((key = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
||||||
rdbstate.key = key;
|
rdbstate.key = key;
|
||||||
rdbstate.keys++;
|
rdbstate.keys++;
|
||||||
/* Read value */
|
/* Read value */
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;
|
||||||
if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;
|
if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;
|
||||||
/* Check if the key already expired. This function is used when loading
|
/* Check if the key already expired. This function is used when loading
|
||||||
* an RDB file from disk, either at startup, or when an RDB was
|
* an RDB file from disk, either at startup, or when an RDB was
|
||||||
@ -213,6 +258,7 @@ int redis_check_rdb(char *rdbfilename) {
|
|||||||
if (rdbver >= 5 && server.rdb_checksum) {
|
if (rdbver >= 5 && server.rdb_checksum) {
|
||||||
uint64_t cksum, expected = rdb.cksum;
|
uint64_t cksum, expected = rdb.cksum;
|
||||||
|
|
||||||
|
rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM;
|
||||||
if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;
|
if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;
|
||||||
memrev64ifbe(&cksum);
|
memrev64ifbe(&cksum);
|
||||||
if (cksum == 0) {
|
if (cksum == 0) {
|
||||||
@ -235,18 +281,20 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */
|
|||||||
*
|
*
|
||||||
* The function never returns, but exits with the status code according
|
* The function never returns, but exits with the status code according
|
||||||
* to success (RDB is sane) or error (RDB is corrupted). */
|
* to success (RDB is sane) or error (RDB is corrupted). */
|
||||||
int redis_check_rdb_main(char **argv, int argc) {
|
int redis_check_rdb_main(int argc, char **argv) {
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
fprintf(stderr, "Usage: %s <rdb-file-name>\n", argv[0]);
|
fprintf(stderr, "Usage: %s <rdb-file-name>\n", argv[0]);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
createSharedObjects(); /* Needed for loading. */
|
createSharedObjects(); /* Needed for loading. */
|
||||||
server.loading_process_events_interval_bytes = 0;
|
server.loading_process_events_interval_bytes = 0;
|
||||||
|
rdbCheckMode = 1;
|
||||||
rdbCheckInfo("Checking RDB file %s", argv[1]);
|
rdbCheckInfo("Checking RDB file %s", argv[1]);
|
||||||
rdbCheckSetupSignals();
|
rdbCheckSetupSignals();
|
||||||
int retval = redis_check_rdb(argv[1]);
|
int retval = redis_check_rdb(argv[1]);
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
rdbCheckInfo("\\o/ RDB looks OK! \\o/");
|
rdbCheckInfo("\\o/ RDB looks OK! \\o/");
|
||||||
|
rdbShowGenericInfo();
|
||||||
}
|
}
|
||||||
exit(retval);
|
exit(retval);
|
||||||
}
|
}
|
||||||
|
@ -4033,7 +4033,7 @@ int main(int argc, char **argv) {
|
|||||||
* the program main. However the program is part of the Redis executable
|
* the program main. However the program is part of the Redis executable
|
||||||
* so that we can easily execute an RDB check on loading errors. */
|
* so that we can easily execute an RDB check on loading errors. */
|
||||||
if (strstr(argv[0],"redis-check-rdb") != NULL)
|
if (strstr(argv[0],"redis-check-rdb") != NULL)
|
||||||
redis_check_rdb_main(argv,argc);
|
redis_check_rdb_main(argc,argv);
|
||||||
|
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
j = 1; /* First option to parse in argv[] */
|
j = 1; /* First option to parse in argv[] */
|
||||||
|
@ -1597,7 +1597,7 @@ void sentinelIsRunning(void);
|
|||||||
|
|
||||||
/* redis-check-rdb */
|
/* redis-check-rdb */
|
||||||
int redis_check_rdb(char *rdbfilename);
|
int redis_check_rdb(char *rdbfilename);
|
||||||
int redis_check_rdb_main(char **argv, int argc);
|
int redis_check_rdb_main(int argc, char **argv);
|
||||||
|
|
||||||
/* Scripting */
|
/* Scripting */
|
||||||
void scriptingInit(int setup);
|
void scriptingInit(int setup);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user