Merge lp://staging/~trond-norbye/libmemcached/bugparade into lp://staging/~tangent-org/libmemcached/trunk

Proposed by Trond Norbye
Status: Merged
Merged at revision: not available
Proposed branch: lp://staging/~trond-norbye/libmemcached/bugparade
Merge into: lp://staging/~tangent-org/libmemcached/trunk
Diff against target: 1146 lines
7 files modified
clients/memcapable.c (+714/-41)
libmemcached/memcached_delete.c (+1/-1)
libmemcached/memcached_get.c (+5/-2)
libmemcached/memcached_purge.c (+1/-1)
libmemcached/memcached_response.c (+7/-1)
libmemcached/memcached_stats.c (+2/-2)
tests/function.c (+154/-0)
To merge this branch: bzr merge lp://staging/~trond-norbye/libmemcached/bugparade
Reviewer Review Type Date Requested Status
Brian Aker Needs Fixing
Review via email: mp+12895@code.staging.launchpad.net
To post a comment you must log in.
Revision history for this message
Trond Norbye (trond-norbye) wrote :

 596. By Trond Norbye 11 minutes ago

    Create workaround for warnings generated by a broken C99 compiler

    Some of the flags to turn on extra analysis and warnings in gcc contains
    various bugs related to struct initializations (see section 6.7.8 in C99)
    causing bogus warnings to be generated. Due to the fact that we compile
    with warning == error, this is a showstopper for us. We cannot expect all
    users to be running the latest compilers, so we have to create the workaround
    in our code.

595. By Trond Norbye 1 hour ago

    Fix return type from test functions (should be TEST_SUCCESS and not 0)

594. By Trond Norbye 1 hour ago

    Strip trailing whitespaces

593. By Trond Norbye 1 hour ago

    Update protocol due to review comments:

    * Typedef the structs in the public interface
    * Removed the EVENT enum, and replaced it with a bitmask of it's own type
    * Added support for PAUSE events in the _binary_ protocol. ASCII is on the
      todo list :-)

592. By Trond Norbye 3 hours ago

    Move libmemcachedutil to libmemcached/util where it belongs

591. By Trond Norbye 4 hours ago

    Fix compilation failure on 32 bit systems caused by macro redefinition

597. By Trond Norbye <tn202803@tor01>

Bug #434843: Large multigets with binary protocol may hang client

598. By Trond Norbye

Bug 421108: memstat reports same value for bytes, bytes_read and bytes_written

Revision history for this message
Brian Aker (brianaker) wrote :

Build failure on gaz. Your const int does not compare directly to counter which is unsigned.

review: Needs Fixing
599. By Trond Norbye

Fix compilation warnings reported by gcc

600. By Trond Norbye

Initial support for the ASCII protocol in memcapable

601. By Trond Norbye

Flush does not reset the bytes stat, so we have no idea of the value

602. By Trond Norbye <tn202803@tor01>

Merge Eric

603. By Trond Norbye <tn202803@tor01>

Bug #442914: 'delete noreply' may hang the client

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'clients/memcapable.c'
2--- clients/memcapable.c 2009-09-28 22:25:10 +0000
3+++ clients/memcapable.c 2009-10-08 14:12:13 +0000
4@@ -19,6 +19,7 @@
5 #include <stdbool.h>
6 #include <unistd.h>
7 #include <poll.h>
8+#include <ctype.h>
9
10 #include <libmemcached/memcached/protocol_binary.h>
11 #include <libmemcached/byteorder.h>
12@@ -168,7 +169,7 @@
13 ret= read(fd, buf, len);
14
15 if (ret == -1 && errno == EWOULDBLOCK) {
16- struct pollfd fds = {
17+ struct pollfd fds= {
18 .events= direction,
19 .fd= fd
20 };
21@@ -183,7 +184,7 @@
22 }
23 else if (err == 0)
24 {
25- errno = ETIMEDOUT;
26+ errno= ETIMEDOUT;
27 }
28 else
29 {
30@@ -206,7 +207,7 @@
31 if (!val)
32 {
33 if (verbose)
34- fprintf(stderr, "%s:%u: %s\n", file, line, expression);
35+ fprintf(stderr, "\n%s:%u: %s", file, line, expression);
36
37 if (do_core)
38 abort();
39@@ -639,7 +640,7 @@
40 cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SET;
41 execute(resend_packet(&cmd));
42 execute(recv_packet(&rsp));
43- verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
44+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
45 PROTOCOL_BINARY_RESPONSE_SUCCESS));
46 cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ;
47 }
48@@ -720,7 +721,7 @@
49 return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
50 }
51
52-static enum test_return set_item(const char *key, const char *value)
53+static enum test_return binary_set_item(const char *key, const char *value)
54 {
55 command cmd;
56 response rsp;
57@@ -761,7 +762,7 @@
58 verify(validate_response_header(&rsp, cc, expected_result));
59
60 if (ii == 0)
61- execute(set_item(key, key));
62+ execute(binary_set_item(key, key));
63 }
64 else
65 execute(test_binary_noop());
66@@ -810,7 +811,7 @@
67 execute(send_packet(&cmd));
68 execute(recv_packet(&rsp));
69 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
70- execute(set_item(key, key));
71+ execute(binary_set_item(key, key));
72
73 /* The item should be present now, resend*/
74 execute(resend_packet(&cmd));
75@@ -851,7 +852,7 @@
76 else
77 execute(test_binary_noop());
78
79- execute(set_item(key, key));
80+ execute(binary_set_item(key, key));
81 execute(resend_packet(&cmd));
82 execute(recv_packet(&rsp));
83 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
84@@ -993,7 +994,7 @@
85
86 for (int ii= 0; ii < 2; ++ii)
87 {
88- execute(set_item(key, key));
89+ execute(binary_set_item(key, key));
90 flush_command(&cmd, cc, 0, ii == 0);
91 execute(send_packet(&cmd));
92
93@@ -1036,7 +1037,7 @@
94 else
95 value=" world";
96
97- execute(set_item(key, value));
98+ execute(binary_set_item(key, value));
99
100 if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
101 value=" world";
102@@ -1117,7 +1118,650 @@
103 ++cc;
104 }
105
106- return TEST_PASS;
107+ return TEST_PASS_RECONNECT;
108+}
109+
110+static enum test_return send_string(const char *cmd)
111+{
112+ execute(retry_write(cmd, strlen(cmd)));
113+ return TEST_PASS;
114+}
115+
116+static enum test_return receive_line(char *buffer, size_t size)
117+{
118+ size_t offset= 0;
119+ while (offset < size)
120+ {
121+ execute(retry_read(buffer + offset, 1));
122+ if (buffer[offset] == '\n')
123+ {
124+ if (offset + 1 < size)
125+ {
126+ buffer[offset + 1]= '\0';
127+ return TEST_PASS;
128+ }
129+ else
130+ return TEST_FAIL;
131+ }
132+ ++offset;
133+ }
134+
135+ return TEST_FAIL;
136+}
137+
138+static enum test_return receive_response(const char *msg) {
139+ char buffer[80];
140+ execute(receive_line(buffer, sizeof(buffer)));
141+ verify(strcmp(msg, buffer) == 0);
142+ return TEST_PASS;
143+}
144+
145+static enum test_return test_ascii_quit(void)
146+{
147+ /* Verify that quit handles unknown options */
148+ execute(send_string("quit foo bar\r\n"));
149+ execute(receive_response("ERROR\r\n"));
150+
151+ /* quit doesn't support noreply */
152+ execute(send_string("quit noreply\r\n"));
153+ execute(receive_response("ERROR\r\n"));
154+
155+ /* Verify that quit works */
156+ execute(send_string("quit\r\n"));
157+
158+ /* Socket should be closed now, read should return 0 */
159+ char buffer[80];
160+ verify(timeout_io_op(sock, POLLIN, buffer, sizeof(buffer)) == 0);
161+ return TEST_PASS_RECONNECT;
162+
163+}
164+
165+static enum test_return test_ascii_version(void)
166+{
167+ /* Verify that version command handles unknown options */
168+ execute(send_string("version foo bar\r\n"));
169+ execute(receive_response("ERROR\r\n"));
170+
171+ /* version doesn't support noreply */
172+ execute(send_string("version noreply\r\n"));
173+ execute(receive_response("ERROR\r\n"));
174+
175+ /* Verify that verify works */
176+ execute(send_string("version\r\n"));
177+ char buffer[256];
178+ execute(receive_line(buffer, sizeof(buffer)));
179+ verify(strncmp(buffer, "VERSION ", 8) == 0);
180+
181+ return TEST_PASS;
182+}
183+
184+static enum test_return test_ascii_verbosity(void)
185+{
186+ /* This command does not adhere to the spec! */
187+ execute(send_string("verbosity foo bar my\r\n"));
188+ execute(receive_response("ERROR\r\n"));
189+
190+ execute(send_string("verbosity noreply\r\n"));
191+ execute(test_ascii_version());
192+
193+ execute(send_string("verbosity 0 noreply\r\n"));
194+ execute(test_ascii_version());
195+
196+ execute(send_string("verbosity\r\n"));
197+ execute(receive_response("ERROR\r\n"));
198+
199+ execute(send_string("verbosity 1\r\n"));
200+ execute(receive_response("OK\r\n"));
201+
202+ execute(send_string("verbosity 0\r\n"));
203+ execute(receive_response("OK\r\n"));
204+
205+ return TEST_PASS;
206+}
207+
208+
209+
210+static enum test_return test_ascii_set_impl(const char* key, bool noreply)
211+{
212+ /* @todo add tests for bogus format! */
213+ char buffer[1024];
214+ sprintf(buffer, "set %s 0 0 5%s\r\nvalue\r\n", key,
215+ noreply ? " noreply" : "");
216+ execute(send_string(buffer));
217+
218+ if (!noreply)
219+ execute(receive_response("STORED\r\n"));
220+
221+ return test_ascii_version();
222+}
223+
224+static enum test_return test_ascii_set(void)
225+{
226+ return test_ascii_set_impl("test_ascii_set", false);
227+}
228+
229+static enum test_return test_ascii_set_noreply(void)
230+{
231+ return test_ascii_set_impl("test_ascii_set_noreply", true);
232+}
233+
234+static enum test_return test_ascii_add_impl(const char* key, bool noreply)
235+{
236+ /* @todo add tests for bogus format! */
237+ char buffer[1024];
238+ sprintf(buffer, "add %s 0 0 5%s\r\nvalue\r\n", key,
239+ noreply ? " noreply" : "");
240+ execute(send_string(buffer));
241+
242+ if (!noreply)
243+ execute(receive_response("STORED\r\n"));
244+
245+ execute(send_string(buffer));
246+
247+ if (!noreply)
248+ execute(receive_response("NOT_STORED\r\n"));
249+
250+ return test_ascii_version();
251+}
252+
253+static enum test_return test_ascii_add(void)
254+{
255+ return test_ascii_add_impl("test_ascii_add", false);
256+}
257+
258+static enum test_return test_ascii_add_noreply(void)
259+{
260+ return test_ascii_add_impl("test_ascii_add_noreply", true);
261+}
262+
263+static enum test_return ascii_get_value(const char *key, const char *value)
264+{
265+
266+ char buffer[1024];
267+ size_t datasize= strlen(value);
268+
269+ verify(datasize < sizeof(buffer));
270+ execute(receive_line(buffer, sizeof(buffer)));
271+ verify(strncmp(buffer, "VALUE ", 6) == 0);
272+ verify(strncmp(buffer + 6, key, strlen(key)) == 0);
273+ char *ptr= buffer + 6 + strlen(key) + 1;
274+ char *end;
275+
276+ unsigned long val= strtoul(ptr, &end, 10); /* flags */
277+ verify(ptr != end);
278+ verify(val == 0);
279+ verify(end != NULL);
280+ val= strtoul(end, &end, 10); /* size */
281+ verify(ptr != end);
282+ verify(val == datasize);
283+ verify(end != NULL);
284+ while (*end != '\n' && isspace(*end))
285+ ++end;
286+ verify(*end == '\n');
287+
288+ execute(retry_read(buffer, datasize));
289+ verify(memcmp(buffer, value, datasize) == 0);
290+
291+ execute(retry_read(buffer, 2));
292+ verify(memcmp(buffer, "\r\n", 2) == 0);
293+
294+ return TEST_PASS;
295+}
296+
297+static enum test_return ascii_get_item(const char *key, const char *value,
298+ bool exist)
299+{
300+ char buffer[1024];
301+ size_t datasize= 0;
302+ if (value != NULL)
303+ datasize= strlen(value);
304+
305+ verify(datasize < sizeof(buffer));
306+ sprintf(buffer, "get %s\r\n", key);
307+ execute(send_string(buffer));
308+
309+ if (exist)
310+ execute(ascii_get_value(key, value));
311+
312+ execute(retry_read(buffer, 5));
313+ verify(memcmp(buffer, "END\r\n", 5) == 0);
314+
315+ return TEST_PASS;
316+}
317+
318+static enum test_return ascii_gets_value(const char *key, const char *value,
319+ unsigned long *cas)
320+{
321+
322+ char buffer[1024];
323+ size_t datasize= strlen(value);
324+
325+ verify(datasize < sizeof(buffer));
326+ execute(receive_line(buffer, sizeof(buffer)));
327+ verify(strncmp(buffer, "VALUE ", 6) == 0);
328+ verify(strncmp(buffer + 6, key, strlen(key)) == 0);
329+ char *ptr= buffer + 6 + strlen(key) + 1;
330+ char *end;
331+
332+ unsigned long val= strtoul(ptr, &end, 10); /* flags */
333+ verify(ptr != end);
334+ verify(val == 0);
335+ verify(end != NULL);
336+ val= strtoul(end, &end, 10); /* size */
337+ verify(ptr != end);
338+ verify(val == datasize);
339+ verify(end != NULL);
340+ *cas= strtoul(end, &end, 10); /* cas */
341+ verify(ptr != end);
342+ verify(val == datasize);
343+ verify(end != NULL);
344+
345+ while (*end != '\n' && isspace(*end))
346+ ++end;
347+ verify(*end == '\n');
348+
349+ execute(retry_read(buffer, datasize));
350+ verify(memcmp(buffer, value, datasize) == 0);
351+
352+ execute(retry_read(buffer, 2));
353+ verify(memcmp(buffer, "\r\n", 2) == 0);
354+
355+ return TEST_PASS;
356+}
357+
358+static enum test_return ascii_gets_item(const char *key, const char *value,
359+ bool exist, unsigned long *cas)
360+{
361+ char buffer[1024];
362+ size_t datasize= 0;
363+ if (value != NULL)
364+ datasize= strlen(value);
365+
366+ verify(datasize < sizeof(buffer));
367+ sprintf(buffer, "gets %s\r\n", key);
368+ execute(send_string(buffer));
369+
370+ if (exist)
371+ execute(ascii_gets_value(key, value, cas));
372+
373+ execute(retry_read(buffer, 5));
374+ verify(memcmp(buffer, "END\r\n", 5) == 0);
375+
376+ return TEST_PASS;
377+}
378+
379+static enum test_return ascii_set_item(const char *key, const char *value)
380+{
381+ char buffer[300];
382+ size_t len= strlen(value);
383+ sprintf(buffer, "set %s 0 0 %u\r\n", key, (unsigned int)len);
384+ execute(send_string(buffer));
385+ execute(retry_write(value, len));
386+ execute(send_string("\r\n"));
387+ execute(receive_response("STORED\r\n"));
388+ return TEST_PASS;
389+}
390+
391+static enum test_return test_ascii_replace_impl(const char* key, bool noreply)
392+{
393+ char buffer[1024];
394+ sprintf(buffer, "replace %s 0 0 5%s\r\nvalue\r\n", key,
395+ noreply ? " noreply" : "");
396+ execute(send_string(buffer));
397+
398+ if (noreply)
399+ execute(test_ascii_version());
400+ else
401+ execute(receive_response("NOT_STORED\r\n"));
402+
403+ execute(ascii_set_item(key, "value"));
404+ execute(ascii_get_item(key, "value", true));
405+
406+
407+ execute(send_string(buffer));
408+
409+ if (noreply)
410+ execute(test_ascii_version());
411+ else
412+ execute(receive_response("STORED\r\n"));
413+
414+ return test_ascii_version();
415+}
416+
417+static enum test_return test_ascii_replace(void)
418+{
419+ return test_ascii_replace_impl("test_ascii_replace", false);
420+}
421+
422+static enum test_return test_ascii_replace_noreply(void)
423+{
424+ return test_ascii_replace_impl("test_ascii_replace_noreply", true);
425+}
426+
427+static enum test_return test_ascii_cas_impl(const char* key, bool noreply)
428+{
429+ char buffer[1024];
430+ unsigned long cas;
431+
432+ execute(ascii_set_item(key, "value"));
433+ execute(ascii_gets_item(key, "value", true, &cas));
434+
435+ sprintf(buffer, "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas,
436+ noreply ? " noreply" : "");
437+ execute(send_string(buffer));
438+
439+ if (noreply)
440+ execute(test_ascii_version());
441+ else
442+ execute(receive_response("STORED\r\n"));
443+
444+ /* reexecute the same command should fail due to illegal cas */
445+ execute(send_string(buffer));
446+
447+ if (noreply)
448+ execute(test_ascii_version());
449+ else
450+ execute(receive_response("EXISTS\r\n"));
451+
452+ return test_ascii_version();
453+}
454+
455+static enum test_return test_ascii_cas(void)
456+{
457+ return test_ascii_cas_impl("test_ascii_cas", false);
458+}
459+
460+static enum test_return test_ascii_cas_noreply(void)
461+{
462+ return test_ascii_cas_impl("test_ascii_cas_noreply", true);
463+}
464+
465+static enum test_return test_ascii_delete_impl(const char *key, bool noreply)
466+{
467+ execute(ascii_set_item(key, "value"));
468+
469+ execute(send_string("delete\r\n"));
470+ execute(receive_response("ERROR\r\n"));
471+ /* BUG: the server accepts delete a b */
472+ execute(send_string("delete a b c d e\r\n"));
473+ execute(receive_response("ERROR\r\n"));
474+
475+ char buffer[1024];
476+ sprintf(buffer, "delete %s%s\r\n", key, noreply ? " noreply" : "");
477+ execute(send_string(buffer));
478+
479+ if (noreply)
480+ execute(test_ascii_version());
481+ else
482+ execute(receive_response("DELETED\r\n"));
483+
484+ execute(ascii_get_item(key, "value", false));
485+ execute(send_string(buffer));
486+ if (noreply)
487+ execute(test_ascii_version());
488+ else
489+ execute(receive_response("NOT_FOUND\r\n"));
490+
491+ return TEST_PASS;
492+}
493+
494+static enum test_return test_ascii_delete(void)
495+{
496+ return test_ascii_delete_impl("test_ascii_delete", false);
497+}
498+
499+static enum test_return test_ascii_delete_noreply(void)
500+{
501+ return test_ascii_delete_impl("test_ascii_delete_noreply", true);
502+}
503+
504+static enum test_return test_ascii_get(void)
505+{
506+ execute(ascii_set_item("test_ascii_get", "value"));
507+
508+ execute(send_string("get\r\n"));
509+ execute(receive_response("ERROR\r\n"));
510+ execute(ascii_get_item("test_ascii_get", "value", true));
511+ execute(ascii_get_item("test_ascii_get_notfound", "value", false));
512+
513+ return TEST_PASS;
514+}
515+
516+static enum test_return test_ascii_gets(void)
517+{
518+ execute(ascii_set_item("test_ascii_gets", "value"));
519+
520+ execute(send_string("gets\r\n"));
521+ execute(receive_response("ERROR\r\n"));
522+ unsigned long cas;
523+ execute(ascii_gets_item("test_ascii_gets", "value", true, &cas));
524+ execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas));
525+
526+ return TEST_PASS;
527+}
528+
529+static enum test_return test_ascii_mget(void)
530+{
531+ execute(ascii_set_item("test_ascii_mget1", "value"));
532+ execute(ascii_set_item("test_ascii_mget2", "value"));
533+ execute(ascii_set_item("test_ascii_mget3", "value"));
534+ execute(ascii_set_item("test_ascii_mget4", "value"));
535+ execute(ascii_set_item("test_ascii_mget5", "value"));
536+
537+ execute(send_string("get test_ascii_mget1 test_ascii_mget2 test_ascii_mget3 "
538+ "test_ascii_mget4 test_ascii_mget5 "
539+ "test_ascii_mget6\r\n"));
540+ execute(ascii_get_value("test_ascii_mget1", "value"));
541+ execute(ascii_get_value("test_ascii_mget2", "value"));
542+ execute(ascii_get_value("test_ascii_mget3", "value"));
543+ execute(ascii_get_value("test_ascii_mget4", "value"));
544+ execute(ascii_get_value("test_ascii_mget5", "value"));
545+
546+ char buffer[5];
547+ execute(retry_read(buffer, 5));
548+ verify(memcmp(buffer, "END\r\n", 5) == 0);
549+ return TEST_PASS;
550+}
551+
552+static enum test_return test_ascii_incr_impl(const char* key, bool noreply)
553+{
554+ char cmd[300];
555+ sprintf(cmd, "incr %s 1%s\r\n", key, noreply ? " noreply" : "");
556+
557+ execute(ascii_set_item(key, "0"));
558+ for (int x= 1; x < 11; ++x)
559+ {
560+ execute(send_string(cmd));
561+
562+ if (noreply)
563+ execute(test_ascii_version());
564+ else
565+ {
566+ char buffer[80];
567+ execute(receive_line(buffer, sizeof(buffer)));
568+ int val= atoi(buffer);
569+ verify(val == x);
570+ }
571+ }
572+
573+ execute(ascii_get_item(key, "10", true));
574+
575+ return TEST_PASS;
576+}
577+
578+static enum test_return test_ascii_incr(void)
579+{
580+ return test_ascii_incr_impl("test_ascii_incr", false);
581+}
582+
583+static enum test_return test_ascii_incr_noreply(void)
584+{
585+ return test_ascii_incr_impl("test_ascii_incr_noreply", true);
586+}
587+
588+static enum test_return test_ascii_decr_impl(const char* key, bool noreply)
589+{
590+ char cmd[300];
591+ sprintf(cmd, "decr %s 1%s\r\n", key, noreply ? " noreply" : "");
592+
593+ execute(ascii_set_item(key, "9"));
594+ for (int x= 8; x > -1; --x)
595+ {
596+ execute(send_string(cmd));
597+
598+ if (noreply)
599+ execute(test_ascii_version());
600+ else
601+ {
602+ char buffer[80];
603+ execute(receive_line(buffer, sizeof(buffer)));
604+ int val= atoi(buffer);
605+ verify(val == x);
606+ }
607+ }
608+
609+ execute(ascii_get_item(key, "0", true));
610+
611+ /* verify that it doesn't wrap */
612+ execute(send_string(cmd));
613+ if (noreply)
614+ execute(test_ascii_version());
615+ else
616+ {
617+ char buffer[80];
618+ execute(receive_line(buffer, sizeof(buffer)));
619+ }
620+ execute(ascii_get_item(key, "0", true));
621+
622+ return TEST_PASS;
623+}
624+
625+static enum test_return test_ascii_decr(void)
626+{
627+ return test_ascii_decr_impl("test_ascii_decr", false);
628+}
629+
630+static enum test_return test_ascii_decr_noreply(void)
631+{
632+ return test_ascii_decr_impl("test_ascii_decr_noreply", true);
633+}
634+
635+
636+static enum test_return test_ascii_flush_impl(const char *key, bool noreply)
637+{
638+#if 0
639+ /* Verify that the flush_all command handles unknown options */
640+ /* Bug in the current memcached server! */
641+ execute(send_string("flush_all foo bar\r\n"));
642+ execute(receive_response("ERROR\r\n"));
643+#endif
644+
645+ execute(ascii_set_item(key, key));
646+ execute(ascii_get_item(key, key, true));
647+
648+ if (noreply)
649+ {
650+ execute(send_string("flush_all noreply\r\n"));
651+ execute(test_ascii_version());
652+ }
653+ else
654+ {
655+ execute(send_string("flush_all\r\n"));
656+ execute(receive_response("OK\r\n"));
657+ }
658+
659+ execute(ascii_get_item(key, key, false));
660+
661+ return TEST_PASS;
662+}
663+
664+static enum test_return test_ascii_flush(void)
665+{
666+ return test_ascii_flush_impl("test_ascii_flush", false);
667+}
668+
669+static enum test_return test_ascii_flush_noreply(void)
670+{
671+ return test_ascii_flush_impl("test_ascii_flush_noreply", true);
672+}
673+
674+static enum test_return test_ascii_concat_impl(const char *key,
675+ bool append,
676+ bool noreply)
677+{
678+ const char *value;
679+
680+ if (append)
681+ value="hello";
682+ else
683+ value=" world";
684+
685+ execute(ascii_set_item(key, value));
686+
687+ if (append)
688+ value=" world";
689+ else
690+ value="hello";
691+
692+ char cmd[400];
693+ sprintf(cmd, "%s %s 0 0 %u%s\r\n%s\r\n",
694+ append ? "append" : "prepend",
695+ key, (unsigned int)strlen(value), noreply ? " noreply" : "",
696+ value);
697+ execute(send_string(cmd));
698+
699+ if (noreply)
700+ execute(test_ascii_version());
701+ else
702+ execute(receive_response("STORED\r\n"));
703+
704+ execute(ascii_get_item(key, "hello world", true));
705+
706+ sprintf(cmd, "%s %s_notfound 0 0 %u%s\r\n%s\r\n",
707+ append ? "append" : "prepend",
708+ key, (unsigned int)strlen(value), noreply ? " noreply" : "",
709+ value);
710+ execute(send_string(cmd));
711+
712+ if (noreply)
713+ execute(test_ascii_version());
714+ else
715+ execute(receive_response("NOT_STORED\r\n"));
716+
717+ return TEST_PASS;
718+}
719+
720+static enum test_return test_ascii_append(void)
721+{
722+ return test_ascii_concat_impl("test_ascii_append", true, false);
723+}
724+
725+static enum test_return test_ascii_prepend(void)
726+{
727+ return test_ascii_concat_impl("test_ascii_prepend", false, false);
728+}
729+
730+static enum test_return test_ascii_append_noreply(void)
731+{
732+ return test_ascii_concat_impl("test_ascii_append_noreply", true, true);
733+}
734+
735+static enum test_return test_ascii_prepend_noreply(void)
736+{
737+ return test_ascii_concat_impl("test_ascii_prepend_noreply", false, true);
738+}
739+
740+static enum test_return test_ascii_stat(void)
741+{
742+ execute(send_string("stats noreply\r\n"));
743+ execute(receive_response("ERROR\r\n"));
744+ execute(send_string("stats\r\n"));
745+ char buffer[1024];
746+ do {
747+ execute(receive_line(buffer, sizeof(buffer)));
748+ } while (strcmp(buffer, "END\r\n") != 0);
749+
750+ return TEST_PASS_RECONNECT;
751 }
752
753 typedef enum test_return(*TEST_FUNC)(void);
754@@ -1129,34 +1773,61 @@
755 };
756
757 struct testcase testcases[]= {
758- { "noop", test_binary_noop},
759- { "quit", test_binary_quit},
760- { "quitq", test_binary_quitq},
761- { "set", test_binary_set},
762- { "setq", test_binary_setq},
763- { "flush", test_binary_flush},
764- { "flushq", test_binary_flushq},
765- { "add", test_binary_add},
766- { "addq", test_binary_addq},
767- { "replace", test_binary_replace},
768- { "replaceq", test_binary_replaceq},
769- { "delete", test_binary_delete},
770- { "deleteq", test_binary_deleteq},
771- { "get", test_binary_get},
772- { "getq", test_binary_getq},
773- { "getk", test_binary_getk},
774- { "getkq", test_binary_getkq},
775- { "incr", test_binary_incr},
776- { "incrq", test_binary_incrq},
777- { "decr", test_binary_decr},
778- { "decrq", test_binary_decrq},
779- { "version", test_binary_version},
780- { "append", test_binary_append},
781- { "appendq", test_binary_appendq},
782- { "prepend", test_binary_prepend},
783- { "prependq", test_binary_prependq},
784- { "stat", test_binary_stat},
785- { "illegal", test_binary_illegal},
786+ { "ascii quit", test_ascii_quit },
787+ { "ascii version", test_ascii_version },
788+ { "ascii verbosity", test_ascii_verbosity },
789+ { "ascii set", test_ascii_set },
790+ { "ascii set noreply", test_ascii_set_noreply },
791+ { "ascii get", test_ascii_get },
792+ { "ascii gets", test_ascii_gets },
793+ { "ascii mget", test_ascii_mget },
794+ { "ascii flush", test_ascii_flush },
795+ { "ascii flush noreply", test_ascii_flush_noreply },
796+ { "ascii add", test_ascii_add },
797+ { "ascii add noreply", test_ascii_add_noreply },
798+ { "ascii replace", test_ascii_replace },
799+ { "ascii replace noreply", test_ascii_replace_noreply },
800+ { "ascii cas", test_ascii_cas },
801+ { "ascii cas noreply", test_ascii_cas_noreply },
802+ { "ascii delete", test_ascii_delete },
803+ { "ascii delete noreply", test_ascii_delete_noreply },
804+ { "ascii incr", test_ascii_incr },
805+ { "ascii incr noreply", test_ascii_incr_noreply },
806+ { "ascii decr", test_ascii_decr },
807+ { "ascii decr noreply", test_ascii_decr_noreply },
808+ { "ascii append", test_ascii_append },
809+ { "ascii append noreply", test_ascii_append_noreply },
810+ { "ascii prepend", test_ascii_prepend },
811+ { "ascii prepend noreply", test_ascii_prepend_noreply },
812+ { "ascii stat", test_ascii_stat },
813+ { "binary noop", test_binary_noop },
814+ { "binary quit", test_binary_quit },
815+ { "binary quitq", test_binary_quitq },
816+ { "binary set", test_binary_set },
817+ { "binary setq", test_binary_setq },
818+ { "binary flush", test_binary_flush },
819+ { "binary flushq", test_binary_flushq },
820+ { "binary add", test_binary_add },
821+ { "binary addq", test_binary_addq },
822+ { "binary replace", test_binary_replace },
823+ { "binary replaceq", test_binary_replaceq },
824+ { "binary delete", test_binary_delete },
825+ { "binary deleteq", test_binary_deleteq },
826+ { "binary get", test_binary_get },
827+ { "binary getq", test_binary_getq },
828+ { "binary getk", test_binary_getk },
829+ { "binary getkq", test_binary_getkq },
830+ { "binary incr", test_binary_incr },
831+ { "binary incrq", test_binary_incrq },
832+ { "binary decr", test_binary_decr },
833+ { "binary decrq", test_binary_decrq },
834+ { "binary version", test_binary_version },
835+ { "binary append", test_binary_append },
836+ { "binary appendq", test_binary_appendq },
837+ { "binary prepend", test_binary_prepend },
838+ { "binary prependq", test_binary_prependq },
839+ { "binary stat", test_binary_stat },
840+ { "binary illegal", test_binary_illegal },
841 { NULL, NULL}
842 };
843
844@@ -1209,20 +1880,22 @@
845 for (int ii= 0; testcases[ii].description != NULL; ++ii)
846 {
847 ++total;
848- fprintf(stdout, "%s\t\t", testcases[ii].description);
849+ fprintf(stdout, "%-40s", testcases[ii].description);
850 fflush(stdout);
851
852 bool reconnect= false;
853 enum test_return ret= testcases[ii].function();
854- fprintf(stderr, "%s\n", status_msg[ret]);
855 if (ret == TEST_FAIL)
856 {
857 reconnect= true;
858 ++failed;
859+ if (verbose)
860+ fprintf(stderr, "\n");
861 }
862 else if (ret == TEST_PASS_RECONNECT)
863 reconnect= true;
864
865+ fprintf(stderr, "%s\n", status_msg[ret]);
866 if (reconnect)
867 {
868 (void) close(sock);
869
870=== modified file 'libmemcached/memcached_delete.c'
871--- libmemcached/memcached_delete.c 2009-07-18 17:09:59 +0000
872+++ libmemcached/memcached_delete.c 2009-10-08 14:12:13 +0000
873@@ -36,7 +36,7 @@
874 return MEMCACHED_NO_SERVERS;
875
876 server_key= memcached_generate_hash(ptr, master_key, master_key_length);
877- to_write= (uint8_t) (ptr->flags & MEM_BUFFER_REQUESTS) ? 0 : 1;
878+ to_write= (uint8_t)((ptr->flags & MEM_BUFFER_REQUESTS) ? 0 : 1);
879 bool no_reply= (ptr->flags & MEM_NOREPLY);
880
881 if (ptr->flags & MEM_BINARY_PROTOCOL)
882
883=== modified file 'libmemcached/memcached_get.c'
884--- libmemcached/memcached_get.c 2009-09-19 12:24:37 +0000
885+++ libmemcached/memcached_get.c 2009-10-08 14:12:13 +0000
886@@ -338,6 +338,9 @@
887 rc= MEMCACHED_SOME_ERRORS;
888 continue;
889 }
890+
891+ /* We just want one pending response per server */
892+ memcached_server_response_reset(&ptr->hosts[server_key]);
893 memcached_server_response_increment(&ptr->hosts[server_key]);
894 if ((x > 0 && x == ptr->io_key_prefetch) &&
895 memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS)
896@@ -371,7 +374,6 @@
897 memcached_io_reset(&ptr->hosts[x]);
898 rc= MEMCACHED_SOME_ERRORS;
899 }
900- memcached_server_response_increment(&ptr->hosts[x]);
901 }
902 }
903
904@@ -438,6 +440,8 @@
905 success= false;
906 continue;
907 }
908+ /* we just want one pending response per server */
909+ memcached_server_response_reset(&ptr->hosts[server]);
910 memcached_server_response_increment(&ptr->hosts[server]);
911 }
912
913@@ -461,7 +465,6 @@
914 dead_servers[x]= true;
915 success= false;
916 }
917- memcached_server_response_increment(&ptr->hosts[x]);
918
919 /* mark all of the messages bound for this server as sent! */
920 for (x= 0; x < number_of_keys; ++x)
921
922=== modified file 'libmemcached/memcached_purge.c'
923--- libmemcached/memcached_purge.c 2009-07-18 17:37:40 +0000
924+++ libmemcached/memcached_purge.c 2009-10-08 14:12:13 +0000
925@@ -10,7 +10,7 @@
926 if (ptr->root->purging || /* already purging */
927 (memcached_server_response_count(ptr) < ptr->root->io_msg_watermark &&
928 ptr->io_bytes_sent < ptr->root->io_bytes_watermark) ||
929- (ptr->io_bytes_sent > ptr->root->io_bytes_watermark &&
930+ (ptr->io_bytes_sent >= ptr->root->io_bytes_watermark &&
931 memcached_server_response_count(ptr) < 2))
932 {
933 return MEMCACHED_SUCCESS;
934
935=== modified file 'libmemcached/memcached_response.c'
936--- libmemcached/memcached_response.c 2009-09-22 08:26:38 +0000
937+++ libmemcached/memcached_response.c 2009-10-08 14:12:13 +0000
938@@ -356,8 +356,14 @@
939 {
940 switch (header.response.opcode)
941 {
942+ case PROTOCOL_BINARY_CMD_GETKQ:
943+ /*
944+ * We didn't increment the response counter for the GETKQ packet
945+ * (only the final NOOP), so we need to increment the counter again.
946+ */
947+ memcached_server_response_increment(ptr);
948+ /* FALLTHROUGH */
949 case PROTOCOL_BINARY_CMD_GETK:
950- case PROTOCOL_BINARY_CMD_GETKQ:
951 {
952 uint16_t keylen= header.response.keylen;
953 memcached_result_reset(result);
954
955=== modified file 'libmemcached/memcached_stats.c'
956--- libmemcached/memcached_stats.c 2009-07-18 17:37:40 +0000
957+++ libmemcached/memcached_stats.c 2009-10-08 14:12:13 +0000
958@@ -185,8 +185,6 @@
959 length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_items);
960 else if (!memcmp("total_items", key, strlen("total_items")))
961 length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_items);
962- else if (!memcmp("bytes", key, strlen("bytes")))
963- length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
964 else if (!memcmp("curr_connections", key, strlen("curr_connections")))
965 length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_connections);
966 else if (!memcmp("total_connections", key, strlen("total_connections")))
967@@ -207,6 +205,8 @@
968 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read);
969 else if (!memcmp("bytes_written", key, strlen("bytes_written")))
970 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written);
971+ else if (!memcmp("bytes", key, strlen("bytes")))
972+ length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
973 else if (!memcmp("limit_maxbytes", key, strlen("limit_maxbytes")))
974 length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes);
975 else if (!memcmp("threads", key, strlen("threads")))
976
977=== modified file 'tests/function.c'
978--- tests/function.c 2009-10-05 21:04:26 +0000
979+++ tests/function.c 2009-10-08 14:12:13 +0000
980@@ -4505,6 +4505,156 @@
981 return TEST_SUCCESS;
982 }
983
984+static test_return regression_bug_434843(memcached_st *memc)
985+{
986+ if (pre_binary(memc) != TEST_SUCCESS)
987+ return TEST_SUCCESS;
988+
989+ memcached_return rc;
990+ unsigned int counter= 0;
991+ memcached_execute_function callbacks[1]= { [0]= &callback_counter };
992+
993+ /*
994+ * I only want to hit only _one_ server so I know the number of requests I'm
995+ * sending in the pipleine to the server. Let's try to do a multiget of
996+ * 10240 (that should satisfy most users don't you tink?)
997+ */
998+ uint32_t number_of_hosts= memc->number_of_hosts;
999+ memc->number_of_hosts= 1;
1000+ const size_t max_keys= 10240;
1001+ char **keys= calloc(max_keys, sizeof(char*));
1002+ size_t *key_length=calloc(max_keys, sizeof(size_t));
1003+
1004+ for (int x= 0; x < (int)max_keys; ++x)
1005+ {
1006+ char k[251];
1007+ key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
1008+ keys[x]= strdup(k);
1009+ assert(keys[x] != NULL);
1010+ }
1011+
1012+ /*
1013+ * Run two times.. the first time we should have 100% cache miss,
1014+ * and the second time we should have 100% cache hits
1015+ */
1016+ for (int y= 0; y < 2; ++y)
1017+ {
1018+ rc= memcached_mget(memc, (const char**)keys, key_length, max_keys);
1019+ assert(rc == MEMCACHED_SUCCESS);
1020+ rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
1021+ if (y == 0)
1022+ {
1023+ /* The first iteration should give me a 100% cache miss. verify that*/
1024+ assert(counter == 0);
1025+ char blob[1024];
1026+ for (int x= 0; x < (int)max_keys; ++x)
1027+ {
1028+ rc= memcached_add(memc, keys[x], key_length[x],
1029+ blob, sizeof(blob), 0, 0);
1030+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
1031+ }
1032+ }
1033+ else
1034+ {
1035+ /* Verify that we received all of the key/value pairs */
1036+ assert(counter == (unsigned int)max_keys);
1037+ }
1038+ }
1039+
1040+ /* Release allocated resources */
1041+ for (size_t x= 0; x < max_keys; ++x)
1042+ free(keys[x]);
1043+ free(keys);
1044+ free(key_length);
1045+
1046+ memc->number_of_hosts= number_of_hosts;
1047+ return TEST_SUCCESS;
1048+}
1049+
1050+static test_return regression_bug_434843_buffered(memcached_st *memc)
1051+{
1052+ memcached_return rc;
1053+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
1054+ assert(rc == MEMCACHED_SUCCESS);
1055+
1056+ return regression_bug_434843(memc);
1057+}
1058+
1059+static test_return regression_bug_421108(memcached_st *memc)
1060+{
1061+ memcached_return rc;
1062+ memcached_stat_st *memc_stat= memcached_stat(memc, NULL, &rc);
1063+ assert(rc == MEMCACHED_SUCCESS);
1064+
1065+ char *bytes= memcached_stat_get_value(memc, memc_stat, "bytes", &rc);
1066+ assert(rc == MEMCACHED_SUCCESS);
1067+ assert(bytes != NULL);
1068+ char *bytes_read= memcached_stat_get_value(memc, memc_stat,
1069+ "bytes_read", &rc);
1070+ assert(rc == MEMCACHED_SUCCESS);
1071+ assert(bytes_read != NULL);
1072+
1073+ char *bytes_written= memcached_stat_get_value(memc, memc_stat,
1074+ "bytes_written", &rc);
1075+ assert(rc == MEMCACHED_SUCCESS);
1076+ assert(bytes_written != NULL);
1077+
1078+ assert(strcmp(bytes, bytes_read) != 0);
1079+ assert(strcmp(bytes, bytes_written) != 0);
1080+
1081+ /* Release allocated resources */
1082+ free(bytes);
1083+ free(bytes_read);
1084+ free(bytes_written);
1085+ memcached_stat_free(NULL, memc_stat);
1086+ return TEST_SUCCESS;
1087+}
1088+
1089+/*
1090+ * The test case isn't obvious so I should probably document why
1091+ * it works the way it does. Bug 442914 was caused by a bug
1092+ * in the logic in memcached_purge (it did not handle the case
1093+ * where the number of bytes sent was equal to the watermark).
1094+ * In this test case, create messages so that we hit that case
1095+ * and then disable noreply mode and issue a new command to
1096+ * verify that it isn't stuck. If we change the format for the
1097+ * delete command or the watermarks, we need to update this
1098+ * test....
1099+ */
1100+static test_return regression_bug_442914(memcached_st *memc)
1101+{
1102+ memcached_return rc;
1103+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
1104+ assert(rc == MEMCACHED_SUCCESS);
1105+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1);
1106+
1107+ uint32_t number_of_hosts= memc->number_of_hosts;
1108+ memc->number_of_hosts= 1;
1109+
1110+ char k[250];
1111+ size_t len;
1112+
1113+ for (int x= 0; x < 250; ++x)
1114+ {
1115+ len= (size_t)snprintf(k, sizeof(k), "%0250u", x);
1116+ rc= memcached_delete(memc, k, len, 0);
1117+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
1118+ }
1119+
1120+ len= (size_t)snprintf(k, sizeof(k), "%037u", 251);
1121+ rc= memcached_delete(memc, k, len, 0);
1122+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
1123+
1124+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 0);
1125+ assert(rc == MEMCACHED_SUCCESS);
1126+ rc= memcached_delete(memc, k, len, 0);
1127+ assert(rc == MEMCACHED_NOTFOUND);
1128+
1129+ memc->number_of_hosts= number_of_hosts;
1130+
1131+ return TEST_SUCCESS;
1132+}
1133+
1134 test_st udp_setup_server_tests[] ={
1135 {"set_udp_behavior_test", 0, set_udp_behavior_test},
1136 {"add_tcp_server_udp_client_test", 0, add_tcp_server_udp_client_test},
1137@@ -4666,6 +4816,10 @@
1138 */
1139 test_st regression_tests[]= {
1140 {"lp:434484", 1, regression_bug_434484 },
1141+ {"lp:434843", 1, regression_bug_434843 },
1142+ {"lp:434843 buffered", 1, regression_bug_434843_buffered },
1143+ {"lp:421108", 1, regression_bug_421108 },
1144+ {"lp:442914", 1, regression_bug_442914 },
1145 {0, 0, 0}
1146 };
1147

Subscribers

People subscribed via source and target branches

to all changes: