]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/osd/TestRados.cc
add subtree-ish sources for 12.0.3
[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 m_nextop(NULL), m_op(0), m_ops(ops), m_seconds(max_seconds),
34 m_objects(objects), m_stats(stats),
35 m_total_weight(0),
36 m_ec_pool(ec_pool),
37 m_balance_reads(balance_reads)
38 {
39 m_start = time(0);
40 for (map<TestOpType, unsigned int>::const_iterator it = op_weights.begin();
41 it != op_weights.end();
42 ++it) {
43 m_total_weight += it->second;
44 m_weight_sums.insert(pair<TestOpType, unsigned int>(it->first,
45 m_total_weight));
46 }
47 }
48
49 TestOp *next(RadosTestContext &context) override
50 {
51 TestOp *retval = NULL;
52
53 ++m_op;
54 if (m_op <= m_objects) {
55 stringstream oid;
56 oid << m_op;
57 if (m_op % 2) {
58 // make it a long name
59 oid << " " << string(300, 'o');
60 }
61 cout << m_op << ": write initial oid " << oid.str() << std::endl;
62 context.oid_not_flushing.insert(oid.str());
63 if (m_ec_pool) {
64 return new WriteOp(m_op, &context, oid.str(), true, true);
65 } else {
66 return new WriteOp(m_op, &context, oid.str(), false, true);
67 }
68 } else if (m_op >= m_ops) {
69 return NULL;
70 }
71
72 if (m_nextop) {
73 retval = m_nextop;
74 m_nextop = NULL;
75 return retval;
76 }
77
78 while (retval == NULL) {
79 unsigned int rand_val = rand() % m_total_weight;
80
81 time_t now = time(0);
82 if (m_seconds && now - m_start > m_seconds)
83 break;
84
85 for (map<TestOpType, unsigned int>::const_iterator it = m_weight_sums.begin();
86 it != m_weight_sums.end();
87 ++it) {
88 if (rand_val < it->second) {
89 retval = gen_op(context, it->first);
90 break;
91 }
92 }
93 }
94 return retval;
95 }
96
97 private:
98
99 TestOp *gen_op(RadosTestContext &context, TestOpType type)
100 {
101 string oid, oid2;
102 //cout << "oids not in use " << context.oid_not_in_use.size() << std::endl;
103 assert(context.oid_not_in_use.size());
104
105 switch (type) {
106 case TEST_OP_READ:
107 oid = *(rand_choose(context.oid_not_in_use));
108 return new ReadOp(m_op, &context, oid, m_balance_reads, m_stats);
109
110 case TEST_OP_WRITE:
111 oid = *(rand_choose(context.oid_not_in_use));
112 cout << m_op << ": " << "write oid " << oid << " current snap is "
113 << context.current_snap << std::endl;
114 return new WriteOp(m_op, &context, oid, false, false, m_stats);
115
116 case TEST_OP_WRITE_EXCL:
117 oid = *(rand_choose(context.oid_not_in_use));
118 cout << m_op << ": " << "write (excl) oid "
119 << oid << " current snap is "
120 << context.current_snap << std::endl;
121 return new WriteOp(m_op, &context, oid, false, true, m_stats);
122
123 case TEST_OP_WRITESAME:
124 oid = *(rand_choose(context.oid_not_in_use));
125 cout << m_op << ": " << "writesame oid "
126 << oid << " current snap is "
127 << context.current_snap << std::endl;
128 return new WriteSameOp(m_op, &context, oid, m_stats);
129
130 case TEST_OP_DELETE:
131 oid = *(rand_choose(context.oid_not_in_use));
132 cout << m_op << ": " << "delete oid " << oid << " current snap is "
133 << context.current_snap << std::endl;
134 return new DeleteOp(m_op, &context, oid, m_stats);
135
136 case TEST_OP_SNAP_CREATE:
137 cout << m_op << ": " << "snap_create" << std::endl;
138 return new SnapCreateOp(m_op, &context, m_stats);
139
140 case TEST_OP_SNAP_REMOVE:
141 if (context.snaps.size() <= context.snaps_in_use.size()) {
142 return NULL;
143 }
144 while (true) {
145 int snap = rand_choose(context.snaps)->first;
146 if (context.snaps_in_use.lookup(snap))
147 continue; // in use; try again!
148 cout << m_op << ": " << "snap_remove snap " << snap << std::endl;
149 return new SnapRemoveOp(m_op, &context, snap, m_stats);
150 }
151
152 case TEST_OP_ROLLBACK:
153 {
154 string oid = *(rand_choose(context.oid_not_in_use));
155 cout << m_op << ": " << "rollback oid " << oid << " current snap is "
156 << context.current_snap << std::endl;
157 return new RollbackOp(m_op, &context, oid);
158 }
159
160 case TEST_OP_SETATTR:
161 oid = *(rand_choose(context.oid_not_in_use));
162 cout << m_op << ": " << "setattr oid " << oid
163 << " current snap is " << context.current_snap << std::endl;
164 return new SetAttrsOp(m_op, &context, oid, m_stats);
165
166 case TEST_OP_RMATTR:
167 oid = *(rand_choose(context.oid_not_in_use));
168 cout << m_op << ": " << "rmattr oid " << oid
169 << " current snap is " << context.current_snap << std::endl;
170 return new RemoveAttrsOp(m_op, &context, oid, m_stats);
171
172 case TEST_OP_WATCH:
173 oid = *(rand_choose(context.oid_not_in_use));
174 cout << m_op << ": " << "watch oid " << oid
175 << " current snap is " << context.current_snap << std::endl;
176 return new WatchOp(m_op, &context, oid, m_stats);
177
178 case TEST_OP_COPY_FROM:
179 oid = *(rand_choose(context.oid_not_in_use));
180 do {
181 oid2 = *(rand_choose(context.oid_not_in_use));
182 } while (oid == oid2);
183 cout << m_op << ": " << "copy_from oid " << oid << " from oid " << oid2
184 << " current snap is " << context.current_snap << std::endl;
185 return new CopyFromOp(m_op, &context, oid, oid2, m_stats);
186
187 case TEST_OP_HIT_SET_LIST:
188 {
189 uint32_t hash = rjhash32(rand());
190 cout << m_op << ": " << "hit_set_list " << hash << std::endl;
191 return new HitSetListOp(m_op, &context, hash, m_stats);
192 }
193
194 case TEST_OP_UNDIRTY:
195 {
196 oid = *(rand_choose(context.oid_not_in_use));
197 cout << m_op << ": " << "undirty oid " << oid << std::endl;
198 return new UndirtyOp(m_op, &context, oid, m_stats);
199 }
200
201 case TEST_OP_IS_DIRTY:
202 {
203 oid = *(rand_choose(context.oid_not_flushing));
204 return new IsDirtyOp(m_op, &context, oid, m_stats);
205 }
206
207 case TEST_OP_CACHE_FLUSH:
208 {
209 oid = *(rand_choose(context.oid_not_in_use));
210 return new CacheFlushOp(m_op, &context, oid, m_stats, true);
211 }
212
213 case TEST_OP_CACHE_TRY_FLUSH:
214 {
215 oid = *(rand_choose(context.oid_not_in_use));
216 return new CacheFlushOp(m_op, &context, oid, m_stats, false);
217 }
218
219 case TEST_OP_CACHE_EVICT:
220 {
221 oid = *(rand_choose(context.oid_not_in_use));
222 return new CacheEvictOp(m_op, &context, oid, m_stats);
223 }
224
225 case TEST_OP_APPEND:
226 oid = *(rand_choose(context.oid_not_in_use));
227 cout << "append oid " << oid << " current snap is "
228 << context.current_snap << std::endl;
229 return new WriteOp(m_op, &context, oid, true, false, m_stats);
230
231 case TEST_OP_APPEND_EXCL:
232 oid = *(rand_choose(context.oid_not_in_use));
233 cout << "append oid (excl) " << oid << " current snap is "
234 << context.current_snap << std::endl;
235 return new WriteOp(m_op, &context, oid, true, true, m_stats);
236
237 default:
238 cerr << m_op << ": Invalid op type " << type << std::endl;
239 ceph_abort();
240 return nullptr;
241 }
242 }
243
244 TestOp *m_nextop;
245 int m_op;
246 int m_ops;
247 int m_seconds;
248 int m_objects;
249 time_t m_start;
250 TestOpStat *m_stats;
251 map<TestOpType, unsigned int> m_weight_sums;
252 unsigned int m_total_weight;
253 bool m_ec_pool;
254 bool m_balance_reads;
255 };
256
257 int main(int argc, char **argv)
258 {
259 int ops = 1000;
260 int objects = 50;
261 int max_in_flight = 16;
262 int64_t size = 4000000; // 4 MB
263 int64_t min_stride_size = -1, max_stride_size = -1;
264 int max_seconds = 0;
265 bool pool_snaps = false;
266 bool write_fadvise_dontneed = false;
267
268 struct {
269 TestOpType op;
270 const char *name;
271 bool ec_pool_valid;
272 } op_types[] = {
273 { TEST_OP_READ, "read", true },
274 { TEST_OP_WRITE, "write", false },
275 { TEST_OP_WRITE_EXCL, "write_excl", false },
276 { TEST_OP_WRITESAME, "writesame", false },
277 { TEST_OP_DELETE, "delete", true },
278 { TEST_OP_SNAP_CREATE, "snap_create", true },
279 { TEST_OP_SNAP_REMOVE, "snap_remove", true },
280 { TEST_OP_ROLLBACK, "rollback", true },
281 { TEST_OP_SETATTR, "setattr", true },
282 { TEST_OP_RMATTR, "rmattr", true },
283 { TEST_OP_WATCH, "watch", true },
284 { TEST_OP_COPY_FROM, "copy_from", true },
285 { TEST_OP_HIT_SET_LIST, "hit_set_list", true },
286 { TEST_OP_IS_DIRTY, "is_dirty", true },
287 { TEST_OP_UNDIRTY, "undirty", true },
288 { TEST_OP_CACHE_FLUSH, "cache_flush", true },
289 { TEST_OP_CACHE_TRY_FLUSH, "cache_try_flush", true },
290 { TEST_OP_CACHE_EVICT, "cache_evict", true },
291 { TEST_OP_APPEND, "append", true },
292 { TEST_OP_APPEND_EXCL, "append_excl", true },
293 { TEST_OP_READ /* grr */, NULL },
294 };
295
296 map<TestOpType, unsigned int> op_weights;
297 string pool_name = "rbd";
298 bool ec_pool = false;
299 bool no_omap = false;
300 bool no_sparse = false;
301 bool balance_reads = false;
302
303 for (int i = 1; i < argc; ++i) {
304 if (strcmp(argv[i], "--max-ops") == 0)
305 ops = atoi(argv[++i]);
306 else if (strcmp(argv[i], "--pool") == 0)
307 pool_name = argv[++i];
308 else if (strcmp(argv[i], "--max-seconds") == 0)
309 max_seconds = atoi(argv[++i]);
310 else if (strcmp(argv[i], "--objects") == 0)
311 objects = atoi(argv[++i]);
312 else if (strcmp(argv[i], "--max-in-flight") == 0)
313 max_in_flight = atoi(argv[++i]);
314 else if (strcmp(argv[i], "--size") == 0)
315 size = atoi(argv[++i]);
316 else if (strcmp(argv[i], "--min-stride-size") == 0)
317 min_stride_size = atoi(argv[++i]);
318 else if (strcmp(argv[i], "--max-stride-size") == 0)
319 max_stride_size = atoi(argv[++i]);
320 else if (strcmp(argv[i], "--no-omap") == 0)
321 no_omap = true;
322 else if (strcmp(argv[i], "--no-sparse") == 0)
323 no_sparse = true;
324 else if (strcmp(argv[i], "--balance_reads") == 0)
325 balance_reads = true;
326 else if (strcmp(argv[i], "--pool-snaps") == 0)
327 pool_snaps = true;
328 else if (strcmp(argv[i], "--write-fadvise-dontneed") == 0)
329 write_fadvise_dontneed = true;
330 else if (strcmp(argv[i], "--ec-pool") == 0) {
331 if (!op_weights.empty()) {
332 cerr << "--ec-pool must be specified prior to any ops" << std::endl;
333 exit(1);
334 }
335 ec_pool = true;
336 no_omap = true;
337 no_sparse = true;
338 } else if (strcmp(argv[i], "--op") == 0) {
339 i++;
340 if (i == argc) {
341 cerr << "Missing op after --op" << std::endl;
342 return 1;
343 }
344 int j;
345 for (j = 0; op_types[j].name; ++j) {
346 if (strcmp(op_types[j].name, argv[i]) == 0) {
347 break;
348 }
349 }
350 if (!op_types[j].name) {
351 cerr << "unknown op " << argv[i] << std::endl;
352 exit(1);
353 }
354 i++;
355 if (i == argc) {
356 cerr << "Weight unspecified." << std::endl;
357 return 1;
358 }
359 int weight = atoi(argv[i]);
360 if (weight < 0) {
361 cerr << "Weights must be nonnegative." << std::endl;
362 return 1;
363 } else if (weight > 0) {
364 if (ec_pool && !op_types[j].ec_pool_valid) {
365 cerr << "Error: cannot use op type " << op_types[j].name
366 << " with --ec-pool" << std::endl;
367 exit(1);
368 }
369 cout << "adding op weight " << op_types[j].name << " -> " << weight << std::endl;
370 op_weights.insert(pair<TestOpType, unsigned int>(op_types[j].op, weight));
371 }
372 } else {
373 cerr << "unknown arg " << argv[i] << std::endl;
374 //usage();
375 exit(1);
376 }
377 }
378
379 if (op_weights.empty()) {
380 cerr << "No operations specified" << std::endl;
381 //usage();
382 exit(1);
383 }
384
385 if (min_stride_size < 0)
386 min_stride_size = size / 10;
387 if (max_stride_size < 0)
388 max_stride_size = size / 5;
389
390 cout << pretty_version_to_str() << std::endl;
391 cout << "Configuration:" << std::endl
392 << "\tNumber of operations: " << ops << std::endl
393 << "\tNumber of objects: " << objects << std::endl
394 << "\tMax in flight operations: " << max_in_flight << std::endl
395 << "\tObject size (in bytes): " << size << std::endl
396 << "\tWrite stride min: " << min_stride_size << std::endl
397 << "\tWrite stride max: " << max_stride_size << std::endl;
398
399 if (min_stride_size > max_stride_size) {
400 cerr << "Error: min_stride_size cannot be more than max_stride_size"
401 << std::endl;
402 return 1;
403 }
404
405 if (min_stride_size > size || max_stride_size > size) {
406 cerr << "Error: min_stride_size and max_stride_size must be "
407 << "smaller than object size" << std::endl;
408 return 1;
409 }
410
411 if (max_in_flight * 2 > objects) {
412 cerr << "Error: max_in_flight must be <= than the number of objects / 2"
413 << std::endl;
414 return 1;
415 }
416
417 char *id = getenv("CEPH_CLIENT_ID");
418 RadosTestContext context(
419 pool_name,
420 max_in_flight,
421 size,
422 min_stride_size,
423 max_stride_size,
424 no_omap,
425 no_sparse,
426 pool_snaps,
427 write_fadvise_dontneed,
428 id);
429
430 TestOpStat stats;
431 WeightedTestGenerator gen = WeightedTestGenerator(
432 ops, objects,
433 op_weights, &stats, max_seconds,
434 ec_pool, balance_reads);
435 int r = context.init();
436 if (r < 0) {
437 cerr << "Error initializing rados test context: "
438 << cpp_strerror(r) << std::endl;
439 exit(1);
440 }
441 context.loop(&gen);
442
443 context.shutdown();
444 cerr << context.errors << " errors." << std::endl;
445 cerr << stats << std::endl;
446 return 0;
447 }