[memcached] bradfitz, r288: merge all trunk changes since tag 1.1.12... (fwd)

Brad Fitzpatrick brad at danga.com
Tue May 30 04:05:56 UTC 2006


FYI...


---------- Forwarded message ----------
Date: Tue, 30 May 2006 04:05:14 +0000 (UTC)
From: commits at code.sixapart.com
To: cvs-commits at livejournal.com
Subject: [memcached] bradfitz,
     r288: merge all trunk changes since tag 1.1.12...

merge all trunk changes since tag 1.1.12 into the facebook branch



U   branches/facebook/ChangeLog
U   branches/facebook/Makefile.am
U   branches/facebook/TODO
U   branches/facebook/configure.ac
A   branches/facebook/daemon.c
A   branches/facebook/doc/OSX.txt
U   branches/facebook/doc/memcached.1
U   branches/facebook/doc/protocol.txt
U   branches/facebook/memcached.c
U   branches/facebook/memcached.h
U   branches/facebook/slabs.c
A   branches/facebook/status.sh


Modified: branches/facebook/ChangeLog
===================================================================
--- branches/facebook/ChangeLog	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/ChangeLog	2006-05-30 04:05:13 UTC (rev 288)
@@ -1,3 +1,30 @@
+2006-04-30
+	* River Tarnell:  autoconf work for Solaris 10.  Brad:
+	merge and verify it works on Nexenta.
+
+2006-03-04
+	* avva: bucket/generation patch (old, but Brad's just finally
+	committing it)
+
+2006-01-01
+	* Brad Fitzpatrick <brad at danga.com>:  allocate 1 slab per class
+	on start-up, to avoid confusing users with out-of-memory errors
+	later.  this is 18 MB of allocation on start, unless max memory
+	allowed with -m is lower, in which case only the smaller slab
+	classes are allocated.
+
+2005-08-09
+	* Elizabeth Mattijsen <liz at dijkmat.nl>: needed a way to flush all
+	memcached backend servers, but not at exactly the same time (to
+	reduce load peaks), I've added some simple functionality to the
+	memcached protocol in the "flush_all" command that allows you to
+	specify a time at which the flush will actually occur (instead of
+	always at the moment the "flush_all" command is received).
+
+2005-05-25
+	* patch from Peter van Dijk <peter at nextgear.nl> to make
+	  stderr unbuffered, for running under daemontools
+
 2005-04-04
 	* patch from Don MacAskill <don at smugmug.com> 'flush_all' doesn't
 	seem to work properly.  Basically, if you try to add a key which

Modified: branches/facebook/Makefile.am
===================================================================
--- branches/facebook/Makefile.am	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/Makefile.am	2006-05-30 04:05:13 UTC (rev 288)
@@ -1,6 +1,7 @@
 bin_PROGRAMS = memcached

 memcached_SOURCES = memcached.c slabs.c items.c memcached.h assoc.c
+memcached_LDADD = @LIBOBJS@

 SUBDIRS = doc
 DIST_DIRS = scripts

Modified: branches/facebook/TODO
===================================================================
--- branches/facebook/TODO	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/TODO	2006-05-30 04:05:13 UTC (rev 288)
@@ -6,3 +6,7 @@

 * calendar queue for early expirations of items, so they don't push
   out other objects with infinite expirations.
+
+* curr_items never decreases?  mailing list report.
+
+* memcached to listen on more than one IP.  mailing list request.

Modified: branches/facebook/configure.ac
===================================================================
--- branches/facebook/configure.ac	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/configure.ac	2006-05-30 04:05:13 UTC (rev 288)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.52)
-AC_INIT(memcached, 1.1.11, brad at danga.com)
+AC_INIT(memcached, 1.1.13-pre, brad at danga.com)
 AC_CANONICAL_SYSTEM
 AC_CONFIG_SRCDIR(memcached.c)
 AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
@@ -19,6 +19,13 @@
 AC_CHECK_LIB(event, event_set, ,
 	[AC_MSG_ERROR(libevent is required.  You can get it from $LIBEVENT_URL)])

+AC_SEARCH_LIBS(socket, socket)
+AC_SEARCH_LIBS(gethostbyname, nsl)
+AC_SEARCH_LIBS(mallinfo, malloc)
+
+AC_CHECK_FUNC(daemon,AC_DEFINE([HAVE_DAEMON],,[Define this if you have daemon()]),[AC_LIBOBJ(daemon)])
+
+
 AC_CHECK_HEADER(malloc.h, AC_DEFINE(HAVE_MALLOC_H,,[do we have malloc.h?]))
 AC_CHECK_MEMBER([struct mallinfo.arena], [
 		AC_DEFINE(HAVE_STRUCT_MALLINFO,,[do we have stuct mallinfo?])

Copied: branches/facebook/daemon.c (from rev 287, trunk/server/daemon.c)

Copied: branches/facebook/doc/OSX.txt (from rev 287, trunk/server/doc/OSX.txt)


Property changes on: branches/facebook/doc/OSX.txt
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Modified: branches/facebook/doc/memcached.1
===================================================================
--- branches/facebook/doc/memcached.1	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/doc/memcached.1	2006-05-30 04:05:13 UTC (rev 288)
@@ -34,10 +34,6 @@
 .B \-m <num>
 Use <num> MB memory max to use for object storage; the default is 64 megabytes.
 .TP
-.B \-M
-Instead of throwing items from the cache when max memory is reached, throw an
-error
-.TP
 .B \-c <num>
 Use <num> max simultaneous connections; the default is 1024.
 .TP

Modified: branches/facebook/doc/protocol.txt
===================================================================
--- branches/facebook/doc/protocol.txt	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/doc/protocol.txt	2006-05-30 04:05:13 UTC (rev 288)
@@ -360,16 +360,17 @@
 Other commands
 --------------

-"flush_all" is a command with no arguments. It always succeeds,
-and the server sends "OK\r\n" in response. Its effect is to immediately
-invalidate all existing items: none of them will be returned in
-response to a retrieval command (unless it's stored again under the
-same key *after* flush_all has been executed). flush_all doesn't
+"flush_all" is a command with an optional numeric argument. It always
+succeeds, and the server sends "OK\r\n" in response. Its effect is to
+invalidate all existing items immediately (by default) or after the
+expiration specified.  After invalidation none of the items will be returned
+in response to a retrieval command (unless it's stored again under the
+same key *after* flush_all has invalidated the items). flush_all doesn't
 actually free all the memory taken up by existing items; that will
 happen gradually as new items are stored. The most precise definition
 of what flush_all does is the following: it causes all items whose
-update time is earlier than the time at which flush_all was executed
-to be ignored for retrieval purposes.
+update time is earlier than the time at which flush_all was set to be
+executed to be ignored for retrieval purposes.

 "version" is a command with no arguments:


Modified: branches/facebook/memcached.c
===================================================================
--- branches/facebook/memcached.c	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/memcached.c	2006-05-30 04:05:13 UTC (rev 288)
@@ -66,9 +66,11 @@
 #define TRANSMIT_SOFT_ERROR 2
 #define TRANSMIT_HARD_ERROR 3

+int *buckets = 0; /* bucket->generation array for a managed instance */
+
+#define REALTIME_MAXDELTA 60*60*24*30
 rel_time_t realtime(time_t exptime) {
     /* no. of seconds in 30 days - largest possible delta exptime */
-    #define REALTIME_MAXDELTA 60*60*24*30

     if (exptime == 0) return 0; /* 0 means never expire */

@@ -100,6 +102,7 @@
     settings.verbose = 0;
     settings.oldest_live = 0;
     settings.evict_to_free = 1;       /* push old items out of cache when memory runs out */
+    settings.managed = 0;
     settings.factor = 1.25;
     settings.chunk_size = 48;         /* space for a modest key and value */
 }
@@ -193,6 +196,11 @@
             perror("malloc()");
             return 0;
         }
+
+        c->rsize = c->wsize = DATA_BUFFER_SIZE;
+        c->rcurr = c->rbuf;
+        c->isize = 200;
+
         stats.conn_structs++;
     }

@@ -211,7 +219,7 @@
     c->rlbytes = 0;
     c->rbytes = c->wbytes = 0;
     c->wcurr = c->wbuf;
-    c->rcurr = c->rbuf;
+    c->ritem = 0;
     c->icurr = c->ilist;
     c->ileft = 0;
     c->iovused = 0;
@@ -221,6 +229,8 @@
     c->write_and_go = conn_read;
     c->write_and_free = 0;
     c->item = 0;
+    c->bucket = -1;
+    c->gen = 0;

     event_set(&c->event, sfd, event_flags, event_handler, (void *)c);
     c->ev_flags = event_flags;
@@ -706,7 +716,23 @@
             out_string(c, "CLIENT_ERROR bad command line format");
             return;
         }
+
+        if (settings.managed) {
+            int bucket = c->bucket;
+            if (bucket == -1) {
+                out_string(c, "CLIENT_ERROR no BG data in managed mode");
+                return;
+            }
+            c->bucket = -1;
+            if (buckets[bucket] != c->gen) {
+                out_string(c, "ERROR_NOT_OWNER");
+                return;
+            }
+        }
+
+        expire = realtime(expire);
         it = item_alloc(key, flags, realtime(expire), len+2);
+
         if (it == 0) {
             out_string(c, "SERVER_ERROR out of memory");
             /* swallow the data line */
@@ -717,7 +743,7 @@

         c->item_comm = comm;
         c->item = it;
-        c->rcurr = ITEM_data(it);
+        c->ritem = ITEM_data(it);
         c->rlbytes = it->nbytes;
         c->state = conn_nread;
         return;
@@ -739,7 +765,20 @@
             out_string(c, "CLIENT_ERROR bad command line format");
             return;
         }
-
+
+        if (settings.managed) {
+            int bucket = c->bucket;
+            if (bucket == -1) {
+                out_string(c, "CLIENT_ERROR no BG data in managed mode");
+                return;
+            }
+            c->bucket = -1;
+            if (buckets[bucket] != c->gen) {
+                out_string(c, "ERROR_NOT_OWNER");
+                return;
+            }
+        }
+
         it = assoc_find(key);
         if (it && (it->it_flags & ITEM_DELETED)) {
             it = 0;
@@ -786,6 +825,10 @@
         return;
     }

+    if (strncmp(command, "bget ", 5) == 0) {
+        c->binary = 1;
+        goto get;
+    }
     if (strncmp(command, "get ", 4) == 0) {

         char *start = command + 4;
@@ -794,7 +837,21 @@
         int i = 0;
         item *it;
         rel_time_t now = current_time;
+    get:

+        if (settings.managed) {
+            int bucket = c->bucket;
+            if (bucket == -1) {
+                out_string(c, "CLIENT_ERROR no BG data in managed mode");
+                return;
+            }
+            c->bucket = -1;
+            if (buckets[bucket] != c->gen) {
+                out_string(c, "ERROR_NOT_OWNER");
+                return;
+            }
+        }
+
         while(sscanf(start, " %250s%n", key, &next) >= 1) {
             start+=next;
             stats.get_cmds++;
@@ -802,8 +859,8 @@
             if (it && (it->it_flags & ITEM_DELETED)) {
                 it = 0;
             }
-            if (settings.oldest_live && it &&
-                it->time <= settings.oldest_live) {
+            if (settings.oldest_live && settings.oldest_live <= now &&
+                it && it->time <= settings.oldest_live) {
                 item_unlink(it);
                 it = 0;
             }
@@ -868,6 +925,19 @@
         int res;
         time_t exptime = 0;

+        if (settings.managed) {
+            int bucket = c->bucket;
+            if (bucket == -1) {
+                out_string(c, "CLIENT_ERROR no BG data in managed mode");
+                return;
+            }
+            c->bucket = -1;
+            if (buckets[bucket] != c->gen) {
+                out_string(c, "ERROR_NOT_OWNER");
+                return;
+            }
+        }
+
         res = sscanf(command, "%*s %250s %ld", key, &exptime);
         it = assoc_find(key);
         if (!it) {
@@ -904,14 +974,97 @@
         out_string(c, "DELETED");
         return;
     }
-
+
+    if (strncmp(command, "own ", 4) == 0) {
+        int bucket, gen;
+        char *start = command+4;
+        if (!settings.managed) {
+            out_string(c, "CLIENT_ERROR not a managed instance");
+            return;
+        }
+        if (sscanf(start, "%u:%u\r\n", &bucket,&gen) == 2) {
+            if ((bucket < 0) || (bucket >= MAX_BUCKETS)) {
+                out_string(c, "CLIENT_ERROR bucket number out of range");
+                return;
+            }
+            buckets[bucket] = gen;
+            out_string(c, "OWNED");
+            return;
+        } else {
+            out_string(c, "CLIENT_ERROR bad format");
+            return;
+        }
+    }
+
+    if (strncmp(command, "disown ", 7) == 0) {
+        int bucket;
+        char *start = command+7;
+        if (!settings.managed) {
+            out_string(c, "CLIENT_ERROR not a managed instance");
+            return;
+        }
+        if (sscanf(start, "%u\r\n", &bucket) == 1) {
+            if ((bucket < 0) || (bucket >= MAX_BUCKETS)) {
+                out_string(c, "CLIENT_ERROR bucket number out of range");
+                return;
+            }
+            buckets[bucket] = 0;
+            out_string(c, "DISOWNED");
+            return;
+        } else {
+            out_string(c, "CLIENT_ERROR bad format");
+            return;
+        }
+    }
+
+    if (strncmp(command, "bg ", 3) == 0) {
+        int bucket, gen;
+        char *start = command+3;
+        if (!settings.managed) {
+            out_string(c, "CLIENT_ERROR not a managed instance");
+            return;
+        }
+        if (sscanf(start, "%u:%u\r\n", &bucket,&gen) == 2) {
+            /* we never write anything back, even if input's wrong */
+            if ((bucket < 0) || (bucket >= MAX_BUCKETS) || (gen<=0)) {
+                /* do nothing, bad input */
+            } else {
+                c->bucket = bucket;
+                c->gen = gen;
+            }
+            c->state = conn_read;
+            /* normally conn_write uncorks the connection, but this
+               is the only time we accept a command w/o writing anything */
+            set_cork(c,0);
+            return;
+        } else {
+            out_string(c, "CLIENT_ERROR bad format");
+            return;
+        }
+    }
+
     if (strncmp(command, "stats", 5) == 0) {
         process_stat(c, command);
         return;
     }

-    if (strcmp(command, "flush_all") == 0) {
-        settings.oldest_live = current_time;
+    if (strncmp(command, "flush_all", 9) == 0) {
+        time_t exptime = 0;
+        int res;
+
+        if (strcmp(command, "flush_all") == 0) {
+            settings.oldest_live = current_time;
+            out_string(c, "OK");
+            return;
+        }
+
+        res = sscanf(command, "%*s %ld", &exptime);
+        if (res != 1) {
+            out_string(c, "ERROR");
+            return;
+        }
+
+        settings.oldest_live = realtime(exptime);
         out_string(c, "OK");
         return;
     }
@@ -957,29 +1110,27 @@
 }

 /*
- * if we have a complete line in the buffer, process it and move whatever
- * remains in the buffer to its beginning.
+ * if we have a complete line in the buffer, process it.
  */
 int try_read_command(conn *c) {
     char *el, *cont;

     if (!c->rbytes)
         return 0;
-    el = memchr(c->rbuf, '\n', c->rbytes);
+    el = memchr(c->rcurr, '\n', c->rbytes);
     if (!el)
         return 0;
     cont = el + 1;
-    if (el - c->rbuf > 1 && *(el - 1) == '\r') {
+    if (el - c->rcurr > 1 && *(el - 1) == '\r') {
         el--;
     }
     *el = '\0';

-    process_command(c, c->rbuf);
+    process_command(c, c->rcurr);

-    if (cont - c->rbuf < c->rbytes) { /* more stuff in the buffer */
-        memmove(c->rbuf, cont, c->rbytes - (cont - c->rbuf));
-    }
-    c->rbytes -= (cont - c->rbuf);
+    c->rbytes -= (cont - c->rcurr);
+    c->rcurr = cont;
+
     return 1;
 }

@@ -1019,11 +1170,20 @@
 /*
  * read from network as much as we can, handle buffer overflow and connection
  * close.
+ * before reading, move the remaining incomplete fragment of a command
+ * (if any) to the beginning of the buffer.
  * return 0 if there's nothing to read on the first read.
  */
 int try_read_network(conn *c) {
     int gotdata = 0;
     int res;
+
+    if (c->rcurr != c->rbuf) {
+        if (c->rbytes != 0) /* otherwise there's nothing to copy */
+            memmove(c->rbuf, c->rcurr, c->rbytes);
+        c->rcurr = c->rbuf;
+    }
+
     while (1) {
         if (c->rbytes >= c->rsize) {
             char *new_rbuf = realloc(c->rbuf, c->rsize*2);
@@ -1035,7 +1195,8 @@
                 c->write_and_go = conn_closing;
                 return 1;
             }
-            c->rbuf = new_rbuf; c->rsize *= 2;
+            c->rcurr  = c->rbuf = new_rbuf;
+            c->rsize *= 2;
         }
         c->request_addr_size = sizeof(c->request_addr);
         res = read(c->sfd, c->rbuf + c->rbytes, c->rsize - c->rbytes);
@@ -1189,7 +1350,7 @@
             break;

         case conn_nread:
-            /* we are reading rlbytes into rcurr; */
+            /* we are reading rlbytes into ritem; */
             if (c->rlbytes == 0) {
                 complete_nread(c);
                 break;
@@ -1197,21 +1358,19 @@
             /* first check if we have leftovers in the conn_read buffer */
             if (c->rbytes > 0) {
                 int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
-                memcpy(c->rcurr, c->rbuf, tocopy);
+                memcpy(c->ritem, c->rcurr, tocopy);
+                c->ritem += tocopy;
+                c->rlbytes -= tocopy;
                 c->rcurr += tocopy;
-                c->rlbytes -= tocopy;
-                if (c->rbytes > tocopy) {
-                    memmove(c->rbuf, c->rbuf+tocopy, c->rbytes - tocopy);
-                }
                 c->rbytes -= tocopy;
                 break;
             }

             /*  now try reading from the socket */
-            res = read(c->sfd, c->rcurr, c->rlbytes);
+            res = read(c->sfd, c->ritem, c->rlbytes);
             if (res > 0) {
                 stats.bytes_read += res;
-                c->rcurr += res;
+                c->ritem += res;
                 c->rlbytes -= res;
                 break;
             }
@@ -1246,9 +1405,7 @@
             if (c->rbytes > 0) {
                 int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes;
                 c->sbytes -= tocopy;
-                if (c->rbytes > tocopy) {
-                    memmove(c->rbuf, c->rbuf+tocopy, c->rbytes - tocopy);
-                }
+                c->rcurr += tocopy;
                 c->rbytes -= tocopy;
                 break;
             }
@@ -1347,10 +1504,9 @@
     return;
 }

-
 void event_handler(int fd, short which, void *arg) {
     conn *c;
-
+
     c = (conn *)arg;
     c->which = which;

@@ -1554,6 +1710,7 @@
     printf("-vv           very verbose (also print client commands/reponses)\n");
     printf("-h            print this help and exit\n");
     printf("-i            print memcached and libevent license\n");
+    printf("-b            run a managed instanced (mnemonic: buckets)\n");
     printf("-P <file>     save PID in <file>, only used with -d option\n");
     printf("-f <factor>   chunk size growth factor, default 1.25\n");
     printf("-s <bytes>    minimum space allocated for key+value+flags, default 48\n");
@@ -1686,12 +1843,18 @@
     /* init settings */
     settings_init();

+    /* set stderr non-buffering (for running under, say, daemontools) */
+    setbuf(stderr, NULL);
+
     /* process arguments */
-    while ((c = getopt(argc, argv, "p:U:m:Mc:khirvdl:u:P:f:s:")) != -1) {
+    while ((c = getopt(argc, argv, "bp:U:m:Mc:khirvdl:u:P:f:s:")) != -1) {
         switch (c) {
         case 'U':
             settings.udpport = atoi(optarg);
             break;
+        case 'b':
+            settings.managed = 1;
+            break;
         case 'p':
             settings.port = atoi(optarg);
             break;
@@ -1717,7 +1880,7 @@
             settings.verbose++;
             break;
         case 'l':
-            if (!inet_aton(optarg, &addr)) {
+            if (!inet_pton(AF_INET, optarg, &addr)) {
                 fprintf(stderr, "Illegal address: %s\n", optarg);
                 return 1;
             } else {
@@ -1863,6 +2026,16 @@
     conn_init();
     slabs_init(settings.maxbytes, settings.factor);

+    /* managed instance? alloc and zero a bucket array */
+    if (settings.managed) {
+        buckets = malloc(sizeof(int)*MAX_BUCKETS);
+        if (buckets == 0) {
+            fprintf(stderr, "failed to allocate the bucket array");
+            exit(1);
+        }
+        memset(buckets, 0, sizeof(int)*MAX_BUCKETS);
+    }
+
     /* lock paged memory if needed */
     if (lock_memory) {
 #ifdef HAVE_MLOCKALL

Modified: branches/facebook/memcached.h
===================================================================
--- branches/facebook/memcached.h	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/memcached.h	2006-05-30 04:05:13 UTC (rev 288)
@@ -34,6 +34,7 @@
     struct in_addr interface;
     int verbose;
     rel_time_t oldest_live; /* ignore existing items older than this */
+    int managed;          /* if 1, a tracker manages virtual buckets */
     int evict_to_free;
     double factor;          /* chunk size growth factor */
     int chunk_size;
@@ -92,11 +93,12 @@
     int    state;
     struct event event;
     short  ev_flags;
-    short  which;  /* which events were just triggered */
+    short  which;   /* which events were just triggered */

-    char   *rbuf;
-    int    rsize;
-    int    rbytes;
+    char   *rbuf;   /* buffer to read commands into */
+    char   *rcurr;  /* but if we parsed some already, this is where we stopped */
+    int    rsize;   /* total allocated size of rbuf */
+    int    rbytes;  /* how much data, starting from rcur, do we have unparsed */

     char   *wbuf;
     char   *wcurr;
@@ -105,7 +107,7 @@
     int    write_and_go; /* which state to go into after finishing current write */
     void   *write_and_free; /* free this memory after finishing writing */

-    char   *rcurr;
+    char   *ritem;  /* when we read in an item's value, it goes here */
     int    rlbytes;

     /* data for the nread state */
@@ -145,8 +147,16 @@
     socklen_t request_addr_size;
     unsigned char *hdrbuf; /* udp packet headers */
     int    hdrsize;   /* number of headers' worth of space is allocated */
+
+    int    binary;    /* are we in binary mode */
+    int    bucket;    /* bucket number for the next command, if running as
+                         a managed instance. -1 (_not_ 0) means invalid. */
+    int    gen;       /* generation requested for the bucket */
 } conn;

+/* number of virtual buckets for a managed instance */
+#define MAX_BUCKETS 32768
+
 /* listening socket */
 extern int l_socket;

@@ -179,6 +189,14 @@
    size equal to the previous slab's chunk size times this factor. */
 void slabs_init(size_t limit, double factor);

+/* Preallocate as many slab pages as possible (called from slabs_init)
+   on start-up, so users don't get confused out-of-memory errors when
+   they do have free (in-slab) space, but no space to make new slabs.
+   if maxslabs is 18 (POWER_LARGEST - POWER_SMALLEST + 1), then all
+   slab types can be made.  if max memory is less than 18 MB, only the
+   smaller ones will be made.  */
+void slabs_preallocate (unsigned int maxslabs);
+
 /* Given object size, return id to use when allocating/freeing memory for object */
 /* 0 means error: can't store such a large object */
 unsigned int slabs_clsid(size_t size);

Modified: branches/facebook/slabs.c
===================================================================
--- branches/facebook/slabs.c	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/slabs.c	2006-05-30 04:05:13 UTC (rev 288)
@@ -106,8 +106,30 @@
     power_largest = i;
     slabclass[power_largest].size = POWER_BLOCK;
     slabclass[power_largest].perslab = 1;
+
+#ifndef DONT_PREALLOC_SLABS
+    slabs_preallocate(limit / POWER_BLOCK);
+#endif
 }

+void slabs_preallocate (unsigned int maxslabs) {
+    int i;
+    unsigned int prealloc = 0;
+
+    /* pre-allocate a 1MB slab in every size class so people don't get
+       confused by non-intuitive "SERVER_ERROR out of memory"
+       messages.  this is the most common question on the mailing
+       list.  if you really don't want this, you can rebuild without
+       these three lines.  */
+
+    for(i=POWER_SMALLEST; i<=POWER_LARGEST; i++) {
+        if (++prealloc > maxslabs)
+            return;
+        slabs_newslab(i);
+    }
+
+}
+
 static int grow_slab_list (unsigned int id) {
     slabclass_t *p = &slabclass[id];
     if (p->slabs == p->list_size) {

Added: branches/facebook/status.sh
===================================================================
--- branches/facebook/status.sh	2006-05-30 02:31:43 UTC (rev 287)
+++ branches/facebook/status.sh	2006-05-30 04:05:13 UTC (rev 288)
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+svn diff http://code.sixapart.com/svn/memcached/trunk/server http://code.sixapart.com/svn/memcached/branches/facebook


Property changes on: branches/facebook/status.sh
___________________________________________________________________
Name: svn:executable
   + *




More information about the memcached mailing list