"Set" and then "get" don't retrieve the stored value

Steven Grimm sgrimm at facebook.com
Mon Nov 20 19:56:28 UTC 2006


Here's a possible solution. This takes advantage of the LRU queue; it is 
too expensive to scan the entire cache and forcibly delete every item, 
but it's *not* that expensive to scan just the last few items in the 
queue up until we get to an item that the regular "oldest allowed" 
setting can handle, deleting the newer ones. With that in place, we can 
set the oldest time back one second, so newly set items aren't 
immediately expired.

-Steve


Steven Grimm wrote:
> I think I have a good solution for this, actually. Patch forthcoming 
> (easier to code it than write it up.)
>
> -Steve

-------------- next part --------------
Index: memcached.c
===================================================================
--- memcached.c	(revision 435)
+++ memcached.c	(working copy)
@@ -95,11 +95,11 @@
     stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = 0;
     stats.curr_bytes = stats.bytes_read = stats.bytes_written = 0;
 
-    /* make the time we started always be 1 second before we really
+    /* make the time we started always be 2 seconds before we really
        did, so time(0) - time.started is never zero.  if so, things
        like 'settings.oldest_live' which act as booleans as well as
        values are now false in boolean context... */
-    stats.started = time(0) - 1;
+    stats.started = time(0) - 2;
 }
 void stats_reset(void) {
     stats.total_items = stats.total_conns = 0;
@@ -1155,7 +1155,8 @@
         set_current_time();
 
         if (strcmp(command, "flush_all") == 0) {
-            settings.oldest_live = current_time;
+            settings.oldest_live = current_time - 1;
+            item_flush_expired();
             out_string(c, "OK");
             return;
         }
@@ -1166,7 +1167,8 @@
             return;
         }
 
-        settings.oldest_live = realtime(exptime);
+        settings.oldest_live = realtime(exptime) - 1;
+        item_flush_expired();
         out_string(c, "OK");
         return;
     }
Index: memcached.h
===================================================================
--- memcached.h	(revision 435)
+++ memcached.h	(working copy)
@@ -271,6 +271,7 @@
 char *item_cachedump(unsigned int slabs_clsid, unsigned int limit, unsigned int *bytes);
 char *item_stats_sizes(int *bytes);
 void item_stats(char *buffer, int buflen);
+void item_flush_expired(void);
 
 /* time handling */
 void set_current_time ();  /* update the global variable holding
Index: items.c
===================================================================
--- items.c	(revision 435)
+++ items.c	(working copy)
@@ -320,3 +320,29 @@
     free(histogram);
     return buf;
 }
+
+/* expires items that are more recent than the oldest_live setting. */
+void item_flush_expired() {
+    int i;
+    item *iter, *next;
+    if (! settings.oldest_live)
+        return;
+    for (i = 0; i < LARGEST_ID; i++) {
+        /* The LRU is sorted in decreasing time order, and an item's timestamp
+         * is never newer than its last access time, so we only need to walk
+         * back until we hit an item older than the oldest_live time.
+         * The oldest_live checking will auto-expire the remaining items.
+         */
+        for (iter = heads[i]; iter != NULL; iter = next) {
+            if (iter->time >= settings.oldest_live) {
+                next = iter->next;
+                if ((iter->it_flags & ITEM_SLABBED) == 0) {
+                    item_unlink(iter);
+                }
+            } else {
+                /* We've hit the first old item. Continue to the next queue. */
+                break;
+            }
+        }
+    }
+}
Index: t/flush-all.t
===================================================================
--- t/flush-all.t	(revision 435)
+++ t/flush-all.t	(working copy)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 
 use strict;
-use Test::More tests => 11;
+use Test::More tests => 10;
 use FindBin qw($Bin);
 use lib "$Bin/lib";
 use MemcachedTest;
@@ -16,19 +16,13 @@
 mem_get_is($sock, "foo", "fooval");
 print $sock "flush_all\r\n";
 is(scalar <$sock>, "OK\r\n", "did flush_all");
-
 mem_get_is($sock, "foo", undef);
-SKIP: {
-    skip "flush_all is still only second-granularity.  need atomic counter on flush_all.", 2 unless 0;
 
-    print $sock "set foo 0 0 3\r\nnew\r\n";
-    is(scalar <$sock>, "STORED\r\n", "stored foo = 'new'");
-    mem_get_is($sock, "foo", 'new');
-}
+# check that flush_all doesn't blow away items that immediately get set
+print $sock "set foo 0 0 3\r\nnew\r\n";
+is(scalar <$sock>, "STORED\r\n", "stored foo = 'new'");
+mem_get_is($sock, "foo", 'new');
 
-sleep 1;
-mem_get_is($sock, "foo", undef);
-
 # and the other form, specifying a flush_all time...
 my $expire = time() + 2;
 print $sock "flush_all $expire\r\n";


More information about the memcached mailing list