Convert check-dump to Redis check-rdb mode

redis-check-dump is now named redis-check-rdb and it runs
as a mode of redis-server instead of an independent binary.

You can now use 'redis-server redis.conf --check-rdb' to check
the RDB defined in redis.conf.  Using argument --check-rdb
checks the RDB and exits.  We could potentially also allow
the server to continue starting if the RDB check succeeds.

This change also enables us to use RDB checking programatically
from inside Redis for certain failure conditions.
This commit is contained in:
Matt Stancliff 2014-05-09 12:06:06 -04:00
parent 9802ec3c83
commit 145473acc5
4 changed files with 60 additions and 45 deletions

View File

@ -117,17 +117,16 @@ endif
REDIS_SERVER_NAME=redis-server REDIS_SERVER_NAME=redis-server
REDIS_SENTINEL_NAME=redis-sentinel REDIS_SENTINEL_NAME=redis-sentinel
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o
REDIS_CLI_NAME=redis-cli REDIS_CLI_NAME=redis-cli
REDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o REDIS_CLI_OBJ=anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o
REDIS_BENCHMARK_NAME=redis-benchmark REDIS_BENCHMARK_NAME=redis-benchmark
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o redis-benchmark.o REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o redis-benchmark.o
REDIS_CHECK_DUMP_NAME=redis-check-dump REDIS_CHECK_RDB_NAME=redis-check-rdb
REDIS_CHECK_DUMP_OBJ=redis-check-dump.o lzf_c.o lzf_d.o crc64.o
REDIS_CHECK_AOF_NAME=redis-check-aof REDIS_CHECK_AOF_NAME=redis-check-aof
REDIS_CHECK_AOF_OBJ=redis-check-aof.o REDIS_CHECK_AOF_OBJ=redis-check-aof.o
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME) all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME)
@echo "" @echo ""
@echo "Hint: It's a good idea to run 'make test' ;)" @echo "Hint: It's a good idea to run 'make test' ;)"
@echo "" @echo ""
@ -178,6 +177,10 @@ $(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME)
# redis-check-rdb
$(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME)
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME)
# redis-cli # redis-cli
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)
@ -186,10 +189,6 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
# redis-check-dump
$(REDIS_CHECK_DUMP_NAME): $(REDIS_CHECK_DUMP_OBJ)
$(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
# redis-check-aof # redis-check-aof
$(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ) $(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ)
$(REDIS_LD) -o $@ $^ $(FINAL_LIBS) $(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
@ -201,7 +200,7 @@ $(REDIS_CHECK_AOF_NAME): $(REDIS_CHECK_AOF_OBJ)
$(REDIS_CC) -c $< $(REDIS_CC) -c $<
clean: clean:
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html
.PHONY: clean .PHONY: clean
@ -257,6 +256,6 @@ install: all
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_DUMP_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_CHECK_RDB_NAME) $(INSTALL_BIN)
$(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN)
@ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)

View File

@ -133,18 +133,13 @@ typedef struct {
char success; char success;
} entry; } entry;
/* Global vars that are actually used as constants. The following double
* values are used for double on-disk serialization, and are initialized
* at runtime to avoid strange compiler optimizations. */
static double R_Zero, R_PosInf, R_NegInf, R_Nan;
#define MAX_TYPES_NUM 256 #define MAX_TYPES_NUM 256
#define MAX_TYPE_NAME_LEN 16 #define MAX_TYPE_NAME_LEN 16
/* store string types for output */ /* store string types for output */
static char types[MAX_TYPES_NUM][MAX_TYPE_NAME_LEN]; static char types[MAX_TYPES_NUM][MAX_TYPE_NAME_LEN];
/* Return true if 't' is a valid object type. */ /* Return true if 't' is a valid object type. */
int checkType(unsigned char t) { static int checkType(unsigned char t) {
/* In case a new object type is added, update the following /* In case a new object type is added, update the following
* condition as necessary. */ * condition as necessary. */
return return
@ -154,7 +149,7 @@ int checkType(unsigned char t) {
} }
/* when number of bytes to read is negative, do a peek */ /* when number of bytes to read is negative, do a peek */
int readBytes(void *target, long num) { static int readBytes(void *target, long num) {
char peek = (num < 0) ? 1 : 0; char peek = (num < 0) ? 1 : 0;
num = (num < 0) ? -num : num; num = (num < 0) ? -num : num;
@ -188,7 +183,7 @@ int processHeader(void) {
return dump_version; return dump_version;
} }
int loadType(entry *e) { static int loadType(entry *e) {
uint32_t offset = CURR_OFFSET; uint32_t offset = CURR_OFFSET;
/* this byte needs to qualify as type */ /* this byte needs to qualify as type */
@ -208,7 +203,7 @@ int loadType(entry *e) {
return 0; return 0;
} }
int peekType() { static int peekType() {
unsigned char t; unsigned char t;
if (readBytes(&t, -1) && (checkType(t))) if (readBytes(&t, -1) && (checkType(t)))
return t; return t;
@ -216,7 +211,7 @@ int peekType() {
} }
/* discard time, just consume the bytes */ /* discard time, just consume the bytes */
int processTime(int type) { static int processTime(int type) {
uint32_t offset = CURR_OFFSET; uint32_t offset = CURR_OFFSET;
unsigned char t[8]; unsigned char t[8];
int timelen = (type == REDIS_EXPIRETIME_MS) ? 8 : 4; int timelen = (type == REDIS_EXPIRETIME_MS) ? 8 : 4;
@ -231,7 +226,7 @@ int processTime(int type) {
return 0; return 0;
} }
uint32_t loadLength(int *isencoded) { static uint32_t loadLength(int *isencoded) {
unsigned char buf[2]; unsigned char buf[2];
uint32_t len; uint32_t len;
int type; int type;
@ -257,7 +252,7 @@ uint32_t loadLength(int *isencoded) {
} }
} }
char *loadIntegerObject(int enctype) { static char *loadIntegerObject(int enctype) {
uint32_t offset = CURR_OFFSET; uint32_t offset = CURR_OFFSET;
unsigned char enc[4]; unsigned char enc[4];
long long val; long long val;
@ -289,7 +284,7 @@ char *loadIntegerObject(int enctype) {
return buf; return buf;
} }
char* loadLzfStringObject() { static char* loadLzfStringObject() {
unsigned int slen, clen; unsigned int slen, clen;
char *c, *s; char *c, *s;
@ -313,7 +308,7 @@ char* loadLzfStringObject() {
} }
/* returns NULL when not processable, char* when valid */ /* returns NULL when not processable, char* when valid */
char* loadStringObject() { static char* loadStringObject() {
uint32_t offset = CURR_OFFSET; uint32_t offset = CURR_OFFSET;
int isencoded; int isencoded;
uint32_t len; uint32_t len;
@ -336,7 +331,7 @@ char* loadStringObject() {
if (len == REDIS_RDB_LENERR) return NULL; if (len == REDIS_RDB_LENERR) return NULL;
char *buf = malloc(sizeof(char) * (len+1)); char *buf = zmalloc(sizeof(char) * (len+1));
if (buf == NULL) return NULL; if (buf == NULL) return NULL;
buf[len] = '\0'; buf[len] = '\0';
if (!readBytes(buf, len)) { if (!readBytes(buf, len)) {
@ -346,7 +341,7 @@ char* loadStringObject() {
return buf; return buf;
} }
int processStringObject(char** store) { static int processStringObject(char** store) {
unsigned long offset = CURR_OFFSET; unsigned long offset = CURR_OFFSET;
char *key = loadStringObject(); char *key = loadStringObject();
if (key == NULL) { if (key == NULL) {
@ -363,7 +358,7 @@ int processStringObject(char** store) {
return 1; return 1;
} }
double* loadDoubleValue() { static double* loadDoubleValue() {
char buf[256]; char buf[256];
unsigned char len; unsigned char len;
double* val; double* val;
@ -386,7 +381,7 @@ double* loadDoubleValue() {
} }
} }
int processDoubleValue(double** store) { static int processDoubleValue(double** store) {
unsigned long offset = CURR_OFFSET; unsigned long offset = CURR_OFFSET;
double *val = loadDoubleValue(); double *val = loadDoubleValue();
if (val == NULL) { if (val == NULL) {
@ -403,7 +398,7 @@ int processDoubleValue(double** store) {
return 1; return 1;
} }
int loadPair(entry *e) { static int loadPair(entry *e) {
uint32_t offset = CURR_OFFSET; uint32_t offset = CURR_OFFSET;
uint32_t i; uint32_t i;
@ -486,7 +481,7 @@ int loadPair(entry *e) {
return 1; return 1;
} }
entry loadEntry() { static entry loadEntry() {
entry e = { NULL, -1, 0 }; entry e = { NULL, -1, 0 };
uint32_t length, offset[4]; uint32_t length, offset[4];
@ -544,7 +539,7 @@ entry loadEntry() {
return e; return e;
} }
void printCentered(int indent, int width, char* body) { static void printCentered(int indent, int width, char* body) {
char head[256], tail[256]; char head[256], tail[256];
memset(head, '\0', 256); memset(head, '\0', 256);
memset(tail, '\0', 256); memset(tail, '\0', 256);
@ -554,21 +549,21 @@ void printCentered(int indent, int width, char* body) {
printf("%s %s %s\n", head, body, tail); printf("%s %s %s\n", head, body, tail);
} }
void printValid(uint64_t ops, uint64_t bytes) { static void printValid(uint64_t ops, uint64_t bytes) {
char body[80]; char body[80];
sprintf(body, "Processed %llu valid opcodes (in %llu bytes)", sprintf(body, "Processed %llu valid opcodes (in %llu bytes)",
(unsigned long long) ops, (unsigned long long) bytes); (unsigned long long) ops, (unsigned long long) bytes);
printCentered(4, 80, body); printCentered(4, 80, body);
} }
void printSkipped(uint64_t bytes, uint64_t offset) { static void printSkipped(uint64_t bytes, uint64_t offset) {
char body[80]; char body[80];
sprintf(body, "Skipped %llu bytes (resuming at 0x%08llx)", sprintf(body, "Skipped %llu bytes (resuming at 0x%08llx)",
(unsigned long long) bytes, (unsigned long long) offset); (unsigned long long) bytes, (unsigned long long) offset);
printCentered(4, 80, body); printCentered(4, 80, body);
} }
void printErrorStack(entry *e) { static void printErrorStack(entry *e) {
unsigned int i; unsigned int i;
char body[64]; char body[64];
@ -708,24 +703,18 @@ void process(void) {
} }
} }
int main(int argc, char **argv) { int redis_check_rdb(char *rdbfilename) {
/* expect the first argument to be the dump file */
if (argc <= 1) {
printf("Usage: %s <dump.rdb>\n", argv[0]);
exit(0);
}
int fd; int fd;
off_t size; off_t size;
struct stat stat; struct stat stat;
void *data; void *data;
fd = open(argv[1], O_RDONLY); fd = open(rdbfilename, O_RDONLY);
if (fd < 1) { if (fd < 1) {
ERROR("Cannot open file: %s\n", argv[1]); ERROR("Cannot open file: %s\n", rdbfilename);
} }
if (fstat(fd, &stat) == -1) { if (fstat(fd, &stat) == -1) {
ERROR("Cannot stat: %s\n", argv[1]); ERROR("Cannot stat: %s\n", rdbfilename);
} else { } else {
size = stat.st_size; size = stat.st_size;
} }
@ -736,7 +725,7 @@ int main(int argc, char **argv) {
data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) { if (data == MAP_FAILED) {
ERROR("Cannot mmap: %s\n", argv[1]); ERROR("Cannot mmap: %s\n", rdbfilename);
} }
/* Initialize static vars */ /* Initialize static vars */

View File

@ -3550,6 +3550,17 @@ int checkForSentinelMode(int argc, char **argv) {
return 0; return 0;
} }
/* Returns 1 if there is --check-rdb among the arguments or if
* argv[0] is exactly "redis-check-rdb". */
int checkForCheckRDBMode(int argc, char **argv) {
int j;
if (strstr(argv[0],"redis-check-rdb") != NULL) return 1;
for (j = 1; j < argc; j++)
if (!strcmp(argv[j],"--check-rdb")) return 1;
return 0;
}
/* Function called at startup to load RDB or AOF file in memory. */ /* Function called at startup to load RDB or AOF file in memory. */
void loadDataFromDisk(void) { void loadDataFromDisk(void) {
long long start = ustime(); long long start = ustime();
@ -3766,6 +3777,11 @@ int main(int argc, char **argv) {
while(j != argc) { while(j != argc) {
if (argv[j][0] == '-' && argv[j][1] == '-') { if (argv[j][0] == '-' && argv[j][1] == '-') {
/* Option name */ /* Option name */
if (!strcmp(argv[j], "--check-rdb")) {
/* Argument has no options, need to skip for parsing. */
j++;
continue;
}
if (sdslen(options)) options = sdscat(options,"\n"); if (sdslen(options)) options = sdscat(options,"\n");
options = sdscat(options,argv[j]+2); options = sdscat(options,argv[j]+2);
options = sdscat(options," "); options = sdscat(options," ");
@ -3791,9 +3807,17 @@ int main(int argc, char **argv) {
redisLog(REDIS_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis"); redisLog(REDIS_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis");
} }
if (checkForCheckRDBMode(argc, argv)) {
redisLog(REDIS_WARNING, "Checking RDB file %s", server.rdb_filename);
redisLog(REDIS_WARNING, "To check different RDB file: "
"redis-check-rdb --dbfilename <dump.rdb>");
exit(redis_check_rdb(server.rdb_filename));
}
server.supervised = redisIsSupervised(server.supervised_mode); server.supervised = redisIsSupervised(server.supervised_mode);
int background = server.daemonize && !server.supervised; int background = server.daemonize && !server.supervised;
if (background) daemonize(); if (background) daemonize();
initServer(); initServer();
if (background || server.pidfile) createPidFile(); if (background || server.pidfile) createPidFile();
redisSetProcTitle(argv[0]); redisSetProcTitle(argv[0]);

View File

@ -1380,6 +1380,9 @@ void sentinelTimer(void);
char *sentinelHandleConfiguration(char **argv, int argc); char *sentinelHandleConfiguration(char **argv, int argc);
void sentinelIsRunning(void); void sentinelIsRunning(void);
/* redis-check-rdb */
int redis_check_rdb(char *rdbfilename);
/* Scripting */ /* Scripting */
void scriptingInit(void); void scriptingInit(void);