<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>devtools/bench_noreply.pl</filename>
    </added>
    <added>
      <filename>t/noreply.t</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -126,9 +126,10 @@ Storage commands
 
 First, the client sends a command line which looks like this:
 
-&lt;command name&gt; &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt; [&lt;cas unqiue&gt;]\r\n
+&lt;command name&gt; &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt; [noreply]\r\n
+cas &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt; &lt;cas unqiue&gt; [noreply]\r\n
 
-- &lt;command name&gt; is &quot;set&quot;, &quot;add&quot;, &quot;replace&quot;, &quot;append&quot;, &quot;prepend&quot;, or &quot;cas&quot;
+- &lt;command name&gt; is &quot;set&quot;, &quot;add&quot;, &quot;replace&quot;, &quot;append&quot; or &quot;prepend&quot;
 
   &quot;set&quot; means &quot;store this data&quot;.  
 
@@ -174,6 +175,12 @@ First, the client sends a command line which looks like this:
   Clients should use the value returned from the &quot;gets&quot; command
   when issuing &quot;cas&quot; updates.
 
+- &quot;noreply&quot; optional parameter instructs the server to not send the
+  reply.  NOTE: if the request line is malformed, the server can't
+  parse &quot;noreply&quot; option reliably.  In this case it may send the error
+  to the client, and not reading it on the client side will break
+  things.  Client should construct only valid requests.
+
 After this line, the client sends the data block:
 
 &lt;data block&gt;\r\n
@@ -245,7 +252,7 @@ Deletion
 
 The command &quot;delete&quot; allows for explicit deletion of items:
 
-delete &lt;key&gt; &lt;time&gt;\r\n
+delete &lt;key&gt; [&lt;time&gt;] [noreply]\r\n
 
 - &lt;key&gt; is the key of the item the client wishes the server to delete
 
@@ -261,6 +268,10 @@ delete &lt;key&gt; &lt;time&gt;\r\n
   (which means that the item will be deleted immediately and further
   storage commands with this key will succeed).
 
+- &quot;noreply&quot; optional parameter instructs the server to not send the
+  reply.  See the note in Storage commands regarding malformed
+  requests.
+
 The response line to this command can be one of:
 
 - &quot;DELETED\r\n&quot; to indicate success
@@ -285,17 +296,21 @@ non-existent key exists with value 0; instead, they will fail.
 
 The client sends the command line:
 
-incr &lt;key&gt; &lt;value&gt;\r\n
+incr &lt;key&gt; &lt;value&gt; [noreply]\r\n
 
 or
 
-decr &lt;key&gt; &lt;value&gt;\r\n
+decr &lt;key&gt; &lt;value&gt; [noreply]\r\n
 
 - &lt;key&gt; is the key of the item the client wishes to change
 
 - &lt;value&gt; is the amount by which the client wants to increase/decrease
 the item. It is a decimal representation of a 64-bit unsigned integer.
 
+- &quot;noreply&quot; optional parameter instructs the server to not send the
+  reply.  See the note in Storage commands regarding malformed
+  requests.
+
 The response will be one of:
 
 - &quot;NOT_FOUND\r\n&quot; to indicate the item with this value was not found
@@ -399,16 +414,17 @@ Other commands
 --------------
 
 &quot;flush_all&quot; is a command with an optional numeric argument. It always
-succeeds, and the server sends &quot;OK\r\n&quot; 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 set to be
-executed to be ignored for retrieval purposes.
+succeeds, and the server sends &quot;OK\r\n&quot; in response (unless &quot;noreply&quot;
+is given as the last parameter). 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 set to be executed to be ignored for retrieval purposes.
 
 The intent of flush_all with a delay, was that in a setting where you
 have a pool of memcached servers, and you need to flush all content,
@@ -431,9 +447,10 @@ In response, the server sends
 &quot;VERSION &lt;version&gt;\r\n&quot;, where &lt;version&gt; is the version string for the
 server.
 
-&quot;verbosity&quot; is a command with a numeric argument. It always                                                  
-succeeds, and the server sends &quot;OK\r\n&quot; in response. Its effect is to                                        
-set the verbosity level of the logging output.                                                               
+&quot;verbosity&quot; is a command with a numeric argument. It always succeeds,
+and the server sends &quot;OK\r\n&quot; in response (unless &quot;noreply&quot; is given
+as the last parameter). Its effect is to set the verbosity level of
+the logging output.
 
 &quot;quit&quot; is a command with no arguments:
 </diff>
      <filename>doc/protocol.txt</filename>
    </modified>
    <modified>
      <diff>@@ -372,6 +372,8 @@ conn *conn_new(const int sfd, const int init_state, const int event_flags,
     c-&gt;bucket = -1;
     c-&gt;gen = 0;
 
+    c-&gt;noreply = false;
+
     event_set(&amp;c-&gt;event, sfd, event_flags, event_handler, (void *)c);
     event_base_set(base, &amp;c-&gt;event);
     c-&gt;ev_flags = event_flags;
@@ -733,6 +735,12 @@ static void out_string(conn *c, const char *str) {
 
     assert(c != NULL);
 
+    if (c-&gt;noreply) {
+        c-&gt;noreply = false;
+        conn_set_state(c, conn_read);
+        return;
+    }
+
     if (settings.verbose &gt; 1)
         fprintf(stderr, &quot;&gt;%d %s\n&quot;, c-&gt;sfd, str);
 
@@ -898,7 +906,7 @@ typedef struct token_s {
 #define KEY_TOKEN 1
 #define KEY_MAX_LENGTH 250
 
-#define MAX_TOKENS 7
+#define MAX_TOKENS 8
 
 /*
  * Tokenize the command string by replacing whitespace with '\0' and update
@@ -968,6 +976,23 @@ static void write_and_free(conn *c, char *buf, int bytes) {
     }
 }
 
+static inline void set_noreply_maybe(conn *c, token_t *tokens, size_t ntokens)
+{
+    int noreply_index = ntokens - 2;
+
+    /*
+      NOTE: this function is not the first place where we are going to
+      send the reply.  We could send it instead from process_command()
+      if the request line has wrong number of tokens.  However parsing
+      malformed line for &quot;noreply&quot; option is not reliable anyway, so
+      it can't be helped.
+    */
+    if (tokens[noreply_index].value
+        &amp;&amp; strcmp(tokens[noreply_index].value, &quot;noreply&quot;) == 0) {
+        c-&gt;noreply = true;
+    }
+}
+
 inline static void process_stats_detail(conn *c, const char *command) {
     assert(c != NULL);
 
@@ -1347,6 +1372,8 @@ static void process_update_command(conn *c, token_t *tokens, const size_t ntoken
 
     assert(c != NULL);
 
+    set_noreply_maybe(c, tokens, ntokens);
+
     if (tokens[KEY_TOKEN].length &gt; KEY_MAX_LENGTH) {
         out_string(c, &quot;CLIENT_ERROR bad command line format&quot;);
         return;
@@ -1418,6 +1445,8 @@ static void process_arithmetic_command(conn *c, token_t *tokens, const size_t nt
 
     assert(c != NULL);
 
+    set_noreply_maybe(c, tokens, ntokens);
+
     if(tokens[KEY_TOKEN].length &gt; KEY_MAX_LENGTH) {
         out_string(c, &quot;CLIENT_ERROR bad command line format&quot;);
         return;
@@ -1514,6 +1543,8 @@ static void process_delete_command(conn *c, token_t *tokens, const size_t ntoken
 
     assert(c != NULL);
 
+    set_noreply_maybe(c, tokens, ntokens);
+
     if (settings.managed) {
         int bucket = c-&gt;bucket;
         if (bucket == -1) {
@@ -1535,7 +1566,7 @@ static void process_delete_command(conn *c, token_t *tokens, const size_t ntoken
         return;
     }
 
-    if(ntokens == 4) {
+    if(ntokens == (c-&gt;noreply ? 5 : 4)) {
         exptime = strtol(tokens[2].value, NULL, 10);
 
         if(errno == ERANGE) {
@@ -1598,6 +1629,8 @@ static void process_verbosity_command(conn *c, token_t *tokens, const size_t nto
 
     assert(c != NULL);
 
+    set_noreply_maybe(c, tokens, ntokens);
+
     level = strtoul(tokens[1].value, NULL, 10);
     settings.verbose = level &gt; MAX_VERBOSITY_LEVEL ? MAX_VERBOSITY_LEVEL : level;
     out_string(c, &quot;OK&quot;);
@@ -1635,7 +1668,7 @@ static void process_command(conn *c, char *command) {
 
         process_get_command(c, tokens, ntokens, false);
 
-    } else if (ntokens == 6 &amp;&amp;
+    } else if ((ntokens == 6 || ntokens == 7) &amp;&amp;
                ((strcmp(tokens[COMMAND_TOKEN].value, &quot;add&quot;) == 0 &amp;&amp; (comm = NREAD_ADD)) ||
                 (strcmp(tokens[COMMAND_TOKEN].value, &quot;set&quot;) == 0 &amp;&amp; (comm = NREAD_SET)) ||
                 (strcmp(tokens[COMMAND_TOKEN].value, &quot;replace&quot;) == 0 &amp;&amp; (comm = NREAD_REPLACE)) ||
@@ -1644,11 +1677,11 @@ static void process_command(conn *c, char *command) {
 
         process_update_command(c, tokens, ntokens, comm, false);
 
-    } else if (ntokens == 7 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;cas&quot;) == 0 &amp;&amp; (comm = NREAD_CAS))) {
+    } else if ((ntokens == 7 || ntokens == 8) &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;cas&quot;) == 0 &amp;&amp; (comm = NREAD_CAS))) {
 
         process_update_command(c, tokens, ntokens, comm, true);
 
-    } else if (ntokens == 4 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;incr&quot;) == 0)) {
+    } else if ((ntokens == 4 || ntokens == 5) &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;incr&quot;) == 0)) {
 
         process_arithmetic_command(c, tokens, ntokens, 1);
 
@@ -1656,11 +1689,11 @@ static void process_command(conn *c, char *command) {
 
         process_get_command(c, tokens, ntokens, true);
 
-    } else if (ntokens == 4 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;decr&quot;) == 0)) {
+    } else if ((ntokens == 4 || ntokens == 5) &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;decr&quot;) == 0)) {
 
         process_arithmetic_command(c, tokens, ntokens, 0);
 
-    } else if (ntokens &gt;= 3 &amp;&amp; ntokens &lt;= 4 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;delete&quot;) == 0)) {
+    } else if (ntokens &gt;= 3 &amp;&amp; ntokens &lt;= 5 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;delete&quot;) == 0)) {
 
         process_delete_command(c, tokens, ntokens);
 
@@ -1729,11 +1762,13 @@ static void process_command(conn *c, char *command) {
 
         process_stat(c, tokens, ntokens);
 
-    } else if (ntokens &gt;= 2 &amp;&amp; ntokens &lt;= 3 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;flush_all&quot;) == 0)) {
+    } else if (ntokens &gt;= 2 &amp;&amp; ntokens &lt;= 4 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;flush_all&quot;) == 0)) {
         time_t exptime = 0;
         set_current_time();
 
-        if(ntokens == 2) {
+        set_noreply_maybe(c, tokens, ntokens);
+
+        if(ntokens == (c-&gt;noreply ? 3 : 2)) {
             settings.oldest_live = current_time - 1;
             item_flush_expired();
             out_string(c, &quot;OK&quot;);
@@ -1798,7 +1833,7 @@ static void process_command(conn *c, char *command) {
 #else
         out_string(c, &quot;CLIENT_ERROR Slab reassignment not supported&quot;);
 #endif
-    } else if (ntokens == 3 &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;verbosity&quot;) == 0)) {
+    } else if ((ntokens == 3 || ntokens == 4) &amp;&amp; (strcmp(tokens[COMMAND_TOKEN].value, &quot;verbosity&quot;) == 0)) {
         process_verbosity_command(c, tokens, ntokens);
     } else {
         out_string(c, &quot;ERROR&quot;);</diff>
      <filename>memcached.c</filename>
    </modified>
    <modified>
      <diff>@@ -220,6 +220,7 @@ struct conn {
     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 */
+    bool   noreply;   /* True if the reply should not be sent. */
     conn   *next;     /* Used for generating a list of conn structures */
 };
 </diff>
      <filename>memcached.h</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>57e3367ce4ba3f1bfd562ddf87c270c51a19bfa9</id>
    </parent>
  </parents>
  <author>
    <name>Tomash Brechko</name>
    <email>tomash.brechko@gmail.com</email>
  </author>
  <url>http://github.com/tmaesaka/memcached/commit/d9ece78091f1dc15332b16131258fb25d9ba3224</url>
  <id>d9ece78091f1dc15332b16131258fb25d9ba3224</id>
  <committed-date>2008-02-21T20:57:41-08:00</committed-date>
  <authored-date>2008-02-21T20:57:41-08:00</authored-date>
  <message>Implement 'noreply' option for update commands. (Tomash Brechko &lt;tomash.brechko@gmail.com&gt;)

Commands add, set, replace, append, prepend, cas, delete, incr, decr,
flush_all, verbosity can take last optional parameter, 'noreply',
which instructs the server to not send the reply.

Add benchmark script for noreply parameter.


git-svn-id: http://code.sixapart.com/svn/memcached/trunk/server@708 b0b603af-a30f-0410-a34e-baf09ae79d0b</message>
  <tree>850b51c15d6d5b00493f850bfd37e6666e186865</tree>
  <committer>
    <name>Tomash Brechko</name>
    <email>tomash.brechko@gmail.com</email>
  </committer>
</commit>
