From f8c73e38b5f5a57d6d573fc6fdfed5b68d4879c8 Mon Sep 17 00:00:00 2001 From: Matt Stancliff Date: Wed, 19 Nov 2014 13:17:08 -0500 Subject: [PATCH] Add SENTINEL INFO-CACHE [masters...] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sentinel queries the INFO from every master and from every replica of every master. We can cache the INFO results in Sentinel so Sentinel can be a single place to quickly get all INFO output for an entire Sentinel monitoring group. This commit gives us SENTINEL INFO-CACHE in two forms: - SENTINEL INFO-CACHE — returns all masters and all replicas - SENTINEL INFO-CACHE master0 master1 ... masterN — vararg specify masters Results are returned as a multibulk reply with two top-level entries for each master. The first entry for each master is the name of the master. The second entry is a nested multibulk reply with the contents of INFO, first for the master, then an additional entry for each of the replicas. --- src/sentinel.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/sentinel.c b/src/sentinel.c index 8e78a226..3fa5d5e7 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -190,6 +190,7 @@ typedef struct sentinelRedisInstance { * are set to NULL no script is executed. */ char *notification_script; char *client_reconfig_script; + sds info; /* cached INFO output */ } sentinelRedisInstance; /* Main state. */ @@ -983,6 +984,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char * ri->promoted_slave = NULL; ri->notification_script = NULL; ri->client_reconfig_script = NULL; + ri->info = NULL; /* Role */ ri->role_reported = ri->flags & (SRI_MASTER|SRI_SLAVE); @@ -1015,6 +1017,7 @@ void releaseSentinelRedisInstance(sentinelRedisInstance *ri) { sdsfree(ri->slave_master_host); sdsfree(ri->leader); sdsfree(ri->auth_pass); + sdsfree(ri->info); releaseSentinelAddr(ri->addr); /* Clear state into the master if needed. */ @@ -1785,6 +1788,10 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) { int numlines, j; int role = 0; + /* cache full INFO output for instance */ + sdsfree(ri->info); + ri->info = sdsnew(info); + /* The following fields must be reset to a given value in the case they * are not found at all in the INFO output. */ ri->master_link_down_time = 0; @@ -2777,6 +2784,68 @@ void sentinelCommand(redisClient *c) { } else if (!strcasecmp(c->argv[1]->ptr,"set")) { if (c->argc < 3 || c->argc % 2 == 0) goto numargserr; sentinelSetCommand(c); + } else if (!strcasecmp(c->argv[1]->ptr,"info-cache")) { + if (c->argc < 2) goto numargserr; + /* Reply format: + * 1.) master name + * 2.) 1.) info from master + * 2.) info from replica + * . + * . + * 3.) next master name ... + * 4.) 1.) info from master + * 2.) info from replica + * . + * . + */ + dictType copy_keeper = instancesDictType; + copy_keeper.valDestructor = NULL; + dict *masters_local = NULL; + int needs_cleanup = 0; + if (c->argc == 2) { + masters_local = sentinel.masters; + } else { + masters_local = dictCreate(©_keeper, NULL); + needs_cleanup = 1; + + for (int i = 2; i < c->argc; i++) { + sentinelRedisInstance *ri; + ri = sentinelGetMasterByName(c->argv[i]->ptr); + if (!ri) continue; /* ignore non-existing names */ + dictAdd(masters_local, ri->name, ri); + } + } + + /* Now we can iterate over individually requested masters the + * same way we iterate over the entire sentinel->masters dict. */ + addReplyMultiBulkLen(c,dictSize(masters_local) * 2); + + dictIterator *di; + dictEntry *de; + di = dictGetIterator(masters_local); + while ((de = dictNext(di)) != NULL) { + sentinelRedisInstance *ri = dictGetVal(de); + addReplyBulkCBuffer(c,ri->name,strlen(ri->name)); + addReplyMultiBulkLen(c,dictSize(ri->slaves) + 1); /* +1 for self */ + if (ri->info) + addReplyBulkCBuffer(c,ri->info,sdslen(ri->info)); + else + addReply(c,shared.nullbulk); + + dictIterator *sdi; + dictEntry *sde; + sdi = dictGetIterator(ri->slaves); + while ((sde = dictNext(sdi)) != NULL) { + sentinelRedisInstance *sri = dictGetVal(sde); + if (sri->info) + addReplyBulkCBuffer(c,sri->info,sdslen(sri->info)); + else + addReply(c,shared.nullbulk); + } + dictReleaseIterator(sdi); + } + dictReleaseIterator(di); + if (needs_cleanup) dictRelease(masters_local); } else { addReplyErrorFormat(c,"Unknown sentinel subcommand '%s'", (char*)c->argv[1]->ptr);