Skip to content

Commit

Permalink
Add nopersist and noreplication options
Browse files Browse the repository at this point in the history
Config option "disable-all-persistence" stops Redis from writing
or reading an AOF or RDB.  If you are on Linux and are running
with the proper permissions (a "ulimit -l" of 'unlimited', typically
only settable by root, or the CAP_IPC_LOCK capability), this option
also disables the paging of any Redis memory to disk.

Config option "disable-replication" stops Redis from becoming a master
or replica or migrating keys to other nodes or participating
in a cluster.

disable-all-persistence automatically enables disable-replication.

disable-all-persisitence also stops Redis from creating any
background-I/O threads since they would have zero work to do anyway.

You *can* use disable-replication independently if you want to store
data to disk but turn off all network replication/clustering/migration.

These options can only be set at launch.  You cannot alter these
settings after Redis starts.  Once your server is running with
any of these isolation guarantees, it will remain isolated.

These options are designed to create isolated and "pure" Redis instances
for data that must never leave Redis except by direct client access.

Of course, someone could always just scan your entire database from
a client and copy it that way, but it's still not a live replication channel.

Without these options, it can be tricky to track down every "disable
storage" option.

Without the "disable-replication" option, there isn't any way to stop
Redis from replicating over the network except using firewalls/ACLs.
  • Loading branch information
mattsta committed Dec 11, 2014
1 parent 3cd36a4 commit 066ffab
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 2 deletions.
24 changes: 23 additions & 1 deletion src/aof.c
Expand Up @@ -233,6 +233,10 @@ void stopAppendOnly(void) {
/* Called when the user switches from "appendonly no" to "appendonly yes"
* at runtime using the CONFIG command. */
int startAppendOnly(void) {
if (server.nopersist) {
redisLog(REDIS_WARNING, "Persistence disabled. Not writing AOF.");
return REDIS_ERR;
}
server.aof_last_fsync = server.unixtime;
server.aof_fd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);
redisAssert(server.aof_state == REDIS_AOF_OFF);
Expand Down Expand Up @@ -275,6 +279,9 @@ void flushAppendOnlyFile(int force) {
int sync_in_progress = 0;
mstime_t latency;

if (server.nopersist)
return;

if (sdslen(server.aof_buf) == 0) return;

if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
Expand Down Expand Up @@ -598,12 +605,18 @@ void freeFakeClient(struct redisClient *c) {
* fatal error an error message is logged and the program exists. */
int loadAppendOnlyFile(char *filename) {
struct redisClient *fakeClient;
FILE *fp = fopen(filename,"r");
FILE *fp = NULL;
struct redis_stat sb;
int old_aof_state = server.aof_state;
long loops = 0;
off_t valid_up_to = 0; /* Offset of the latest well-formed command loaded. */

if (server.nopersist) {
redisLog(REDIS_WARNING, "Persistence disabled. Not opening AOF.");
return REDIS_ERR;
}

fp = fopen(filename, "r");
if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
server.aof_current_size = 0;
fclose(fp);
Expand Down Expand Up @@ -1029,6 +1042,11 @@ int rewriteAppendOnlyFile(char *filename) {
char byte;
size_t processed = 0;

if (server.nopersist) {
redisLog(REDIS_WARNING, "Persistence disabled. Not rewriting AOF.");
return REDIS_ERR;
}

/* Note that we have to use a different temp name here compared to the
* one used by rewriteAppendOnlyFileBackground() function. */
snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid());
Expand Down Expand Up @@ -1320,6 +1338,10 @@ int rewriteAppendOnlyFileBackground(void) {
}

void bgrewriteaofCommand(redisClient *c) {
if (server.nopersist) {
addReplyError(c,"Persistence disabled. No AOF to rewrite.");
return;
}
if (server.aof_child_pid != -1) {
addReplyError(c,"Background append only file rewriting already in progress");
} else if (server.rdb_child_pid != -1) {
Expand Down
5 changes: 5 additions & 0 deletions src/cluster.c
Expand Up @@ -4381,6 +4381,11 @@ void migrateCommand(redisClient *c) {
rio cmd, payload;
int retry_num = 0;

if (server.noreplication) {
addReplyError(c,"MIGRATE denied because disable-replication is set.");
return;
}

try_again:
/* Initialization */
copy = 0;
Expand Down
70 changes: 70 additions & 0 deletions src/config.c
Expand Up @@ -521,6 +521,14 @@ void loadServerConfigFromString(char *config) {
if (server.repl_min_slaves_max_lag < 0) {
err = "Invalid value for min-slaves-max-lag."; goto loaderr;
}
} else if (!strcasecmp(argv[0],"disable-all-persistence") && argc == 2) {
if ((server.nopersist = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"disable-replication") && argc == 2) {
if ((server.noreplication = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) {
int flags = keyspaceEventsStringToFlags(argv[1]);

Expand All @@ -546,6 +554,64 @@ void loadServerConfigFromString(char *config) {
sdsfreesplitres(argv,argc);
}

if (server.nopersist) {
/* No data persistence means no replication either */
server.noreplication = 1;

/* The settings below are just for convenience to stop Redis from
* attempting save operations by default.
* If someone enables RDB or AOF at runtime using CONFIG SET, the
* persistence disable guards inside each AOF/RDB/replication/cluster
* write functions will continue to deny any persistence. */
resetServerSaveParams(); /* Kill any "save" options previously set */
server.aof_fsync = AOF_FSYNC_NO; /* Don't try to sync because no AOF */
server.aof_state = REDIS_AOF_OFF; /* Don't try to AOF */
server.stop_writes_on_bgsave_err = 0; /* Don't stop on save errors */

/* Disable commands related to persistence */
char *cmds[] = {"save", "bgsave", "bgrewriteaof"};
for (size_t i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) {
sds cmd = sdsnew(cmds[i]);

if ((dictDelete(server.commands, cmd) != DICT_OK)) {
redisLog(REDIS_WARNING, "Could not disable command %s", cmd);
}

sdsfree(cmd);
}

#if _POSIX_MEMLOCK == 1
/* No persist = don't leak data to swap space either.
* Forces all memory to remain in memory. */
/* NOTE: This only works if you either:
* - run as root
* - have CAP_IPC_LOCK capability
*/
if ((mlockall(MCL_CURRENT|MCL_FUTURE) == -1)) {
redisLog(REDIS_WARNING, "Unable to force memory locking. "
"Some of your data may leak into your OS swap space.");
}
#endif
}

if (server.noreplication) {
/* No replication implies no cluster. */
server.cluster_enabled = 0;

/* Disable commands related to replication/clustering */
char *cmds[] = {"migrate", "replconf", "sync",
"psync", "cluster", "slaveof"};
for (size_t i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) {
sds cmd = sdsnew(cmds[i]);

if ((dictDelete(server.commands, cmd) != DICT_OK)) {
redisLog(REDIS_WARNING, "Could not disable command %s", cmd);
}

sdsfree(cmd);
}
}

/* Sanity checks. */
if (server.cluster_enabled && server.masterhost) {
linenum = slaveof_linenum;
Expand Down Expand Up @@ -1904,6 +1970,10 @@ void configCommand(redisClient *c) {
addReplyError(c,"The server is running without a config file");
return;
}
if (server.nopersist) {
addReplyError(c,"Persistence disabled. No config writing allowed.");
return;
}
if (rewriteConfig(server.configfile) == -1) {
redisLog(REDIS_WARNING,"CONFIG REWRITE failed: %s", strerror(errno));
addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno));
Expand Down
18 changes: 18 additions & 0 deletions src/rdb.c
Expand Up @@ -643,6 +643,11 @@ int rdbSaveRio(rio *rdb, int *error) {
long long now = mstime();
uint64_t cksum;

if (server.nopersist) {
redisLog(REDIS_WARNING, "Persistence disabled. Not saving RDB.");
return REDIS_ERR;
}

if (server.rdb_checksum)
rdb->update_cksum = rioGenericUpdateChecksum;
snprintf(magic,sizeof(magic),"REDIS%04d",REDIS_RDB_VERSION);
Expand Down Expand Up @@ -1134,6 +1139,11 @@ int rdbLoad(char *filename) {
FILE *fp;
rio rdb;

if (server.nopersist) {
redisLog(REDIS_WARNING, "Persistence disabled. Not loading RDB.");
return REDIS_ERR;
}

if ((fp = fopen(filename,"r")) == NULL) return REDIS_ERR;

rioInitWithFile(&rdb,fp);
Expand Down Expand Up @@ -1519,6 +1529,10 @@ int rdbSaveToSlavesSockets(void) {
}

void saveCommand(redisClient *c) {
if (server.nopersist) {
addReplyError(c,"Persistence disabled. Not saving RDB.");
return;
}
if (server.rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
return;
Expand All @@ -1531,6 +1545,10 @@ void saveCommand(redisClient *c) {
}

void bgsaveCommand(redisClient *c) {
if (server.nopersist) {
addReplyError(c,"Persistence disabled. Not saving RDB.");
return;
}
if (server.rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
} else if (server.aof_child_pid != -1) {
Expand Down
18 changes: 17 additions & 1 deletion src/redis.c
Expand Up @@ -1401,6 +1401,8 @@ void initServerConfig(void) {
server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM;
server.ipfd_count = 0;
server.sofd = -1;
server.nopersist = 0;
server.noreplication = 0;
server.dbnum = REDIS_DEFAULT_DBNUM;
server.verbosity = REDIS_DEFAULT_VERBOSITY;
server.maxidletime = REDIS_MAXIDLETIME;
Expand Down Expand Up @@ -1871,7 +1873,7 @@ void initServer(void) {
scriptingInit();
slowlogInit();
latencyMonitorInit();
bioInit();
if (!server.nopersist) bioInit();
}

/* Populates the Redis Command Table starting from the hard coded list
Expand Down Expand Up @@ -2360,6 +2362,12 @@ int prepareForShutdown(int flags) {
aof_fsync(server.aof_fd);
}
if ((server.saveparamslen > 0 && !nosave) || save) {

if (server.nopersist) {
redisLog(REDIS_WARNING,"Persistence disabled. Not writing RDB.");
return REDIS_ERR;
}

redisLog(REDIS_NOTICE,"Saving the final RDB snapshot before exiting.");
/* Snapshotting. Perform a SYNC SAVE and exit */
if (rdbSave(server.rdb_filename) != REDIS_OK) {
Expand Down Expand Up @@ -3540,6 +3548,14 @@ int checkForSentinelMode(int argc, char **argv) {
/* Function called at startup to load RDB or AOF file in memory. */
void loadDataFromDisk(void) {
long long start = ustime();

if (server.nopersist) {
redisLog(REDIS_WARNING, "Persistence disabled. "
"No RDB, AOF, replication, or cluster allowed.");
return;
}

start = ustime();
if (server.aof_state == REDIS_AOF_ON) {
if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
Expand Down
6 changes: 6 additions & 0 deletions src/redis.h
Expand Up @@ -66,6 +66,10 @@ typedef long long mstime_t; /* millisecond time type. */
#include "latency.h" /* Latency monitor API */
#include "sparkline.h" /* ASII graphs API */

#ifdef __linux__
#include <sys/mman.h> /* For mlockall() used by disable-persistence */
#endif

/* Error codes */
#define REDIS_OK 0
#define REDIS_ERR -1
Expand Down Expand Up @@ -666,6 +670,8 @@ struct redisServer {
int cronloops; /* Number of times the cron function run */
char runid[REDIS_RUN_ID_SIZE+1]; /* ID always different at every exec. */
int sentinel_mode; /* True if this instance is a Sentinel. */
int nopersist; /* Disable all persistence (save and replication) */
int noreplication; /* Disable all replication and clustering */
/* Networking */
int port; /* TCP listening port */
int tcp_backlog; /* TCP listen() backlog */
Expand Down
20 changes: 20 additions & 0 deletions src/replication.c
Expand Up @@ -464,6 +464,11 @@ void syncCommand(redisClient *c) {
/* ignore SYNC if already slave or in monitor mode */
if (c->flags & REDIS_SLAVE) return;

if (server.nopersist) {
addReplyError(c,"Persistence disabled. No SYNC allowed.");
return;
}

/* Refuse SYNC requests if we are a slave but the link with our master
* is not ok... */
if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED) {
Expand Down Expand Up @@ -604,6 +609,11 @@ void replconfCommand(redisClient *c) {
return;
}

if (server.nopersist) {
addReplyError(c,"Persistence disabled. No replication allowed.");
return;
}

/* Process every option-value pair. */
for (j = 1; j < c->argc; j+=2) {
if (!strcasecmp(c->argv[j]->ptr,"listening-port")) {
Expand Down Expand Up @@ -1215,6 +1225,11 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
REDIS_NOTUSED(privdata);
REDIS_NOTUSED(mask);

if (server.noreplication) {
redisLog(REDIS_WARNING, "Replication disabled. Not syncing master.");
return;
}

/* If this event fired after the user turned the instance into a master
* with SLAVEOF NO ONE we must just return ASAP. */
if (server.repl_state == REDIS_REPL_NONE) {
Expand Down Expand Up @@ -1475,6 +1490,11 @@ void slaveofCommand(redisClient *c) {
return;
}

if (server.noreplication) {
addReplyError(c,"SLAVEOF denied because disable-replication is set.");
return;
}

/* The special host/port combination "NO" "ONE" turns the instance
* into a master. Otherwise the new master address is set. */
if (!strcasecmp(c->argv[1]->ptr,"no") &&
Expand Down

0 comments on commit 066ffab

Please sign in to comment.