X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/1fce3201145f256401bef7fde96a8c1509bcb4a7..cb9b35c8cafc703ea5f2754b2961c9a3268c35d5:/src/diskstore.c diff --git a/src/diskstore.c b/src/diskstore.c index 35c591d7..26f3af60 100644 --- a/src/diskstore.c +++ b/src/diskstore.c @@ -74,6 +74,7 @@ #include #include +#include int create256dir(char *prefix) { char buf[1024]; @@ -140,12 +141,13 @@ int dsClose(void) { } /* Convert key into full path for this object. Dirty but hopefully - * is fast enough. */ -void dsKeyToPath(redisDb *db, char *buf, robj *key) { + * is fast enough. Returns the length of the returned path. */ +int dsKeyToPath(redisDb *db, char *buf, robj *key) { SHA1_CTX ctx; unsigned char hash[20]; char hex[40], digits[] = "0123456789abcdef"; int j, l; + char *origbuf = buf; SHA1Init(&ctx); SHA1Update(&ctx,key->ptr,sdslen(key->ptr)); @@ -178,19 +180,42 @@ void dsKeyToPath(redisDb *db, char *buf, robj *key) { buf[0] = '_'; memcpy(buf+1,hex,40); buf[41] = '\0'; + return (buf-origbuf)+41; } int dsSet(redisDb *db, robj *key, robj *val) { - char buf[1024]; + char buf[1024], buf2[1024]; FILE *fp; - int retval; + int retval, len; - dsKeyToPath(db,buf,key); - fp = fopen(buf,"w"); + len = dsKeyToPath(db,buf,key); + memcpy(buf2,buf,len); + snprintf(buf2+len,sizeof(buf2)-len,"_%ld_%ld",(long)time(NULL),(long)val); + while ((fp = fopen(buf2,"w")) == NULL) { + if (errno == ENOSPC) { + redisLog(REDIS_WARNING,"Diskstore: No space left on device. Please make room and wait 30 seconds for Redis to continue."); + sleep(30); + } else { + redisLog(REDIS_WARNING,"diskstore error opening %s: %s", + buf2, strerror(errno)); + redisPanic("Unrecoverable diskstore error. Exiting."); + } + } if ((retval = rdbSaveKeyValuePair(fp,db,key,val,time(NULL))) == -1) return REDIS_ERR; fclose(fp); - if (retval == 0) unlink(buf); /* Expired key. Unlink failing not critical */ + if (retval == 0) { + /* Expired key. Unlink failing not critical */ + unlink(buf); + unlink(buf2); + } else { + /* Use rename for atomic updadte of value */ + if (rename(buf2,buf) == -1) { + redisLog(REDIS_WARNING,"rename(2) returned an error: %s", + strerror(errno)); + redisPanic("Unrecoverable diskstore error. Exiting."); + } + } return REDIS_OK; } @@ -239,6 +264,8 @@ robj *dsGet(redisDb *db, robj *key, time_t *expire) { return val; readerr: + redisLog(REDIS_WARNING,"Read error reading reading %s. Corrupted key?", + buf); redisPanic("Unrecoverable error reading from disk store"); return NULL; /* unreached */ } @@ -262,7 +289,109 @@ int dsDel(redisDb *db, robj *key) { } int dsExists(redisDb *db, robj *key) { + char buf[1024]; + + dsKeyToPath(db,buf,key); + return access(buf,R_OK) == 0; +} + +void dsFlushOneDir(char *path, int dbid) { + DIR *dir; + struct dirent *dp, de; + + dir = opendir(path); + if (dir == NULL) { + redisLog(REDIS_WARNING,"Disk store can't open dir %s: %s", + path, strerror(errno)); + redisPanic("Unrecoverable Disk store errore. Existing."); + } + while(1) { + char buf[1024]; + + readdir_r(dir,&de,&dp); + if (dp == NULL) break; + if (dp->d_name[0] == '.') continue; + + /* Check if we need to remove this entry accordingly to the + * DB number */ + if (dbid != -1) { + char id[64]; + char *p = strchr(dp->d_name,'_'); + int len = (p - dp->d_name); + + redisAssert(p != NULL && len < 64); + memcpy(id,dp->d_name,len); + id[len] = '\0'; + if (atoi(id) != dbid) continue; /* skip this file */ + } + + /* Finally unlink the file */ + snprintf(buf,1024,"%s/%s",path,dp->d_name); + if (unlink(buf) == -1) { + redisLog(REDIS_WARNING, + "Can't unlink %s: %s", buf, strerror(errno)); + redisPanic("Unrecoverable Disk store errore. Existing."); + } + } + closedir(dir); } -int dsFlushDb(int dbid) { +void dsFlushDb(int dbid) { + char buf[1024]; + int j, i; + + redisLog(REDIS_NOTICE,"Flushing diskstore DB (%d)",dbid); + for (j = 0; j < 256; j++) { + for (i = 0; i < 256; i++) { + snprintf(buf,1024,"%s/%02x/%02x",server.ds_path,j,i); + dsFlushOneDir(buf,dbid); + } + } +} + +int dsRdbSave(char *filename) { + char tmpfile[256]; + int j, i; + time_t now = time(NULL); + + snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid()); + fp = fopen(tmpfile,"w"); + if (!fp) { + redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno)); + return REDIS_ERR; + } + if (fwrite("REDIS0001",9,1,fp) == 0) goto werr; + + /* Scan all diskstore dirs looking for keys */ + for (j = 0; j < 256; j++) { + for (i = 0; i < 256; i++) { + snprintf(buf,1024,"%s/%02x/%02x",server.ds_path,j,i); + + /* Write the SELECT DB opcode */ + if (rdbSaveType(fp,REDIS_SELECTDB) == -1) goto werr; + if (rdbSaveLen(fp,j) == -1) goto werr; + } + } + + /* Make sure data will not remain on the OS's output buffers */ + fflush(fp); + fsync(fileno(fp)); + fclose(fp); + + /* Use RENAME to make sure the DB file is changed atomically only + * if the generate DB file is ok. */ + if (rename(tmpfile,filename) == -1) { + redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno)); + unlink(tmpfile); + return REDIS_ERR; + } + redisLog(REDIS_NOTICE,"DB saved on disk"); + server.dirty = 0; + server.lastsave = time(NULL); + return REDIS_OK; + +werr: + fclose(fp); + unlink(tmpfile); + redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno)); }