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