aboutsummaryrefslogtreecommitdiff
path: root/src/service/datastore/perf_datastore_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/datastore/perf_datastore_api.c')
-rw-r--r--src/service/datastore/perf_datastore_api.c630
1 files changed, 630 insertions, 0 deletions
diff --git a/src/service/datastore/perf_datastore_api.c b/src/service/datastore/perf_datastore_api.c
new file mode 100644
index 000000000..3b5488168
--- /dev/null
+++ b/src/service/datastore/perf_datastore_api.c
@@ -0,0 +1,630 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011, 2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/*
21 * @file datastore/perf_datastore_api.c
22 * @brief performance measurement for the datastore implementation
23 * @author Christian Grothoff
24 *
25 * This testcase inserts a bunch of (variable size) data and then
26 * deletes data until the (reported) database size drops below a given
27 * threshold. This is iterated 10 times, with the actual size of the
28 * content stored and the number of operations performed being printed
29 * for each iteration. The code also prints a "I" for every 40 blocks
30 * inserted and a "D" for every 40 blocks deleted. The deletion
31 * strategy uses the "random" iterator. Priorities and expiration
32 * dates are set using a pseudo-random value within a realistic range.
33 */
34#include "platform.h"
35#include "gnunet_util_lib.h"
36#include "gnunet_protocols.h"
37#include "gnunet_datastore_service.h"
38#include "gnunet_testing_lib.h"
39#include <gauger.h>
40
41/**
42 * How long until we give up on transmitting the message?
43 */
44#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
45
46/**
47 * Target datastore size (in bytes).
48 */
49#define MAX_SIZE (1024LL * 1024 * 4)
50
51/**
52 * Report progress outside of major reports? Should probably be #GNUNET_YES if
53 * size is > 16 MB.
54 */
55#define REPORT_ID GNUNET_YES
56
57/**
58 * Number of put operations equivalent to 1/3rd of #MAX_SIZE
59 */
60#define PUT_10 MAX_SIZE / 32 / 1024 / 3
61
62/**
63 * Total number of iterations (each iteration doing
64 * PUT_10 put operations); we report full status every
65 * 10 iterations. Abort with CTRL-C.
66 */
67#define ITERATIONS 8
68
69/**
70 * Total number of iterations to do to go beyond the quota.
71 * The quota is set to 10 MB or 2.5 times #MAX_SIZE,
72 * so we got 16 times #MAX_SIZE to be sure to hit it a LOT.
73 */
74#define QUOTA_PUTS (MAX_SIZE / 32 / 1024 * 16LL)
75
76
77/**
78 * Number of bytes stored in the datastore in total.
79 */
80static unsigned long long stored_bytes;
81
82/**
83 * Number of entries stored in the datastore in total.
84 */
85static unsigned long long stored_entries;
86
87/**
88 * Number of database operations performed. Inserting
89 * counts as one operation, deleting as two (as deletion
90 * requires selecting a value for deletion first).
91 */
92static unsigned long long stored_ops;
93
94/**
95 * Start time of the benchmark.
96 */
97static struct GNUNET_TIME_Absolute start_time;
98
99/**
100 * Database backend we use.
101 */
102static const char *plugin_name;
103
104/**
105 * Handle to the datastore.
106 */
107static struct GNUNET_DATASTORE_Handle *datastore;
108
109/**
110 * Value we return from #main().
111 */
112static int ok;
113
114/**
115 * Which phase of the process are we in?
116 */
117enum RunPhase
118{
119 /**
120 * We are done (shutting down normally).
121 */
122 RP_DONE = 0,
123
124 /**
125 * We are adding new entries to the datastore.
126 */
127 RP_PUT,
128
129 /**
130 * We are deleting entries from the datastore.
131 */
132 RP_CUT,
133
134 /**
135 * We are putting as much as we can to see how the database performs
136 * when it reaches the quota and has to auto-delete (see #3903).
137 */
138 RP_PUT_QUOTA,
139
140 /**
141 * We are generating a report.
142 */
143 RP_REPORT,
144
145 /**
146 * Execution failed with some kind of error.
147 */
148 RP_ERROR
149};
150
151
152/**
153 * Closure we give to all of the functions executing the
154 * benchmark. Could right now be global, but this allows
155 * us to theoretically run multiple clients "in parallel".
156 */
157struct CpsRunContext
158{
159 /**
160 * Execution phase we are in.
161 */
162 enum RunPhase phase;
163
164 /**
165 * Size of the value we are currently storing (during #RP_PUT).
166 */
167 size_t size;
168
169 /**
170 * Current iteration counter, we are done with the benchmark
171 * once it hits #ITERATIONS.
172 */
173 unsigned int i;
174
175 /**
176 * Counts the number of items put in the current phase.
177 * Once it hits #PUT_10, we progress to the #RP_CUT phase
178 * or are done if @e i reaches #ITERATIONS.
179 */
180 unsigned int j;
181};
182
183
184/**
185 * Main state machine. Executes the next step of the benchmark
186 * depending on the current state.
187 *
188 * @param cls the `struct CpsRunContext`
189 */
190static void
191run_continuation (void *cls);
192
193
194/**
195 * Continuation called to notify client about result of the insertion
196 * operation. Checks for errors, updates our iteration counters and
197 * continues execution with #run_continuation().
198 *
199 * @param cls the `struct CpsRunContext`
200 * @param success #GNUNET_SYSERR on failure
201 * @param min_expiration minimum expiration time required for content to be stored
202 * by the datacache at this time, zero for unknown
203 * @param msg NULL on success, otherwise an error message
204 */
205static void
206check_success (void *cls,
207 int success,
208 struct GNUNET_TIME_Absolute min_expiration,
209 const char *msg)
210{
211 struct CpsRunContext *crc = cls;
212
213#if REPORT_ID
214 fprintf (stderr, "%s", (GNUNET_OK == success) ? "I" : "i");
215#endif
216 if (GNUNET_OK != success)
217 {
218 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
219 "Check success failed: `%s'\n",
220 msg);
221 crc->phase = RP_ERROR;
222 GNUNET_SCHEDULER_add_now (&run_continuation,
223 crc);
224 return;
225 }
226 stored_bytes += crc->size;
227 stored_ops++;
228 stored_entries++;
229 crc->j++;
230 switch (crc->phase)
231 {
232 case RP_PUT:
233 if (crc->j >= PUT_10)
234 {
235 crc->j = 0;
236 crc->i++;
237 if (crc->i == ITERATIONS)
238 crc->phase = RP_PUT_QUOTA;
239 else
240 crc->phase = RP_CUT;
241 }
242 break;
243
244 case RP_PUT_QUOTA:
245 if (crc->j >= QUOTA_PUTS)
246 {
247 crc->j = 0;
248 crc->phase = RP_DONE;
249 }
250 break;
251
252 default:
253 GNUNET_assert (0);
254 }
255 GNUNET_SCHEDULER_add_now (&run_continuation,
256 crc);
257}
258
259
260/**
261 * Continuation called to notify client about result of the
262 * deletion operation. Checks for errors and continues
263 * execution with #run_continuation().
264 *
265 * @param cls the `struct CpsRunContext`
266 * @param success #GNUNET_SYSERR on failure
267 * @param min_expiration minimum expiration time required for content to be stored
268 * by the datacache at this time, zero for unknown
269 * @param msg NULL on success, otherwise an error message
270 */
271static void
272remove_next (void *cls,
273 int success,
274 struct GNUNET_TIME_Absolute min_expiration,
275 const char *msg)
276{
277 struct CpsRunContext *crc = cls;
278
279 if (GNUNET_OK != success)
280 {
281 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282 "remove_next failed: `%s'\n",
283 msg);
284 crc->phase = RP_ERROR;
285 GNUNET_SCHEDULER_add_now (&run_continuation,
286 crc);
287 return;
288 }
289#if REPORT_ID
290 fprintf (stderr, "%s", "D");
291#endif
292 GNUNET_assert (GNUNET_OK == success);
293 GNUNET_SCHEDULER_add_now (&run_continuation,
294 crc);
295}
296
297
298/**
299 * We have selected a value for deletion, trigger removal.
300 *
301 * @param cls the `struct CpsRunContext`
302 * @param key key for the content
303 * @param size number of bytes in data
304 * @param data content stored
305 * @param type type of the content
306 * @param priority priority of the content
307 * @param anonymity anonymity-level for the content
308 * @param replication replication-level for the content
309 * @param expiration expiration time for the content
310 * @param uid unique identifier for the datum;
311 * maybe 0 if no unique identifier is available
312 */
313static void
314delete_value (void *cls,
315 const struct GNUNET_HashCode *key,
316 size_t size,
317 const void *data,
318 enum GNUNET_BLOCK_Type type,
319 uint32_t priority,
320 uint32_t anonymity,
321 uint32_t replication,
322 struct GNUNET_TIME_Absolute expiration,
323 uint64_t uid)
324{
325 struct CpsRunContext *crc = cls;
326
327 GNUNET_assert (NULL != key);
328 stored_ops++;
329 stored_bytes -= size;
330 stored_entries--;
331 stored_ops++;
332 if (stored_bytes < MAX_SIZE)
333 crc->phase = RP_PUT;
334 GNUNET_assert (NULL !=
335 GNUNET_DATASTORE_remove (datastore,
336 key,
337 size,
338 data, 1, 1,
339 &remove_next, crc));
340}
341
342
343/**
344 * Main state machine. Executes the next step of the benchmark
345 * depending on the current state.
346 *
347 * @param cls the `struct CpsRunContext`
348 */
349static void
350run_continuation (void *cls)
351{
352 struct CpsRunContext *crc = cls;
353 size_t size;
354 static struct GNUNET_HashCode key;
355 static char data[65536];
356 char gstr[128];
357
358 ok = (int) crc->phase;
359 switch (crc->phase)
360 {
361 case RP_PUT:
362 memset (&key,
363 256 - crc->i,
364 sizeof(struct GNUNET_HashCode));
365 /* most content is 32k */
366 size = 32 * 1024;
367 if (0 ==
368 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
369 16)) /* but some of it is less! */
370 size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
371 32 * 1024);
372 crc->size = size = size - (size & 7); /* always multiple of 8 */
373 GNUNET_CRYPTO_hash (&key,
374 sizeof(struct GNUNET_HashCode),
375 &key);
376 memset (data,
377 (int) crc->j,
378 size);
379 if (crc->j > 255)
380 memset (data,
381 (int) (crc->j - 255),
382 size / 2);
383 data[0] = crc->i;
384 GNUNET_assert (NULL !=
385 GNUNET_DATASTORE_put (datastore,
386 0,
387 &key,
388 size,
389 data,
390 crc->j + 1,
391 GNUNET_CRYPTO_random_u32
392 (GNUNET_CRYPTO_QUALITY_WEAK, 100),
393 crc->j,
394 0,
395 GNUNET_TIME_relative_to_absolute
396 (GNUNET_TIME_relative_multiply
397 (GNUNET_TIME_UNIT_SECONDS,
398 GNUNET_CRYPTO_random_u32
399 (GNUNET_CRYPTO_QUALITY_WEAK,
400 1000))),
401 1,
402 1,
403 &check_success, crc));
404 break;
405
406 case RP_CUT:
407 /* trim down below MAX_SIZE again */
408 GNUNET_assert (NULL !=
409 GNUNET_DATASTORE_get_for_replication (datastore,
410 1, 1,
411 &delete_value,
412 crc));
413 break;
414
415 case RP_REPORT:
416 printf (
417#if REPORT_ID
418 "\n"
419#endif
420 "Stored %llu kB / %lluk ops / %llu ops/s\n",
421 stored_bytes / 1024, /* used size in k */
422 stored_ops / 1024, /* total operations (in k) */
423 1000LL * 1000LL * stored_ops / (1
424 + GNUNET_TIME_absolute_get_duration
425 (start_time).rel_value_us));
426 crc->phase = RP_PUT;
427 crc->j = 0;
428 GNUNET_SCHEDULER_add_now (&run_continuation,
429 crc);
430 break;
431
432 case RP_PUT_QUOTA:
433 memset (&key,
434 256 - crc->i,
435 sizeof(struct GNUNET_HashCode));
436 /* most content is 32k */
437 size = 32 * 1024;
438 if (0 ==
439 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
440 16)) /* but some of it is less! */
441 size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
442 32 * 1024);
443 crc->size = size = size - (size & 7); /* always multiple of 8 */
444 GNUNET_CRYPTO_hash (&key,
445 sizeof(struct GNUNET_HashCode),
446 &key);
447 memset (data,
448 (int) crc->j,
449 size);
450 if (crc->j > 255)
451 memset (data,
452 (int) (crc->j - 255),
453 size / 2);
454 data[0] = crc->i;
455 GNUNET_assert (NULL !=
456 GNUNET_DATASTORE_put (datastore,
457 0, /* reservation ID */
458 &key,
459 size,
460 data,
461 crc->j + 1, /* type */
462 GNUNET_CRYPTO_random_u32
463 (GNUNET_CRYPTO_QUALITY_WEAK,
464 100), /* priority */
465 crc->j, /* anonymity */
466 0, /* replication */
467 GNUNET_TIME_relative_to_absolute
468 (GNUNET_TIME_relative_multiply
469 (GNUNET_TIME_UNIT_SECONDS,
470 GNUNET_CRYPTO_random_u32
471 (GNUNET_CRYPTO_QUALITY_WEAK,
472 1000))),
473 1,
474 1,
475 &check_success, crc));
476 break;
477
478 case RP_DONE:
479 GNUNET_snprintf (gstr,
480 sizeof(gstr),
481 "DATASTORE-%s",
482 plugin_name);
483 if ((crc->i == ITERATIONS) && (stored_ops > 0))
484 {
485 GAUGER (gstr,
486 "PUT operation duration",
487 GNUNET_TIME_absolute_get_duration (start_time).rel_value_us
488 / 1000LL
489 / stored_ops,
490 "ms/operation");
491 fprintf (stdout,
492 "\nPUT performance: %s for %llu operations\n",
493 GNUNET_STRINGS_relative_time_to_string (
494 GNUNET_TIME_absolute_get_duration (start_time),
495 GNUNET_YES),
496 stored_ops);
497 fprintf (stdout,
498 "PUT performance: %llu ms/operation\n",
499 GNUNET_TIME_absolute_get_duration (start_time).rel_value_us
500 / 1000LL
501 / stored_ops);
502 }
503 GNUNET_DATASTORE_disconnect (datastore,
504 GNUNET_YES);
505 GNUNET_free (crc);
506 ok = 0;
507 break;
508
509 case RP_ERROR:
510 GNUNET_DATASTORE_disconnect (datastore,
511 GNUNET_YES);
512 GNUNET_free (crc);
513 ok = 1;
514 break;
515
516 default:
517 GNUNET_assert (0);
518 }
519}
520
521
522/**
523 * Function called with the result of the initial PUT operation. If
524 * the PUT succeeded, we start the actual benchmark loop, otherwise we
525 * bail out with an error.
526 *
527 *
528 * @param cls closure
529 * @param success #GNUNET_SYSERR on failure
530 * @param min_expiration minimum expiration time required for content to be stored
531 * by the datacache at this time, zero for unknown
532 * @param msg NULL on success, otherwise an error message
533 */
534static void
535run_tests (void *cls,
536 int success,
537 struct GNUNET_TIME_Absolute min_expiration,
538 const char *msg)
539{
540 struct CpsRunContext *crc = cls;
541
542 if (success != GNUNET_YES)
543 {
544 fprintf (stderr,
545 "Test 'put' operation failed with error `%s' database likely not setup, skipping test.\n",
546 msg);
547 GNUNET_DATASTORE_disconnect (datastore,
548 GNUNET_YES);
549 GNUNET_free (crc);
550 return;
551 }
552 GNUNET_SCHEDULER_add_now (&run_continuation,
553 crc);
554}
555
556
557/**
558 * Beginning of the actual execution of the benchmark.
559 * Performs a first test operation (PUT) to verify that
560 * the plugin works at all.
561 *
562 * @param cls NULL
563 * @param cfg configuration to use
564 * @param peer peer handle (unused)
565 */
566static void
567run (void *cls,
568 const struct GNUNET_CONFIGURATION_Handle *cfg,
569 struct GNUNET_TESTING_Peer *peer)
570{
571 struct CpsRunContext *crc;
572 static struct GNUNET_HashCode zkey;
573
574 datastore = GNUNET_DATASTORE_connect (cfg);
575 start_time = GNUNET_TIME_absolute_get ();
576 crc = GNUNET_new (struct CpsRunContext);
577 crc->phase = RP_PUT;
578 if (NULL ==
579 GNUNET_DATASTORE_put (datastore,
580 0,
581 &zkey,
582 4, "TEST",
583 GNUNET_BLOCK_TYPE_TEST,
584 0, 0, 0,
585 GNUNET_TIME_relative_to_absolute (
586 GNUNET_TIME_UNIT_SECONDS),
587 0, 1,
588 &run_tests, crc))
589 {
590 fprintf (stderr,
591 "%s",
592 "Test 'put' operation failed.\n");
593 ok = 1;
594 GNUNET_free (crc);
595 }
596}
597
598
599/**
600 * Entry point into the test. Determines which configuration / plugin
601 * we are running with based on the name of the binary and starts
602 * the peer.
603 *
604 * @param argc should be 1
605 * @param argv used to determine plugin / configuration name.
606 * @return 0 on success
607 */
608int
609main (int argc,
610 char *argv[])
611{
612 char cfg_name[PATH_MAX];
613
614 plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
615 GNUNET_snprintf (cfg_name,
616 sizeof(cfg_name),
617 "test_datastore_api_data_%s.conf",
618 plugin_name);
619 if (0 !=
620 GNUNET_TESTING_peer_run ("perf-gnunet-datastore",
621 cfg_name,
622 &run,
623 NULL))
624 return 1;
625 fprintf (stderr, "%s", "\n");
626 return ok;
627}
628
629
630/* end of perf_datastore_api.c */