1 /*
2 * MDCacheD basic functionality test application / regression tests
3 * (c) 2007.-2011. Ivan Voras <ivoras@gmail.com>
4 * $Id: test_mdcached.c 475 2012-04-08 10:27:00Z ivoras $
5 */
6 #include <sys/types.h>
7 #include <sys/fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/socket.h>
10 #include <sys/time.h>
11 #include <sys/un.h>
12 #include <netinet/in.h>
13 #include <netinet/tcp.h>
14 #include <netdb.h>
15 #include <time.h>
16
17 #include "linux-shims.h"
18
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <paths.h>
25 #include <syslog.h>
26 #include <assert.h>
27 #include <err.h>
28
29 #include "mc_protocol.h"
30 #include "mc_client.h"
31
32 char *server_host = MC_DEFAULT_UNIX_SOCKET;
33 int server_port = MC_DEFAULT_INET_PORT;
34 int is_unix_socket = TRUE;
35 int scale = 1;
36 struct mc_connection *conn;
37
38
39 #define N_HANDSHAKES 10000
40 #define N_DATA 10000
41
get_time()42 double get_time()
43 {
44 struct timeval tv;
45 gettimeofday(&tv, NULL);
46 return (double)(tv.tv_sec) + (double)(tv.tv_usec / 1000000.0);
47 }
48
49
gen_value(char * value,int i)50 int gen_value(char *value, int i) {
51 return sprintf(value, "%05d - %03x %d %d %d", i, N_DATA-i, i, i, i);
52 }
53
main(int argc,char ** argv)54 int main(int argc, char **argv)
55 {
56 int i;
57 int ch, res;
58 char *err;
59 double t1, t2;
60 char *name, *value, *value0;
61 unsigned int name_len, value_len, value0_len, n_tags;
62 struct mc_tag tags1[2], *tags;
63 tag_value_t tval[2];
64 struct mc_multidata_result *mdres;
65 struct mc_multistring_result *msres;
66 struct mc_data_entry *records[2];
67 uint16_t key_lengths[2];
68 char *keys[2];
69 struct mc_data_entry *rp;
70 int64_t ll;
71
72 while ((ch = getopt(argc, argv, "c:s:h")) != -1) {
73 switch (ch) {
74 case 'h':
75 printf("usage: %s [-c server] [-s #] [-h]\n", argv[0]);
76 printf(" -c server Server to connect to (default = local unix socket)\n");
77 printf(" -s # Scale of tests (larger # -> test more data)\n");
78 exit(0);
79 break;
80 case 'c':
81 server_host = optarg;
82 is_unix_socket = FALSE;
83 break;
84 case 's':
85 scale = atoi(optarg);
86 break;
87 default:
88 fprintf(stderr, "Unknown argument: %c\n", ch);
89 exit(1);
90 }
91 }
92
93 printf("Structure size survey: mc_header(%zd) mc_handshake(%zd) mc_data_entry(%zd)\nConnecting...\n",
94 sizeof(struct mc_header), sizeof(struct mc_handshake), sizeof(struct mc_data_entry));
95
96 if (is_unix_socket)
97 conn = mc_connect_local(server_host, TRUE, &err);
98 else
99 conn = mc_connect_tcp(server_host, server_port, TRUE, &err);
100 if (conn == NULL)
101 errx(1, "%s", err);
102
103 /* Test basic operations */
104 printf("Connection ok (with handshake). Testing handshake speed.\n");
105 t1 = get_time();
106 for (i = 0; i < N_HANDSHAKES*scale; i++)
107 if (mc_perform_handshake(conn, &err) != MC_RESULT_OK)
108 errx(1, "%s", err);
109 t2 = get_time();
110 printf("%u handshakes took %0.3lf seconds: %0.1lf handshakes/s\n",
111 N_HANDSHAKES*scale, t2-t1, (N_HANDSHAKES*scale)/(t2-t1));
112
113 name = malloc(100);
114 value = malloc(500);
115 printf("Testing ADD operation\n");
116 t1 = get_time();
117 for (i = 0; i < N_DATA*scale; i++) {
118 name_len = sprintf(name, "%d", i);
119 value_len = gen_value(value, i);
120 if (mc_put_simple(conn, name, name_len, value, value_len, 120, &err) != MC_RESULT_OK)
121 errx(1, "mc_put_simple: %s", err);
122 }
123 t2 = get_time();
124 printf("%u additions took %0.3lf seconds: %0.1lf additions/s\n",
125 N_DATA*scale, t2-t1, (N_DATA*scale)/(t2-t1));
126
127 printf("Testing GET operation\n");
128 t1 = get_time();
129 for (i = 0; i < N_DATA*scale; i++) {
130 name_len = sprintf(name, "%d", i);
131 value_len = gen_value(value, i);
132 res = mc_get_simple(conn, name, name_len, &value0, &value0_len, &err);
133 if (res != MC_RESULT_OK)
134 errx(1, "Error in mc_get_simple: %s (%d)", err, res);
135 if (memcmp(value0, value, value0_len) != 0)
136 errx(1, "Value mismatch for %s", name);
137 free(value0);
138 }
139 t2 = get_time();
140 printf("%u retrievals took %0.3lf seconds: %0.1lf additions/s\n",
141 N_DATA*scale, t2-t1, (N_DATA*scale)/(t2-t1));
142
143 printf("Testing large data PUT & GET\n");
144 value_len = 64*1024;
145 value = realloc(value, value_len);
146 name_len = sprintf(name, "large-data");
147 if (mc_put_simple(conn, name, name_len, value, value_len, 120, &err) != MC_RESULT_OK)
148 errx(1, "mc_put_simple: %s\n", err);
149 res = mc_get_simple(conn, name, name_len, &value0, &value0_len, &err);
150 if (res != MC_RESULT_OK)
151 errx(1, "mc_get_simple: %s\n", err);
152
153 /* Test tags: insertion */
154 printf("Testing ADD with TAGS operation\n");
155 t1 = get_time();
156 for (i = 0; i < N_DATA*scale; i++) {
157 name_len = sprintf(name, "t-%d", i);
158 value_len = gen_value(value, i);
159 tags1[0].key = -1;
160 tags1[0].value = i;
161 tags1[1].key = i % 500;
162 tags1[1].value = i;
163 if (mc_put_simple_tags(conn, name, name_len, value, value_len, tags1, 2, 120, &err) != MC_RESULT_OK)
164 errx(1, "%s", err);
165 }
166 t2 = get_time();
167 printf("%u additions took %0.3lf seconds: %0.1lf additions/s\n",
168 N_DATA*scale, t2-t1, (N_DATA*scale)/(t2-t1));
169
170 /* Test tags: retrieval */
171 printf("Testing TAGS retrieval\n");
172 tval[0] = 0;
173 tval[1] = 10;
174 res = mc_get_by_tag_values(conn, -1, tval, 2, &mdres, &err);
175 if (res != MC_RESULT_OK)
176 errx(1, "mc_get_by_tag_values: %s (%d)", err, res);
177 if (mdres->n_records != 2)
178 errx(1, "Exactly 2 records needed to be returned; instead, %d were returned", mdres->n_records);
179 name = calloc(1, 100);
180 for (i = 0; i < mdres->n_records; i++) {
181 rp = mdres->records[i];
182 strlcpy(name, mc_data_entry_key(rp), rp->name_size+1);
183 if (strcmp(name, "t-0") != 0 && strcmp(name, "t-10") != 0)
184 errx(1, "Returned records not the #0 and #10 as exepected! Got \"%s\"", name);
185 }
186 assert(mdres->pkt != NULL);
187 mc_multidata_result_free(mdres);
188
189 /* Test tags: deletion */
190 printf("Testing TAGS deletion\n");
191 if (mc_perform_handshake(conn, &err) != MC_RESULT_OK)
192 errx(1, "%s", err);
193 if (mc_del_by_tag_values(conn, -1, tval, 2, NULL, &err) != MC_RESULT_OK)
194 errx(1, "mc_del_by_tag_values: %s", err);
195
196 /* Test get_all_keys */
197 printf("Testing GET_ALL_KEYS\n");
198 if ((res = mc_get_all_keys(conn, 0, &msres, &err)) != MC_RESULT_OK)
199 errx(1, "mc_get_all_keys: %d, %s", res, err);
200 for (i = 0; i < msres->n_records; i++)
201 if (memcmp(msres->records[i]->data, "t-0", 4) == 0 ||
202 memcmp(msres->records[i]->data, "t-10", 5) == 0)
203 errx(1, "Found supposedly deleted record: %s", msres->records[i]->data);
204 if (mc_perform_handshake(conn, &err) != MC_RESULT_OK)
205 errx(1, "%s", err);
206
207 /* Test mput */
208 printf("Testing MPUT\n");
209 mc_build_data_entry(&records[0], "mdr0", 5, "data0", 6, NULL, 0, 10, MC_SEQ);
210 mc_build_data_entry(&records[1], "mdr1", 5, "data1", 6, NULL, 0, 10, MC_SEQ);
211 if (mc_mput(conn, 2, records, 0, &err) != MC_RESULT_OK)
212 errx(1, "mc_mput: %s", err);
213 if (mc_get_simple(conn, "mdr0", 5, &value0, &value0_len, &err) != MC_RESULT_OK)
214 errx(1, "mc_get_simple after mc_mput failure: %s (0)", err);
215 if (mc_get_simple(conn, "mdr1", 5, &value0, &value0_len, &err) != MC_RESULT_OK)
216 errx(1, "mc_get_simple after mc_mput failure: %s (1)", err);
217 if (mc_perform_handshake(conn, &err) != MC_RESULT_OK)
218 errx(1, "%s", err);
219
220 /* Test mget */
221 printf("Testing MGET\n");
222 key_lengths[0] = key_lengths[1] = 5;
223 keys[0] = "mdr0";
224 keys[1] = "mdr1";
225 if (mc_mget(conn, 2, key_lengths, keys, 0, &mdres, &err) != MC_RESULT_OK)
226 errx(1, "mc_mget: %s", err);
227 if (mdres->n_records != 2)
228 errx(1, "n_records after mc_mget mismatch (%d)", mdres->n_records);
229 rp = mdres->records[0];
230 if (memcmp(mc_data_entry_key(rp), keys[0], 5) != 0)
231 errx(1, "memcmp after mc_mget failure (name, 0)");
232 if (memcmp(mc_data_entry_value(rp), "data0", 6) != 0)
233 errx(1, "memcmp after mc_mget failure (data, 0)");
234 rp = mdres->records[1];
235 if (memcmp(mc_data_entry_key(rp), keys[1], 5) != 0)
236 errx(1, "memcmp after mc_mget failure (name, 1)");
237 if (memcmp(mc_data_entry_value(rp), "data1", 6) != 0)
238 errx(1, "memcmp after mc_mget failure (data, 1)");
239 if (mc_perform_handshake(conn, &err) != MC_RESULT_OK)
240 errx(1, "%s", err);
241
242 /* Test tstack operation */
243 printf("Testing TSTACK\n");
244 tags1[0].key = 50;
245 tags1[0].value = 100;
246
247 if (mc_tstack_push(conn, tags1, 1, "in_tstack1", sizeof("in_tstack1"), &name, &name_len, &err) != MC_RESULT_OK)
248 errx(1, "%s", err);
249 if (mc_tstack_push(conn, tags1, 1, "in_tstack2", sizeof("in_tstack2"), &name, &name_len, &err) != MC_RESULT_OK)
250 errx(1, "%s", err);
251 i = 0;
252 while (1) {
253 if ((res = mc_tstack_pop(conn, tags1[0].key, tags1[0].value, &rp, &err)) == MC_RESULT_OK) {
254 switch (i) {
255 case 0:
256 if (memcmp(mc_data_entry_value(rp), "in_tstack2", sizeof("in_tstack2")) != 0)
257 warnx("Unexpected stack element at 0: %s", mc_data_entry_value(rp));
258 break;
259 case 1:
260 if (memcmp(mc_data_entry_value(rp), "in_tstack1", sizeof("in_tstack2")) != 0)
261 warnx("Unexpected stack element at 0: %s", mc_data_entry_value(rp));
262 break;
263 default:
264 errx(1, "tstack failure: more elements than there should be");
265 }
266 free(rp);
267 i++;
268 } else {
269 if (i != 2)
270 errx(1, "tstack failure: less elements than there should be!");
271 break;
272 }
273 }
274
275 /* Test atomic ops */
276 printf("Testing ATOMIC\n");
277 if ((res = mc_atomic_get(conn, "atomic-", 7, &ll, &err)) != MC_RESULT_NOT_FOUND)
278 errx(1, "ATOMIC_GET failure: found impossible record: %d", res);
279 if (mc_atomic_put(conn, "atomic0", 7, 0, NULL, 0, 0, &err) != MC_RESULT_OK)
280 errx(1, "%s", err);
281 if (mc_atomic_add(conn, "atomic0", 7, 5, &ll, &err) != MC_RESULT_OK)
282 errx(1, "%s", err);
283 if (ll != 5)
284 errx(1, "ATOMIC_ADD failure: got %lld, expected %lld", (long long)ll, 5LL);
285 if (mc_atomic_get(conn, "atomic0", 7, &ll, &err) != MC_RESULT_OK)
286 errx(1, "ATOMIC_GET failure");
287 if (ll != 5)
288 errx(1, "ATOMIC_GET failure: got %lld, expected %lld", (long long)ll, 5LL);
289
290 printf("Testing SET_TAGS\n");
291 if (mc_put_simple(conn, "recT", 4, "value0", 6, 120, &err) != MC_RESULT_OK)
292 errx(1, "mc_put_simple");
293 if (mc_get_simple_tags(conn, "recT", 4, &value, &value_len, &tags, &n_tags, &err) != MC_RESULT_OK)
294 errx(1, "mc_get_simple_tags");
295 if (n_tags != 0)
296 errx(1, "mc_get_simple_tags returned a record with %d tags", n_tags);
297 tags1[0].key = 31;
298 tags1[0].value = 41;
299 tags1[1].key = 59;
300 tags1[1].value = 26;
301 if (mc_set_tags(conn, "recT", 4, tags1, 2, &err) != MC_RESULT_OK)
302 errx(1, "mc_set_tags");
303 if (mc_get_simple_tags(conn, "recT", 4, &value, &value_len, &tags, &n_tags, &err) != MC_RESULT_OK)
304 errx(1, "mc_get_simple_tags");
305 if (n_tags != 2)
306 errx(1, "mc_get_simple_tags returned a record with %d tags", n_tags);
307 for (i = 0; i < 2; i++)
308 if ((tags[i].key != 31 && tags[i].key != 59) || (tags[i].value != 41 && tags[i].value != 26))
309 errx(1, "tags bogus?");
310
311 if (mc_perform_handshake(conn, &err) != MC_RESULT_OK)
312 errx(1, "%s", err);
313 printf("Ok.\n");
314 return 0;
315 }
316