]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/osd/TestRados.cc
update sources to v12.1.0
[ceph.git] / ceph / src / test / osd / TestRados.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 #include "common/Mutex.h"
4 #include "common/Cond.h"
5 #include "common/errno.h"
6 #include "common/version.h"
7
8 #include <iostream>
9 #include <sstream>
10 #include <map>
11 #include <numeric>
12 #include <string>
13 #include <vector>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #include "test/osd/RadosModel.h"
18
19
20 using namespace std;
21
22 class WeightedTestGenerator : public TestOpGenerator
23 {
24 public:
25
26 WeightedTestGenerator(int ops,
27 int objects,
28 map<TestOpType, unsigned int> op_weights,
29 TestOpStat *stats,
30 int max_seconds,
31 bool ec_pool,
32 bool balance_reads,
33 bool set_redirect) :
34 m_nextop(NULL), m_op(0), m_ops(ops), m_seconds(max_seconds),
35 m_objects(objects), m_stats(stats),
36 m_total_weight(0),
37 m_ec_pool(ec_pool),
38 m_balance_reads(balance_reads),
39 m_set_redirect(set_redirect)
40 {
41 m_start = time(0);
42 for (map<TestOpType, unsigned int>::const_iterator it = op_weights.begin();
43 it != op_weights.end();
44 ++it) {
45 m_total_weight += it->second;
46 m_weight_sums.insert(pair<TestOpType, unsigned int>(it->first,
47 m_total_weight));
48 }
49 if (m_set_redirect) {
50 /* create redirect objects + set-redirect*/
51 m_redirect_objects = objects*2; // for copy_from + set-redirect test
52 m_initial_redirected_objects = objects;
53 m_ops = ops+m_redirect_objects+m_initial_redirected_objects;
54 }
55 }
56
57 TestOp *next(RadosTestContext &context) override
58 {
59 TestOp *retval = NULL;
60
61 ++m_op;
62 if (m_op <= m_objects) {
63 stringstream oid;
64 oid << m_op;
65 if (m_op % 2) {
66 // make it a long name
67 oid << " " << string(300, 'o');
68 }
69 cout << m_op << ": write initial oid " << oid.str() << std::endl;
70 context.oid_not_flushing.insert(oid.str());
71 if (m_ec_pool) {
72 return new WriteOp(m_op, &context, oid.str(), true, true);
73 } else {
74 return new WriteOp(m_op, &context, oid.str(), false, true);
75 }
76 } else if (m_op >= m_ops) {
77 return NULL;
78 }
79
80 if (m_set_redirect) {
81 /*
82 * set-redirect test
83 * 1. create objects (copy from)
84 * 2. set-redirect
85 */
86 int create_objects_end = m_objects + m_redirect_objects;
87 int set_redirect_end = create_objects_end + m_initial_redirected_objects;
88
89 if (m_op <= create_objects_end) {
90 stringstream oid;
91 int _oid = m_op;
92 oid << _oid;
93 if ((_oid) % 2) {
94 oid << " " << string(300, 'o');
95 }
96 stringstream oid2;
97 int _oid2 = _oid - m_objects;
98 oid2 << _oid2;
99 if ((_oid2) % 2) {
100 oid2 << " " << string(300, 'o');
101 }
102 cout << m_op << ": " << "(create redirect oid) copy_from oid " << oid.str()
103 << " from oid " << oid2.str() << std::endl;
104 return new CopyFromOp(m_op, &context, oid.str(), oid2.str(), m_stats);
105 } else if (m_op <= set_redirect_end) {
106 stringstream oid;
107 int _oid = m_op-create_objects_end;
108 oid << _oid;
109 if ((_oid) % 2) {
110 oid << " " << string(300, 'o');
111 }
112 stringstream oid2;
113 int _oid2 = _oid + m_objects;
114 oid2 << _oid2;
115 if ((_oid2) % 2) {
116 oid2 << " " << string(300, 'o');
117 }
118 cout << m_op << ": " << "set_redirect oid " << oid.str() << " target oid "
119 << oid2.str() << std::endl;
120 return new SetRedirectOp(m_op, &context, oid.str(), oid2.str(), context.pool_name);
121 }
122
123 if (!context.oid_redirect_not_in_use.size() && m_op > set_redirect_end) {
124 int set_size = context.oid_not_in_use.size();
125 if (set_size < m_objects + m_redirect_objects) {
126 return NULL;
127 }
128 for (int t_op = m_objects+1; t_op <= create_objects_end; t_op++) {
129 stringstream oid;
130 oid << t_op;
131 if (t_op % 2) {
132 oid << " " << string(300, 'o');
133 }
134 context.oid_not_flushing.erase(oid.str());
135 context.oid_not_in_use.erase(oid.str());
136 context.oid_in_use.erase(oid.str());
137 cout << m_op << ": " << " remove oid " << oid.str() << " from oid_*_use " << std::endl;
138 if (t_op > m_objects + m_initial_redirected_objects) {
139 context.oid_redirect_not_in_use.insert(oid.str());
140 }
141 }
142 cout << m_op << ": " << " oid_not_in_use: " << context.oid_not_in_use.size()
143 << " oid_in_use: " << context.oid_in_use.size() << std::endl;
144 }
145 }
146
147 if (m_nextop) {
148 retval = m_nextop;
149 m_nextop = NULL;
150 return retval;
151 }
152
153 while (retval == NULL) {
154 unsigned int rand_val = rand() % m_total_weight;
155
156 time_t now = time(0);
157 if (m_seconds && now - m_start > m_seconds)
158 break;
159
160 for (map<TestOpType, unsigned int>::const_iterator it = m_weight_sums.begin();
161 it != m_weight_sums.end();
162 ++it) {
163 if (rand_val < it->second) {
164 retval = gen_op(context, it->first);
165 break;
166 }
167 }
168 }
169 return retval;
170 }
171
172 private:
173
174 TestOp *gen_op(RadosTestContext &context, TestOpType type)
175 {
176 string oid, oid2;
177 assert(context.oid_not_in_use.size());
178
179 switch (type) {
180 case TEST_OP_READ:
181 oid = *(rand_choose(context.oid_not_in_use));
182 return new ReadOp(m_op, &context, oid, m_balance_reads, m_stats);
183
184 case TEST_OP_WRITE:
185 oid = *(rand_choose(context.oid_not_in_use));
186 cout << m_op << ": " << "write oid " << oid << " current snap is "
187 << context.current_snap << std::endl;
188 return new WriteOp(m_op, &context, oid, false, false, m_stats);
189
190 case TEST_OP_WRITE_EXCL:
191 oid = *(rand_choose(context.oid_not_in_use));
192 cout << m_op << ": " << "write (excl) oid "
193 << oid << " current snap is "
194 << context.current_snap << std::endl;
195 return new WriteOp(m_op, &context, oid, false, true, m_stats);
196
197 case TEST_OP_WRITESAME:
198 oid = *(rand_choose(context.oid_not_in_use));
199 cout << m_op << ": " << "writesame oid "
200 << oid << " current snap is "
201 << context.current_snap << std::endl;
202 return new WriteSameOp(m_op, &context, oid, m_stats);
203
204 case TEST_OP_DELETE:
205 oid = *(rand_choose(context.oid_not_in_use));
206 cout << m_op << ": " << "delete oid " << oid << " current snap is "
207 << context.current_snap << std::endl;
208 return new DeleteOp(m_op, &context, oid, m_stats);
209
210 case TEST_OP_SNAP_CREATE:
211 cout << m_op << ": " << "snap_create" << std::endl;
212 return new SnapCreateOp(m_op, &context, m_stats);
213
214 case TEST_OP_SNAP_REMOVE:
215 if (context.snaps.size() <= context.snaps_in_use.size()) {
216 return NULL;
217 }
218 while (true) {
219 int snap = rand_choose(context.snaps)->first;
220 if (context.snaps_in_use.lookup(snap))
221 continue; // in use; try again!
222 cout << m_op << ": " << "snap_remove snap " << snap << std::endl;
223 return new SnapRemoveOp(m_op, &context, snap, m_stats);
224 }
225
226 case TEST_OP_ROLLBACK:
227 {
228 string oid = *(rand_choose(context.oid_not_in_use));
229 cout << m_op << ": " << "rollback oid " << oid << " current snap is "
230 << context.current_snap << std::endl;
231 return new RollbackOp(m_op, &context, oid);
232 }
233
234 case TEST_OP_SETATTR:
235 oid = *(rand_choose(context.oid_not_in_use));
236 cout << m_op << ": " << "setattr oid " << oid
237 << " current snap is " << context.current_snap << std::endl;
238 return new SetAttrsOp(m_op, &context, oid, m_stats);
239
240 case TEST_OP_RMATTR:
241 oid = *(rand_choose(context.oid_not_in_use));
242 cout << m_op << ": " << "rmattr oid " << oid
243 << " current snap is " << context.current_snap << std::endl;
244 return new RemoveAttrsOp(m_op, &context, oid, m_stats);
245
246 case TEST_OP_WATCH:
247 oid = *(rand_choose(context.oid_not_in_use));
248 cout << m_op << ": " << "watch oid " << oid
249 << " current snap is " << context.current_snap << std::endl;
250 return new WatchOp(m_op, &context, oid, m_stats);
251
252 case TEST_OP_COPY_FROM:
253 oid = *(rand_choose(context.oid_not_in_use));
254 do {
255 oid2 = *(rand_choose(context.oid_not_in_use));
256 } while (oid == oid2);
257 cout << m_op << ": " << "copy_from oid " << oid << " from oid " << oid2
258 << " current snap is " << context.current_snap << std::endl;
259 return new CopyFromOp(m_op, &context, oid, oid2, m_stats);
260
261 case TEST_OP_HIT_SET_LIST:
262 {
263 uint32_t hash = rjhash32(rand());
264 cout << m_op << ": " << "hit_set_list " << hash << std::endl;
265 return new HitSetListOp(m_op, &context, hash, m_stats);
266 }
267
268 case TEST_OP_UNDIRTY:
269 {
270 oid = *(rand_choose(context.oid_not_in_use));
271 cout << m_op << ": " << "undirty oid " << oid << std::endl;
272 return new UndirtyOp(m_op, &context, oid, m_stats);
273 }
274
275 case TEST_OP_IS_DIRTY:
276 {
277 oid = *(rand_choose(context.oid_not_flushing));
278 return new IsDirtyOp(m_op, &context, oid, m_stats);
279 }
280
281 case TEST_OP_CACHE_FLUSH:
282 {
283 oid = *(rand_choose(context.oid_not_in_use));
284 return new CacheFlushOp(m_op, &context, oid, m_stats, true);
285 }
286
287 case TEST_OP_CACHE_TRY_FLUSH:
288 {
289 oid = *(rand_choose(context.oid_not_in_use));
290 return new CacheFlushOp(m_op, &context, oid, m_stats, false);
291 }
292
293 case TEST_OP_CACHE_EVICT:
294 {
295 oid = *(rand_choose(context.oid_not_in_use));
296 return new CacheEvictOp(m_op, &context, oid, m_stats);
297 }
298
299 case TEST_OP_APPEND:
300 oid = *(rand_choose(context.oid_not_in_use));
301 cout << "append oid " << oid << " current snap is "
302 << context.current_snap << std::endl;
303 return new WriteOp(m_op, &context, oid, true, false, m_stats);
304
305 case TEST_OP_APPEND_EXCL:
306 oid = *(rand_choose(context.oid_not_in_use));
307 cout << "append oid (excl) " << oid << " current snap is "
308 << context.current_snap << std::endl;
309 return new WriteOp(m_op, &context, oid, true, true, m_stats);
310
311 case TEST_OP_SET_REDIRECT:
312 oid = *(rand_choose(context.oid_not_in_use));
313 oid2 = *(rand_choose(context.oid_redirect_not_in_use));
314 cout << m_op << ": " << "set_redirect oid " << oid << " target oid " << oid2 << std::endl;
315 return new SetRedirectOp(m_op, &context, oid, oid2, context.pool_name, m_stats);
316
317 case TEST_OP_UNSET_REDIRECT:
318 oid = *(rand_choose(context.oid_not_in_use));
319 cout << m_op << ": " << "unset_redirect oid " << oid << std::endl;
320 return new UnsetRedirectOp(m_op, &context, oid, m_stats);
321
322 default:
323 cerr << m_op << ": Invalid op type " << type << std::endl;
324 ceph_abort();
325 return nullptr;
326 }
327 }
328
329 TestOp *m_nextop;
330 int m_op;
331 int m_ops;
332 int m_seconds;
333 int m_objects;
334 time_t m_start;
335 TestOpStat *m_stats;
336 map<TestOpType, unsigned int> m_weight_sums;
337 unsigned int m_total_weight;
338 bool m_ec_pool;
339 bool m_balance_reads;
340 bool m_set_redirect;
341 int m_redirect_objects;
342 int m_initial_redirected_objects;
343 };
344
345 int main(int argc, char **argv)
346 {
347 int ops = 1000;
348 int objects = 50;
349 int max_in_flight = 16;
350 int64_t size = 4000000; // 4 MB
351 int64_t min_stride_size = -1, max_stride_size = -1;
352 int max_seconds = 0;
353 bool pool_snaps = false;
354 bool write_fadvise_dontneed = false;
355
356 struct {
357 TestOpType op;
358 const char *name;
359 bool ec_pool_valid;
360 } op_types[] = {
361 { TEST_OP_READ, "read", true },
362 { TEST_OP_WRITE, "write", false },
363 { TEST_OP_WRITE_EXCL, "write_excl", false },
364 { TEST_OP_WRITESAME, "writesame", false },
365 { TEST_OP_DELETE, "delete", true },
366 { TEST_OP_SNAP_CREATE, "snap_create", true },
367 { TEST_OP_SNAP_REMOVE, "snap_remove", true },
368 { TEST_OP_ROLLBACK, "rollback", true },
369 { TEST_OP_SETATTR, "setattr", true },
370 { TEST_OP_RMATTR, "rmattr", true },
371 { TEST_OP_WATCH, "watch", true },
372 { TEST_OP_COPY_FROM, "copy_from", true },
373 { TEST_OP_HIT_SET_LIST, "hit_set_list", true },
374 { TEST_OP_IS_DIRTY, "is_dirty", true },
375 { TEST_OP_UNDIRTY, "undirty", true },
376 { TEST_OP_CACHE_FLUSH, "cache_flush", true },
377 { TEST_OP_CACHE_TRY_FLUSH, "cache_try_flush", true },
378 { TEST_OP_CACHE_EVICT, "cache_evict", true },
379 { TEST_OP_APPEND, "append", true },
380 { TEST_OP_APPEND_EXCL, "append_excl", true },
381 { TEST_OP_SET_REDIRECT, "set_redirect", true },
382 { TEST_OP_UNSET_REDIRECT, "unset_redirect", true },
383 { TEST_OP_READ /* grr */, NULL },
384 };
385
386 map<TestOpType, unsigned int> op_weights;
387 string pool_name = "rbd";
388 bool ec_pool = false;
389 bool no_omap = false;
390 bool no_sparse = false;
391 bool balance_reads = false;
392 bool set_redirect = false;
393
394 for (int i = 1; i < argc; ++i) {
395 if (strcmp(argv[i], "--max-ops") == 0)
396 ops = atoi(argv[++i]);
397 else if (strcmp(argv[i], "--pool") == 0)
398 pool_name = argv[++i];
399 else if (strcmp(argv[i], "--max-seconds") == 0)
400 max_seconds = atoi(argv[++i]);
401 else if (strcmp(argv[i], "--objects") == 0)
402 objects = atoi(argv[++i]);
403 else if (strcmp(argv[i], "--max-in-flight") == 0)
404 max_in_flight = atoi(argv[++i]);
405 else if (strcmp(argv[i], "--size") == 0)
406 size = atoi(argv[++i]);
407 else if (strcmp(argv[i], "--min-stride-size") == 0)
408 min_stride_size = atoi(argv[++i]);
409 else if (strcmp(argv[i], "--max-stride-size") == 0)
410 max_stride_size = atoi(argv[++i]);
411 else if (strcmp(argv[i], "--no-omap") == 0)
412 no_omap = true;
413 else if (strcmp(argv[i], "--no-sparse") == 0)
414 no_sparse = true;
415 else if (strcmp(argv[i], "--balance_reads") == 0)
416 balance_reads = true;
417 else if (strcmp(argv[i], "--pool-snaps") == 0)
418 pool_snaps = true;
419 else if (strcmp(argv[i], "--write-fadvise-dontneed") == 0)
420 write_fadvise_dontneed = true;
421 else if (strcmp(argv[i], "--ec-pool") == 0) {
422 if (!op_weights.empty()) {
423 cerr << "--ec-pool must be specified prior to any ops" << std::endl;
424 exit(1);
425 }
426 ec_pool = true;
427 no_omap = true;
428 no_sparse = true;
429 } else if (strcmp(argv[i], "--op") == 0) {
430 i++;
431 if (i == argc) {
432 cerr << "Missing op after --op" << std::endl;
433 return 1;
434 }
435 int j;
436 for (j = 0; op_types[j].name; ++j) {
437 if (strcmp(op_types[j].name, argv[i]) == 0) {
438 break;
439 }
440 }
441 if (!op_types[j].name) {
442 cerr << "unknown op " << argv[i] << std::endl;
443 exit(1);
444 }
445 i++;
446 if (i == argc) {
447 cerr << "Weight unspecified." << std::endl;
448 return 1;
449 }
450 int weight = atoi(argv[i]);
451 if (weight < 0) {
452 cerr << "Weights must be nonnegative." << std::endl;
453 return 1;
454 } else if (weight > 0) {
455 if (ec_pool && !op_types[j].ec_pool_valid) {
456 cerr << "Error: cannot use op type " << op_types[j].name
457 << " with --ec-pool" << std::endl;
458 exit(1);
459 }
460 cout << "adding op weight " << op_types[j].name << " -> " << weight << std::endl;
461 op_weights.insert(pair<TestOpType, unsigned int>(op_types[j].op, weight));
462 }
463 } else if (strcmp(argv[i], "--set_redirect") == 0) {
464 set_redirect = true;
465 } else {
466 cerr << "unknown arg " << argv[i] << std::endl;
467 exit(1);
468 }
469 }
470
471 if (op_weights.empty()) {
472 cerr << "No operations specified" << std::endl;
473 exit(1);
474 }
475
476 if (min_stride_size < 0)
477 min_stride_size = size / 10;
478 if (max_stride_size < 0)
479 max_stride_size = size / 5;
480
481 cout << pretty_version_to_str() << std::endl;
482 cout << "Configuration:" << std::endl
483 << "\tNumber of operations: " << ops << std::endl
484 << "\tNumber of objects: " << objects << std::endl
485 << "\tMax in flight operations: " << max_in_flight << std::endl
486 << "\tObject size (in bytes): " << size << std::endl
487 << "\tWrite stride min: " << min_stride_size << std::endl
488 << "\tWrite stride max: " << max_stride_size << std::endl;
489
490 if (min_stride_size > max_stride_size) {
491 cerr << "Error: min_stride_size cannot be more than max_stride_size"
492 << std::endl;
493 return 1;
494 }
495
496 if (min_stride_size > size || max_stride_size > size) {
497 cerr << "Error: min_stride_size and max_stride_size must be "
498 << "smaller than object size" << std::endl;
499 return 1;
500 }
501
502 if (max_in_flight * 2 > objects) {
503 cerr << "Error: max_in_flight must be <= than the number of objects / 2"
504 << std::endl;
505 return 1;
506 }
507
508 char *id = getenv("CEPH_CLIENT_ID");
509 RadosTestContext context(
510 pool_name,
511 max_in_flight,
512 size,
513 min_stride_size,
514 max_stride_size,
515 no_omap,
516 no_sparse,
517 pool_snaps,
518 write_fadvise_dontneed,
519 id);
520
521 TestOpStat stats;
522 WeightedTestGenerator gen = WeightedTestGenerator(
523 ops, objects,
524 op_weights, &stats, max_seconds,
525 ec_pool, balance_reads, set_redirect);
526 int r = context.init();
527 if (r < 0) {
528 cerr << "Error initializing rados test context: "
529 << cpp_strerror(r) << std::endl;
530 exit(1);
531 }
532 context.loop(&gen);
533
534 context.shutdown();
535 cerr << context.errors << " errors." << std::endl;
536 cerr << stats << std::endl;
537 return 0;
538 }