2 * 2008+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
17 #include <sys/resource.h>
26 #include <elliptics/cppdef.h>
28 using namespace ioremap::elliptics
;
30 static void test_log_raw(logger
*l
, int level
, const char *format
, ...)
34 int buflen
= sizeof(buf
);
36 if (l
->get_log_level() < level
)
39 va_start(args
, format
);
40 vsnprintf(buf
, buflen
, format
, args
);
46 class callback_io
: public callback
{
48 callback_io(logger
*l
) { log
= l
; };
49 virtual ~callback_io() {};
51 virtual int handle(struct dnet_net_state
*state
, struct dnet_cmd
*cmd
);
57 int callback_io::handle(struct dnet_net_state
*state
, struct dnet_cmd
*cmd
)
60 struct dnet_io_attr
*io
;
62 if (is_trans_destroyed(state
, cmd
)) {
67 if (cmd
->status
|| !cmd
->size
) {
72 if (cmd
->size
<= sizeof(struct dnet_io_attr
)) {
73 test_log_raw(log
, DNET_LOG_ERROR
, "%s: read completion error: wrong size: "
74 "cmd_size: %llu, must be more than %zu.\n",
75 dnet_dump_id(&cmd
->id
), (unsigned long long)cmd
->size
,
76 sizeof(struct dnet_io_attr
));
82 test_log_raw(log
, DNET_LOG_ERROR
, "%s: no attributes but command size is not null.\n",
83 dnet_dump_id(&cmd
->id
));
88 io
= (struct dnet_io_attr
*)(cmd
+ 1);
90 dnet_convert_io_attr(io
);
93 test_log_raw(log
, DNET_LOG_INFO
, "%s: io completion: offset: %llu, size: %llu.\n",
94 dnet_dump_id(&cmd
->id
), (unsigned long long)io
->offset
, (unsigned long long)io
->size
);
97 if (!cmd
|| !(cmd
->flags
& DNET_FLAGS_MORE
))
98 test_log_raw(log
, DNET_LOG_INFO
, "%s: io completed: %d.\n", cmd
? dnet_dump_id(&cmd
->id
) : "nil", err
);
102 static void test_prepare_commit(session
&s
, int psize
, int csize
)
104 std::string written
, ret
;
106 std::string key
= "prepare-commit-test";
108 std::string prepare_data
= "prepare data|";
109 std::string commit_data
= "commit data";
110 std::string plain_data
[3] = {"plain data0|", "plain data1|", "plain data2|"};
113 prepare_data
.clear();
118 uint64_t total_size_to_reserve
= 1024;
121 unsigned int ioflags
= 0;
125 s
.write_prepare(key
, prepare_data
, offset
, total_size_to_reserve
, cflags
, ioflags
, column
);
126 offset
+= prepare_data
.size();
128 written
+= prepare_data
;
130 for (int i
= 0; i
< 3; ++i
) {
131 s
.write_plain(key
, plain_data
[i
], offset
, cflags
, ioflags
, column
);
132 offset
+= plain_data
[i
].size();
134 written
+= plain_data
[i
];
137 /* append data first so that subsequent written.size() call returned real size of the written data */
138 written
+= commit_data
;
140 s
.write_commit(key
, commit_data
, offset
, written
.size(), cflags
, ioflags
, column
);
142 ret
= s
.read_data_wait(key
, 0, 0, cflags
, ioflags
, column
);
143 std::cout
<< "prepare/commit write: '" << written
<< "', read: '" << ret
<< "'" << std::endl
;
144 } catch (const std::exception
&e
) {
145 std::cerr
<< "PREPARE/COMMIT test failed: " << e
.what() << std::endl
;
149 if (ret
!= written
) {
150 std::cerr
<< "PREPARE/COMMIT test failed: read mismatch" << std::endl
;
151 throw std::runtime_error("PREPARE/COMMIT test failed: read mismatch");
155 static void test_range_request(session
&s
, int limit_start
, int limit_num
, uint64_t cflags
, int group_id
)
157 struct dnet_io_attr io
;
159 memset(&io
, 0, sizeof(io
));
162 dnet_parse_numeric_id("76a046fcd25ebeaaa65a0fa692faf8b8701695c6ba67008b5922ae9f134fc1da7ffffed191edf767000000000000000000000000000000000000000000000000", &io
.id
);
163 dnet_parse_numeric_id("76a046fcd25ebeaaa65a0fa692faf8b8701695c6ba67008b5922ae9f134fc1da7ffffed22220037fffffffffffffffffffffffffffffffffffffffffffffffff", &io
.parent
);
165 memset(io
.id
, 0x00, sizeof(io
.id
));
166 memset(io
.parent
, 0xff, sizeof(io
.id
));
168 io
.start
= limit_start
;
171 std::vector
<std::string
> ret
;
172 ret
= s
.read_data_range(io
, group_id
, cflags
);
174 std::cout
<< "range [LIMIT(" << limit_start
<< ", " << limit_num
<< "): " << ret
.size() << " elements" << std::endl
;
176 for (size_t i
= 0; i
< ret
.size(); ++i
) {
177 char id_str
[DNET_ID_SIZE
* 2 + 1];
178 const char *data
= ret
[i
].data();
179 const unsigned char *id
= (const unsigned char *)data
;
180 uint64_t size
= dnet_bswap64(*(uint64_t *)(data
+ DNET_ID_SIZE
));
181 char *str
= (char *)(data
+ DNET_ID_SIZE
+ 8);
183 std::cout
<< "range [LIMIT(" << limit_start
<< ", " << limit_num
<< "): " <<
184 dnet_dump_id_len_raw(id
, DNET_ID_SIZE
, id_str
) << ": size: " << size
<< ": " << str
<< std::endl
;
189 static void test_lookup_parse(const std::string
&key
, const std::string
&lret
)
191 struct dnet_addr
*addr
= (struct dnet_addr
*)lret
.data();
192 struct dnet_cmd
*cmd
= (struct dnet_cmd
*)(addr
+ 1);
193 struct dnet_addr_attr
*a
= (struct dnet_addr_attr
*)(cmd
+ 1);
195 dnet_convert_addr_attr(a
);
196 std::cout
<< key
<< ": lives on addr: " << dnet_server_convert_dnet_addr(&a
->addr
);
198 if (cmd
->size
> sizeof(struct dnet_addr_attr
)) {
199 struct dnet_file_info
*info
= (struct dnet_file_info
*)(a
+ 1);
201 dnet_convert_file_info(info
);
202 std::cout
<< ": mode: " << std::oct
<< info
->mode
<< std::dec
;
203 std::cout
<< ", offset: " << (unsigned long long)info
->offset
;
204 std::cout
<< ", size: " << (unsigned long long)info
->size
;
205 std::cout
<< ", file: " << (char *)(info
+ 1);
207 std::cout
<< std::endl
;
210 static void test_lookup(session
&s
, std::vector
<int> &groups
)
213 std::string key
= "2.xml";
214 std::string data
= "lookup data";
216 std::string lret
= s
.write_data_wait(key
, data
, 0, 0, 0, 0);
217 test_lookup_parse(key
, lret
);
220 s
.transform(key
, id
);
226 struct timespec ts
= {0, 0};
227 s
.write_metadata(id
, key
, groups
, ts
, cflags
);
229 lret
= s
.lookup(key
);
230 test_lookup_parse(key
, lret
);
231 } catch (const std::exception
&e
) {
232 std::cerr
<< "LOOKUP test failed: " << e
.what() << std::endl
;
236 static void test_append(session
&s
)
239 std::string key
= "append-test";
240 std::string data
= "first part of the message";
242 s
.write_data_wait(key
, data
, 0, 0, 0, 0);
244 data
= "| second part of the message";
245 s
.write_data_wait(key
, data
, 0, 0, DNET_IO_FLAGS_APPEND
, 0);
247 std::cout
<< key
<< ": " << s
.read_data_wait(key
, 0, 0, 0, 0, 0) << std::endl
;
248 } catch (const std::exception
&e
) {
249 std::cerr
<< "APPEND test failed: " << e
.what() << std::endl
;
250 throw std::runtime_error("APPEND test failed");
254 static void read_column_raw(session
&s
, const std::string
&key
, const std::string
&data
, int column
)
258 ret
= s
.read_data_wait(key
, 0, 0, 0, 0, column
);
259 } catch (const std::exception
&e
) {
260 std::cerr
<< "COLUMN-" << column
<< " read test failed: " << e
.what() << std::endl
;
264 std::cout
<< "read-column-" << column
<< ": " << key
<< " : " << ret
<< std::endl
;
266 throw std::runtime_error("column test failed");
270 static void column_test(session
&s
)
272 std::string key
= "some-key-1";
274 std::string data0
= "some-compressed-data-in-column-0";
275 std::string data1
= "some-data-in-column-2";
276 std::string data2
= "some-data-in-column-3";
278 s
.write_data_wait(key
, data0
, 0, 0, DNET_IO_FLAGS_COMPRESS
, 0);
279 s
.write_data_wait(key
, data1
, 0, 0, 0, 2);
280 s
.write_data_wait(key
, data2
, 0, 0, 0, 3);
282 read_column_raw(s
, key
, data0
, 0);
283 read_column_raw(s
, key
, data1
, 2);
284 read_column_raw(s
, key
, data2
, 3);
287 static void test_bulk_write(session
&s
)
290 std::vector
<struct dnet_io_attr
> ios
;
291 std::vector
<std::string
> data
;
295 for (i
= 0; i
< 3; ++i
) {
296 std::ostringstream os
;
297 struct dnet_io_attr io
;
300 os
<< "bulk_write" << i
;
302 memset(&io
, 0, sizeof(io
));
303 memset(&id
, 0, sizeof(id
));
305 s
.transform(os
.str(), id
);
306 memcpy(io
.id
, id
.id
, DNET_ID_SIZE
);
308 io
.size
= os
.str().size();
311 data
.push_back(os
.str());
314 std::string ret
= s
.bulk_write(ios
, data
, 0);
316 std::cout
<< "ret size = " << ret
.size() << std::endl
;
319 int ioflags
= DNET_IO_FLAGS_NOCSUM
;
325 /* read without checksums since we did not write metadata */
326 for (i
= 0; i
< 3; ++i
) {
327 std::ostringstream os
;
329 os
<< "bulk_write" << i
;
330 std::cout
<< os
.str() << ": " << s
.read_data_wait(os
.str(), offset
, size
, cflags
, ioflags
, type
) << std::endl
;
332 } catch (const std::exception
&e
) {
333 std::cerr
<< "BULK WRITE test failed: " << e
.what() << std::endl
;
337 static void test_bulk_read(session
&s
)
340 std::vector
<std::string
> keys
;
344 for (i
= 0; i
< 3; ++i
) {
345 std::ostringstream os
;
346 os
<< "bulk_write" << i
;
347 keys
.push_back(os
.str());
350 std::vector
<std::string
> ret
= s
.bulk_read(keys
, 0);
352 std::cout
<< "ret size = " << ret
.size() << std::endl
;
354 /* read without checksums since we did not write metadata */
355 for (i
= 0; i
< 3; ++i
) {
356 std::ostringstream os
;
358 os
<< "bulk_read" << i
;
359 std::cout
<< os
.str() << ": " << ret
[i
].substr(DNET_ID_SIZE
+ 8) << std::endl
;
361 } catch (const std::exception
&e
) {
362 std::cerr
<< "BULK READ test failed: " << e
.what() << std::endl
;
367 static void memory_test_io(session
&s
, int num
)
371 for (int i
= 0; i
< num
; ++i
) {
374 data
.resize(rand() % 102400 + 100);
376 for (int j
= 0; j
< (int)ARRAY_SIZE(ids
); ++j
)
379 std::string
id((char *)ids
, sizeof(ids
));
383 written
= s
.write_data_wait(id
, data
, 0, 0, 0, 0);
384 std::string res
= s
.read_data_wait(id
, 0, 0, 0, 0, 0);
385 } catch (const std::exception
&e
) {
386 std::cerr
<< "could not perform read/write: " << e
.what() << std::endl
;
387 if (written
.size() > 0) {
388 std::cerr
<< "but written successfully\n";
389 test_lookup_parse(id
, written
);
396 static void test_cache_write(session
&s
, int num
)
399 std::vector
<struct dnet_io_attr
> ios
;
400 std::vector
<std::string
> data
;
404 for (i
= 0; i
< num
; ++i
) {
405 std::ostringstream os
;
406 struct dnet_io_attr io
;
409 os
<< "test_cache" << i
;
411 memset(&io
, 0, sizeof(io
));
412 memset(&id
, 0, sizeof(id
));
414 s
.transform(os
.str(), id
);
415 memcpy(io
.id
, id
.id
, DNET_ID_SIZE
);
417 io
.size
= os
.str().size();
420 data
.push_back(os
.str());
423 s
.bulk_write(ios
, data
, 0);
424 } catch (const std::exception
&e
) {
425 std::cerr
<< "cache write test failed: " << e
.what() << std::endl
;
427 std::cout
<< "Cache entries writted: " << num
<< std::endl
;
430 static void test_cache_read(session
&s
, int num
)
434 /* Read random 20% of records written by test_cache_write() */
435 for (int i
= 0; i
< num
; ++i
) {
436 if ((rand() % 100) > 20)
439 std::ostringstream os
;
441 os
<< "test_cache" << i
;
443 std::string
id(os
.str());
446 int ioflags
= DNET_IO_FLAGS_NOCSUM
;
453 s
.read_data_wait(os
.str(), offset
, size
, cflags
, ioflags
, type
);
454 } catch (const std::exception
&e
) {
455 std::cerr
<< "could not perform read : " << e
.what() << std::endl
;
459 std::cout
<< "Cache entries read: " << count
<< std::endl
;
462 static void test_cache_delete(session
&s
, int num
)
466 /* Read random 20% of records written by test_cache_write() */
467 for (int i
= 0; i
< num
; ++i
) {
468 if ((rand() % 100) > 20)
471 std::ostringstream os
;
473 os
<< "test_cache" << i
;
475 std::string
id(os
.str());
479 } catch (const std::exception
&e
) {
480 std::cerr
<< "could not perform remove: " << e
.what() << std::endl
;
484 std::cout
<< "Cache entries deleted: " << count
<< std::endl
;
487 static void memory_test(session
&s
)
489 struct rusage start
, end
;
491 getrusage(RUSAGE_SELF
, &start
);
492 memory_test_io(s
, 1000);
493 getrusage(RUSAGE_SELF
, &end
);
494 std::cout
<< "IO leaked: " << end
.ru_maxrss
- start
.ru_maxrss
<< " Kb\n";
499 fprintf(stderr
, "Usage: %s <options>\n"
500 " -r host - remote host name\n"
501 " -p port - remote port\n"
502 " -g group_id - group_id for range request and bulk write\n"
503 " -w - write cache before read\n"
504 " -m - start client's memory leak test (rather long - several minutes, and space consuming)\n"
509 int main(int argc
, char *argv
[])
512 std::vector
<int> groups(g
, g
+ARRAY_SIZE(g
));
513 char *host
= (char *)"localhost";
515 int ch
, write_cache
= 0;
519 while ((ch
= getopt(argc
, argv
, "mr:p:g:wh")) != -1) {
528 group_id
= atoi(optarg
);
544 log_file
log("/dev/stderr", DNET_LOG_DEBUG
);
548 s
.add_groups(groups
);
551 n
.add_remote(host
, port
, AF_INET
);
553 throw std::runtime_error("Could not add remote nodes, exiting");
556 test_lookup(s
, groups
);
562 test_prepare_commit(s
, 0, 0);
563 test_prepare_commit(s
, 1, 0);
564 test_prepare_commit(s
, 0, 1);
565 test_prepare_commit(s
, 1, 1);
567 test_range_request(s
, 0, 0, 0, group_id
);
568 test_range_request(s
, 0, 0, DNET_ATTR_SORT
, group_id
);
569 test_range_request(s
, 1, 0, 0, group_id
);
570 test_range_request(s
, 0, 1, 0, group_id
);
581 test_cache_write(s
, 1000);
583 test_cache_read(s
, 1000);
584 test_cache_delete(s
, 1000);
585 test_cache_write(s
, 1000);
587 } catch (const std::exception
&e
) {
588 std::cerr
<< "Error occured : " << e
.what() << std::endl
;
590 std::cerr
<< "Error : " << err
<< std::endl
;