Прикладная демонология, практические рецепты

Расширенная статистика кеша memcached

--- items.h.orig    2008-04-28 04:37:56.000000000 +0400
+++ items.h 2008-11-14 13:10:36.000000000 +0300
@@ -13,6 +13,7 @@

 /*@null@*/
 char *do_item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);
+char *do_item_cachestat(const unsigned int flags, const unsigned int limit, const unsigned int memlimit, unsigned int *bytes);
 char *do_item_stats(int *bytes);

 /*@null@*/
--- items.c.orig    2008-07-29 20:37:27.000000000 +0400
+++ items.c 2008-11-14 13:10:36.000000000 +0300
@@ -333,6 +333,61 @@
     return buffer;
 }

+char *do_item_cachestat(const unsigned int flags, const unsigned int limit, const unsigned int memlimit, unsigned int *bytes) {
+    char *buffer;
+    unsigned int bufcurr;
+    item *it;
+    unsigned int len;
+    unsigned int shown = 0;
+    unsigned int done = 0;
+    char temp[512];
+    unsigned int slabs_clsid;
+    unsigned int memlimitb;
+    rel_time_t now = current_time;
+
+    if (memlimit<=0) return NULL;
+    memlimitb = memlimit * 1024 * 1024;
+    buffer = malloc((size_t)memlimitb);
+    if (buffer == 0) return NULL;
+    bufcurr = 0;
+
+    len = snprintf(temp, sizeof(temp), "KEY BYTES EXPIRE ATIME\r\n");
+    strcpy(buffer + bufcurr, temp);
+    bufcurr += len;
+    if (limit) {
+        len = snprintf(temp, sizeof(temp), "LIMITED TO %u\r\n", limit);
+        strcpy(buffer + bufcurr, temp);
+        bufcurr += len;
+    }
+
+    for (slabs_clsid=0; slabs_clsid<=LARGEST_ID && !done; slabs_clsid++) {
+        it = heads[slabs_clsid];
+        while (it != NULL && (limit == 0 || shown < limit)) {
+            if ((!(flags & 1) && it->exptime) || (!(flags & 2) && !it->exptime)) {
+                it = it->next;
+                continue;
+            }
+            len = snprintf(temp, sizeof(temp), "%s %d %d %d\r\n", ITEM_key(it), it->nbytes - 2, (it->exptime ? it->exptime - now : 0), now - it->time);
+            if (bufcurr + len + 29 > memlimitb) {  /* 29 is MEMORY LIMIT EXCEEDED\r\nEND\r\n\0 */
+                strcpy(buffer + bufcurr, "MEMORY LIMIT EXCEEDED\r\n");
+                bufcurr += 23;
+       done = 1;
+                break;
+            }
+            strcpy(buffer + bufcurr, temp);
+            bufcurr += len;
+            shown++;
+            it = it->next;
+        }
+    }
+
+    memcpy(buffer + bufcurr, "END\r\n", 6);
+    bufcurr += 5;
+
+    *bytes = bufcurr;
+    return buffer;
+}
+
 char *do_item_stats(int *bytes) {
     size_t bufleft = (size_t) LARGEST_ID * 160;
     char *buffer = malloc(bufleft);
--- memcached.h.orig    2008-07-29 20:37:27.000000000 +0400
+++ memcached.h 2008-11-14 13:10:36.000000000 +0300
@@ -69,6 +69,7 @@
     unsigned int  conn_structs;
     uint64_t      get_cmds;
     uint64_t      set_cmds;
+    uint64_t      set_cmds_forever;
     uint64_t      get_hits;
     uint64_t      get_misses;
     uint64_t      evictions;
@@ -345,6 +346,7 @@
 # define is_listen_thread()          1
 # define item_alloc(x,y,z,a,b)       do_item_alloc(x,y,z,a,b)
 # define item_cachedump(x,y,z)       do_item_cachedump(x,y,z)
+# define item_cachestat(w,x,y,z)     do_item_cachestat(w,x,y,z)
 # define item_flush_expired()        do_item_flush_expired()
 # define item_get_notedeleted(x,y,z) do_item_get_notedeleted(x,y,z)
 # define item_link(x)                do_item_link(x)
--- memcached.c.orig    2008-07-29 20:37:27.000000000 +0400
+++ memcached.c 2008-11-14 13:10:36.000000000 +0300
@@ -142,7 +142,7 @@

 static void stats_init(void) {
     stats.curr_items = stats.total_items = stats.curr_conns = stats.total_conns = stats.conn_structs = 0;
-    stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = stats.evictions = 0;
+    stats.get_cmds = stats.set_cmds = stats.set_cmds_forever = stats.get_hits = stats.get_misses = stats.evictions = 0;
     stats.curr_bytes = stats.bytes_read = stats.bytes_written = 0;

     /* make the time we started always be 2 seconds before we really
@@ -156,7 +156,7 @@
 static void stats_reset(void) {
     STATS_LOCK();
     stats.total_items = stats.total_conns = 0;
-    stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = stats.evictions = 0;
+    stats.get_cmds = stats.set_cmds = stats.set_cmds_forever = stats.get_hits = stats.get_misses = stats.evictions = 0;
     stats.bytes_read = stats.bytes_written = 0;
     stats_prefix_clear();
     STATS_UNLOCK();
@@ -782,6 +782,7 @@

     STATS_LOCK();
     stats.set_cmds++;
+    if (!it->exptime) stats.set_cmds_forever++;
     STATS_UNLOCK();

     if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) {
@@ -1089,6 +1090,7 @@
         pos += sprintf(pos, "STAT connection_structures %u\r\n", stats.conn_structs);
         pos += sprintf(pos, "STAT cmd_get %llu\r\n", stats.get_cmds);
         pos += sprintf(pos, "STAT cmd_set %llu\r\n", stats.set_cmds);
+        pos += sprintf(pos, "STAT cmd_set_forever %llu\r\n", stats.set_cmds_forever);
         pos += sprintf(pos, "STAT get_hits %llu\r\n", stats.get_hits);
         pos += sprintf(pos, "STAT get_misses %llu\r\n", stats.get_misses);
         pos += sprintf(pos, "STAT evictions %llu\r\n", stats.evictions);
@@ -1194,6 +1196,30 @@
         return;
     }

+    if (strcmp(subcommand, "cachestat") == 0) {
+
+        char *buf;
+        unsigned int bytes, flags = 0, limit = 10, memlimit = 2;
+
+        if(ntokens < 3) {
+            out_string(c, "CLIENT_ERROR bad command line");
+            return;
+        }
+
+        if (ntokens>3) flags = strtoul(tokens[2].value, NULL, 10);
+        if (ntokens>4) limit = strtoul(tokens[3].value, NULL, 10);
+        if (ntokens>5) memlimit = strtoul(tokens[4].value, NULL, 10);
+
+        if(errno == ERANGE) {
+            out_string(c, "CLIENT_ERROR bad command line format");
+            return;
+        }
+
+        buf = item_cachestat(flags, limit, memlimit, &bytes);
+        write_and_free(c, buf, bytes);
+        return;
+    }
+
     if (strcmp(subcommand, "slabs") == 0) {
         int bytes = 0;
         char *buf = slabs_stats(&bytes);