Merge pull request #586 from saj/aof_last_bgrewrite_status
authorSalvatore Sanfilippo <antirez@gmail.com>
Fri, 27 Jul 2012 10:55:20 +0000 (03:55 -0700)
committerSalvatore Sanfilippo <antirez@gmail.com>
Fri, 27 Jul 2012 10:55:20 +0000 (03:55 -0700)
New in INFO: aof_last_bgrewrite_status

1  2 
src/redis.c
src/redis.h

diff --combined src/redis.c
index e636aa9b550504c6fbccba0dac2f0d3fb5dd347b,c8fea86f74a5d599e60964d464f9050846f38560..f2c5e2c7004d34ae30946b6fe0bad29096df08bc
@@@ -106,10 -106,6 +106,10 @@@ struct redisCommand *commandTable
   *    results. For instance SPOP and RANDOMKEY are two random commands.
   * S: Sort command output array if called from script, so that the output
   *    is deterministic.
 + * l: Allow command while loading the database.
 + * t: Allow command while a slave has stale data but is not allowed to
 + *    server this data. Normally no command is accepted in this condition
 + *    but just a few.
   */
  struct redisCommand redisCommandTable[] = {
      {"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
      {"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},
      {"flushall",flushallCommand,1,"w",0,NULL,0,0,0,0,0},
      {"sort",sortCommand,-2,"wmS",0,NULL,1,1,1,0,0},
 -    {"info",infoCommand,-1,"r",0,NULL,0,0,0,0,0},
 +    {"info",infoCommand,-1,"rlt",0,NULL,0,0,0,0,0},
      {"monitor",monitorCommand,1,"ars",0,NULL,0,0,0,0,0},
      {"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
      {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
      {"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
 -    {"slaveof",slaveofCommand,3,"as",0,NULL,0,0,0,0,0},
 +    {"slaveof",slaveofCommand,3,"ast",0,NULL,0,0,0,0,0},
      {"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
      {"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
 -    {"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
 -    {"unsubscribe",unsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
 -    {"psubscribe",psubscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
 -    {"punsubscribe",punsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
 -    {"publish",publishCommand,3,"pf",0,NULL,0,0,0,0,0},
 +    {"subscribe",subscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
 +    {"unsubscribe",unsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
 +    {"psubscribe",psubscribeCommand,-2,"rpslt",0,NULL,0,0,0,0,0},
 +    {"punsubscribe",punsubscribeCommand,-1,"rpslt",0,NULL,0,0,0,0,0},
 +    {"publish",publishCommand,3,"pflt",0,NULL,0,0,0,0,0},
      {"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0},
      {"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0},
      {"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0},
@@@ -821,8 -817,13 +821,8 @@@ void clientsCron(void) 
   * a macro is used: run_with_period(milliseconds) { .... }
   */
  
 -/* Using the following macro you can run code inside serverCron() with the
 - * specified period, specified in milliseconds.
 - * The actual resolution depends on REDIS_HZ. */
 -#define run_with_period(_ms_) if (!(loops % ((_ms_)/(1000/REDIS_HZ))))
 -
  int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
 -    int j, loops = server.cronloops;
 +    int j;
      REDIS_NOTUSED(eventLoop);
      REDIS_NOTUSED(id);
      REDIS_NOTUSED(clientData);
      }
  
      /* Show information about connected clients */
 -    run_with_period(5000) {
 -        redisLog(REDIS_VERBOSE,"%d clients connected (%d slaves), %zu bytes in use",
 -            listLength(server.clients)-listLength(server.slaves),
 -            listLength(server.slaves),
 -            zmalloc_used_memory());
 +    if (!server.sentinel_mode) {
 +        run_with_period(5000) {
 +            redisLog(REDIS_VERBOSE,
 +                "%d clients connected (%d slaves), %zu bytes in use",
 +                listLength(server.clients)-listLength(server.slaves),
 +                listLength(server.slaves),
 +                zmalloc_used_memory());
 +        }
      }
  
      /* We need to do a few operations on clients asynchronously. */
          if (server.cluster_enabled) clusterCron();
      }
  
 +    /* Run the sentinel timer if we are in sentinel mode. */
 +    run_with_period(100) {
 +        if (server.sentinel_mode) sentinelTimer();
 +    }
 +
      server.cronloops++;
      return 1000/REDIS_HZ;
  }
@@@ -1121,6 -1114,7 +1121,7 @@@ void initServerConfig() 
      server.aof_last_fsync = time(NULL);
      server.aof_rewrite_time_last = -1;
      server.aof_rewrite_time_start = -1;
+     server.aof_lastbgrewrite_status = REDIS_OK;
      server.aof_delayed_fsync = 0;
      server.aof_fd = -1;
      server.aof_selected_db = -1; /* Make sure the first time will not match */
@@@ -1388,8 -1382,6 +1389,8 @@@ void populateCommandTable(void) 
              case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
              case 'R': c->flags |= REDIS_CMD_RANDOM; break;
              case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;
 +            case 'l': c->flags |= REDIS_CMD_LOADING; break;
 +            case 't': c->flags |= REDIS_CMD_STALE; break;
              default: redisPanic("Unsupported command flag"); break;
              }
              f++;
@@@ -1631,7 -1623,7 +1632,7 @@@ int processCommand(redisClient *c) 
          return REDIS_OK;
      }
  
 -    /* Don't accept wirte commands if this is a read only slave. But
 +    /* Don't accept write commands if this is a read only slave. But
       * accept write commands if this is our master. */
      if (server.masterhost && server.repl_slave_ro &&
          !(c->flags & REDIS_MASTER) &&
       * we are a slave with a broken link with master. */
      if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED &&
          server.repl_serve_stale_data == 0 &&
 -        c->cmd->proc != infoCommand && c->cmd->proc != slaveofCommand)
 +        !(c->cmd->flags & REDIS_CMD_STALE))
      {
          addReply(c, shared.masterdownerr);
          return REDIS_OK;
      }
  
 -    /* Loading DB? Return an error if the command is not INFO */
 -    if (server.loading && c->cmd->proc != infoCommand) {
 +    /* Loading DB? Return an error if the command has not the
 +     * REDIS_CMD_LOADING flag. */
 +    if (server.loading && !(c->cmd->flags & REDIS_CMD_LOADING)) {
          addReply(c, shared.loadingerr);
          return REDIS_OK;
      }
  
 -    /* Lua script too slow? Only allow SHUTDOWN NOSAVE and SCRIPT KILL. */
 +    /* Lua script too slow? Only allow commands with REDIS_CMD_STALE flag. */
      if (server.lua_timedout &&
          !(c->cmd->proc == shutdownCommand &&
            c->argc == 2 &&
@@@ -1967,12 -1958,13 +1968,13 @@@ sds genRedisInfoString(char *section) 
              "aof_rewrite_in_progress:%d\r\n"
              "aof_rewrite_scheduled:%d\r\n"
              "aof_last_rewrite_time_sec:%ld\r\n"
-             "aof_current_rewrite_time_sec:%ld\r\n",
+             "aof_current_rewrite_time_sec:%ld\r\n"
+             "aof_last_bgrewrite_status:%s\r\n",
              server.loading,
              server.dirty,
              server.rdb_child_pid != -1,
              server.lastsave,
-             server.lastbgsave_status == REDIS_OK ? "ok" : "err",
+             (server.lastbgsave_status == REDIS_OK) ? "ok" : "err",
              server.rdb_save_time_last,
              (server.rdb_child_pid == -1) ?
                  -1 : time(NULL)-server.rdb_save_time_start,
              server.aof_rewrite_scheduled,
              server.aof_rewrite_time_last,
              (server.aof_child_pid == -1) ?
-                 -1 : time(NULL)-server.aof_rewrite_time_start);
+                 -1 : time(NULL)-server.aof_rewrite_time_start,
+             (server.aof_lastbgrewrite_status == REDIS_OK) ? "ok" : "err");
  
          if (server.aof_state != REDIS_AOF_OFF) {
              info = sdscatprintf(info,
@@@ -2447,26 -2440,21 +2450,26 @@@ void usage() 
      fprintf(stderr,"       ./redis-server /etc/redis/6379.conf\n");
      fprintf(stderr,"       ./redis-server --port 7777\n");
      fprintf(stderr,"       ./redis-server --port 7777 --slaveof 127.0.0.1 8888\n");
 -    fprintf(stderr,"       ./redis-server /etc/myredis.conf --loglevel verbose\n");
 +    fprintf(stderr,"       ./redis-server /etc/myredis.conf --loglevel verbose\n\n");
 +    fprintf(stderr,"Sentinel mode:\n");
 +    fprintf(stderr,"       ./redis-server /etc/sentinel.conf --sentinel\n");
      exit(1);
  }
  
  void redisAsciiArt(void) {
  #include "asciilogo.h"
      char *buf = zmalloc(1024*16);
 +    char *mode = "stand alone";
 +
 +    if (server.cluster_enabled) mode = "cluster";
 +    else if (server.sentinel_mode) mode = "sentinel";
  
      snprintf(buf,1024*16,ascii_logo,
          REDIS_VERSION,
          redisGitSHA1(),
          strtol(redisGitDirty(),NULL,10) > 0,
          (sizeof(long) == 8) ? "64" : "32",
 -        server.cluster_enabled ? "cluster" : "stand alone",
 -        server.port,
 +        mode, server.port,
          (long) getpid()
      );
      redisLogRaw(REDIS_NOTICE|REDIS_LOG_RAW,buf);
@@@ -2504,35 -2492,8 +2507,35 @@@ void setupSignalHandlers(void) 
  
  void memtest(size_t megabytes, int passes);
  
 +/* Returns 1 if there is --sentinel among the arguments or if
 + * argv[0] is exactly "redis-sentinel". */
 +int checkForSentinelMode(int argc, char **argv) {
 +    int j;
 +
 +    if (strstr(argv[0],"redis-sentinel") != NULL) return 1;
 +    for (j = 1; j < argc; j++)
 +        if (!strcmp(argv[j],"--sentinel")) return 1;
 +    return 0;
 +}
 +
 +/* Function called at startup to load RDB or AOF file in memory. */
 +void loadDataFromDisk(void) {
 +    long long 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);
 +    } else {
 +        if (rdbLoad(server.rdb_filename) == REDIS_OK) {
 +            redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
 +                (float)(ustime()-start)/1000000);
 +        } else if (errno != ENOENT) {
 +            redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
 +            exit(1);
 +        }
 +    }
 +}
 +
  int main(int argc, char **argv) {
 -    long long start;
      struct timeval tv;
  
      /* We need to initialize our libraries, and the server configuration. */
      srand(time(NULL)^getpid());
      gettimeofday(&tv,NULL);
      dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
 +    server.sentinel_mode = checkForSentinelMode(argc,argv);
      initServerConfig();
  
 +    /* We need to init sentinel right now as parsing the configuration file
 +     * in sentinel mode will have the effect of populating the sentinel
 +     * data structures with master nodes to monitor. */
 +    if (server.sentinel_mode) {
 +        initSentinelConfig();
 +        initSentinel();
 +    }
 +
      if (argc >= 2) {
          int j = 1; /* First option to parse in argv[] */
          sds options = sdsempty();
          loadServerConfig(configfile,options);
          sdsfree(options);
      } else {
 -        redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");
 +        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 (server.daemonize) daemonize();
      initServer();
      if (server.daemonize) createPidFile();
      redisAsciiArt();
 -    redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
 -#ifdef __linux__
 -    linuxOvercommitMemoryWarning();
 -#endif
 -    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);
 -    } else {
 -        if (rdbLoad(server.rdb_filename) == REDIS_OK) {
 -            redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
 -                (float)(ustime()-start)/1000000);
 -        } else if (errno != ENOENT) {
 -            redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
 -            exit(1);
 -        }
 +
 +    if (!server.sentinel_mode) {
 +        /* Things only needed when not runnign in Sentinel mode. */
 +        redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
 +    #ifdef __linux__
 +        linuxOvercommitMemoryWarning();
 +    #endif
 +        loadDataFromDisk();
 +        if (server.ipfd > 0)
 +            redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
 +        if (server.sofd > 0)
 +            redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
      }
 -    if (server.ipfd > 0)
 -        redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
 -    if (server.sofd > 0)
 -        redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
 +
      aeSetBeforeSleepProc(server.el,beforeSleep);
      aeMain(server.el);
      aeDeleteEventLoop(server.el);
diff --combined src/redis.h
index bee8cf4f15098335a9ad42911d215afc369f5a71,2fe2fb9e166ce9ac17a7f519f13db6039285ab4e..d12e944586d4f710c09f19e07b83b603f444ebb0
@@@ -85,8 -85,6 +85,8 @@@
  #define REDIS_CMD_NOSCRIPT  64              /* "s" flag */
  #define REDIS_CMD_RANDOM 128                /* "R" flag */
  #define REDIS_CMD_SORT_FOR_SCRIPT 256       /* "S" flag */
 +#define REDIS_CMD_LOADING 512               /* "l" flag */
 +#define REDIS_CMD_STALE 1024                /* "t" flag */
  
  /* Object types */
  #define REDIS_STRING 0
  #define REDIS_PROPAGATE_AOF 1
  #define REDIS_PROPAGATE_REPL 2
  
 +/* Using the following macro you can run code inside serverCron() with the
 + * specified period, specified in milliseconds.
 + * The actual resolution depends on REDIS_HZ. */
 +#define run_with_period(_ms_) if (!(server.cronloops%((_ms_)/(1000/REDIS_HZ))))
 +
  /* We can print the stacktrace, so our assert is defined this way: */
  #define redisAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_redisAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))
  #define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
@@@ -584,7 -577,6 +584,7 @@@ struct redisServer 
      int arch_bits;              /* 32 or 64 depending on sizeof(long) */
      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. */
      /* Networking */
      int port;                   /* TCP listening port */
      char *bindaddr;             /* Bind address or NULL */
      time_t aof_last_fsync;            /* UNIX time of last fsync() */
      time_t aof_rewrite_time_last;   /* Time used by last AOF rewrite run. */
      time_t aof_rewrite_time_start;  /* Current AOF rewrite start time. */
+     int aof_lastbgrewrite_status;   /* REDIS_OK or REDIS_ERR */
      unsigned long aof_delayed_fsync;  /* delayed AOF fsync() counter */
      /* RDB persistence */
      long long dirty;                /* Changes to DB from the last save */
@@@ -1121,12 -1114,6 +1122,12 @@@ void clusterCron(void)
  clusterNode *getNodeByQuery(redisClient *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask);
  void clusterPropagatePublish(robj *channel, robj *message);
  
 +/* Sentinel */
 +void initSentinelConfig(void);
 +void initSentinel(void);
 +void sentinelTimer(void);
 +char *sentinelHandleConfiguration(char **argv, int argc);
 +
  /* Scripting */
  void scriptingInit(void);
  
@@@ -1292,10 -1279,4 +1293,10 @@@ void enableWatchdog(int period)
  void disableWatchdog(void);
  void watchdogScheduleSignal(int period);
  void redisLogHexDump(int level, char *descr, void *value, size_t len);
 +
 +#define redisDebug(fmt, ...) \
 +    printf("DEBUG %s:%d > " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
 +#define redisDebugMark() \
 +    printf("-- MARK %s:%d --\n", __FILE__, __LINE__)
 +
  #endif