2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011, 2015 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @file datastore/perf_datastore_api.c
22 * @brief performance measurement for the datastore implementation
23 * @author Christian Grothoff
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.
35 #include "gnunet_util_lib.h"
36 #include "gnunet_protocols.h"
37 #include "gnunet_datastore_service.h"
38 #include "gnunet_testing_lib.h"
42 * How long until we give up on transmitting the message?
44 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
47 * Target datastore size (in bytes).
49 #define MAX_SIZE (1024LL * 1024 * 4)
52 * Report progress outside of major reports? Should probably be #GNUNET_YES if
55 #define REPORT_ID GNUNET_YES
58 * Number of put operations equivalent to 1/3rd of #MAX_SIZE
60 #define PUT_10 MAX_SIZE / 32 / 1024 / 3
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.
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.
74 #define QUOTA_PUTS (MAX_SIZE / 32 / 1024 * 16LL)
78 * Number of bytes stored in the datastore in total.
80 static unsigned long long stored_bytes;
83 * Number of entries stored in the datastore in total.
85 static unsigned long long stored_entries;
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).
92 static unsigned long long stored_ops;
95 * Start time of the benchmark.
97 static struct GNUNET_TIME_Absolute start_time;
100 * Database backend we use.
102 static const char *plugin_name;
105 * Handle to the datastore.
107 static struct GNUNET_DATASTORE_Handle *datastore;
110 * Value we return from #main().
115 * Which phase of the process are we in?
120 * We are done (shutting down normally).
125 * We are adding new entries to the datastore.
130 * We are deleting entries from the datastore.
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).
141 * We are generating a report.
146 * Execution failed with some kind of error.
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".
160 * Execution phase we are in.
165 * Size of the value we are currently storing (during #RP_PUT).
170 * Current iteration counter, we are done with the benchmark
171 * once it hits #ITERATIONS.
176 * Counts the number of items put in the current phase.
177 * Once it hits #PUT_10, we progress tot he #RP_CUT phase
178 * or are done if @e i reaches #ITERATIONS.
185 * Main state machine. Executes the next step of the benchmark
186 * depending on the current state.
188 * @param cls the `struct CpsRunContext`
189 * @param tc scheduler context (unused)
192 run_continuation (void *cls,
193 const struct GNUNET_SCHEDULER_TaskContext *tc);
197 * Continuation called to notify client about result of the insertion
198 * operation. Checks for errors, updates our iteration counters and
199 * continues execution with #run_continuation().
201 * @param cls the `struct CpsRunContext`
202 * @param success #GNUNET_SYSERR on failure
203 * @param min_expiration minimum expiration time required for content to be stored
204 * by the datacache at this time, zero for unknown
205 * @param msg NULL on success, otherwise an error message
208 check_success (void *cls,
210 struct GNUNET_TIME_Absolute min_expiration,
213 struct CpsRunContext *crc = cls;
216 FPRINTF (stderr, "%s", (GNUNET_OK == success) ? "I" : "i");
218 if (GNUNET_OK != success)
220 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
221 "Check success failed: `%s'\n",
223 crc->phase = RP_ERROR;
224 GNUNET_SCHEDULER_add_now (&run_continuation,
228 stored_bytes += crc->size;
235 if (crc->j >= PUT_10)
239 if (crc->i == ITERATIONS)
240 crc->phase = RP_PUT_QUOTA;
246 if (crc->j >= QUOTA_PUTS)
249 crc->phase = RP_DONE;
255 GNUNET_SCHEDULER_add_now (&run_continuation,
261 * Continuation called to notify client about result of the
262 * deletion operation. Checks for errors and continues
263 * execution with #run_continuation().
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
272 remove_next (void *cls,
274 struct GNUNET_TIME_Absolute min_expiration,
277 struct CpsRunContext *crc = cls;
279 if (GNUNET_OK != success)
281 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282 "remove_next failed: `%s'\n",
284 crc->phase = RP_ERROR;
285 GNUNET_SCHEDULER_add_now (&run_continuation,
290 FPRINTF (stderr, "%s", "D");
292 GNUNET_assert (GNUNET_OK == success);
293 GNUNET_SCHEDULER_add_now (&run_continuation,
299 * We have selected a value for deletion, trigger removal.
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 expiration expiration time for the content
309 * @param uid unique identifier for the datum;
310 * maybe 0 if no unique identifier is available
313 delete_value (void *cls,
314 const struct GNUNET_HashCode *key,
317 enum GNUNET_BLOCK_Type type,
320 struct GNUNET_TIME_Absolute expiration,
323 struct CpsRunContext *crc = cls;
325 GNUNET_assert (NULL != key);
327 stored_bytes -= size;
330 if (stored_bytes < MAX_SIZE)
332 GNUNET_assert (NULL !=
333 GNUNET_DATASTORE_remove (datastore,
343 * Main state machine. Executes the next step of the benchmark
344 * depending on the current state.
346 * @param cls the `struct CpsRunContext`
347 * @param tc scheduler context (unused)
350 run_continuation (void *cls,
351 const struct GNUNET_SCHEDULER_TaskContext *tc)
353 struct CpsRunContext *crc = cls;
355 static struct GNUNET_HashCode key;
356 static char data[65536];
359 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
360 crc->phase = RP_ERROR;
361 ok = (int) crc->phase;
367 sizeof (struct GNUNET_HashCode));
368 /* most content is 32k */
371 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
372 16)) /* but some of it is less! */
373 size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
375 crc->size = size = size - (size & 7); /* always multiple of 8 */
376 GNUNET_CRYPTO_hash (&key,
377 sizeof (struct GNUNET_HashCode),
384 (int) (crc->j - 255),
387 GNUNET_assert (NULL !=
388 GNUNET_DATASTORE_put (datastore,
394 GNUNET_CRYPTO_random_u32
395 (GNUNET_CRYPTO_QUALITY_WEAK, 100),
398 GNUNET_TIME_relative_to_absolute
399 (GNUNET_TIME_relative_multiply
400 (GNUNET_TIME_UNIT_SECONDS,
401 GNUNET_CRYPTO_random_u32
402 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
406 &check_success, crc));
409 /* trim down below MAX_SIZE again */
410 GNUNET_assert (NULL !=
411 GNUNET_DATASTORE_get_for_replication (datastore,
422 "Stored %llu kB / %lluk ops / %llu ops/s\n",
423 stored_bytes / 1024, /* used size in k */
424 stored_ops / 1024, /* total operations (in k) */
425 1000LL * 1000LL * stored_ops / (1 +
426 GNUNET_TIME_absolute_get_duration
427 (start_time).rel_value_us));
430 GNUNET_SCHEDULER_add_now (&run_continuation,
436 sizeof (struct GNUNET_HashCode));
437 /* most content is 32k */
440 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
441 16)) /* but some of it is less! */
442 size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
444 crc->size = size = size - (size & 7); /* always multiple of 8 */
445 GNUNET_CRYPTO_hash (&key,
446 sizeof (struct GNUNET_HashCode),
453 (int) (crc->j - 255),
456 GNUNET_assert (NULL !=
457 GNUNET_DATASTORE_put (datastore,
458 0, /* reservation ID */
462 crc->j + 1, /* type */
463 GNUNET_CRYPTO_random_u32
464 (GNUNET_CRYPTO_QUALITY_WEAK,
466 crc->j, /* anonymity */
468 GNUNET_TIME_relative_to_absolute
469 (GNUNET_TIME_relative_multiply
470 (GNUNET_TIME_UNIT_SECONDS,
471 GNUNET_CRYPTO_random_u32
472 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
476 &check_success, crc));
480 GNUNET_snprintf (gstr,
484 if ((crc->i == ITERATIONS) && (stored_ops > 0))
487 "PUT operation duration",
488 GNUNET_TIME_absolute_get_duration (start_time).rel_value_us / 1000LL /
492 "\nPUT performance: %s for %llu operations\n",
493 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (start_time),
497 "PUT performance: %llu ms/operation\n",
498 GNUNET_TIME_absolute_get_duration (start_time).rel_value_us / 1000LL /
501 GNUNET_DATASTORE_disconnect (datastore,
507 GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
518 * Function called with the result of the initial PUT operation. If
519 * the PUT succeeded, we start the actual benchmark loop, otherwise we
520 * bail out with an error.
524 * @param success #GNUNET_SYSERR on failure
525 * @param min_expiration minimum expiration time required for content to be stored
526 * by the datacache at this time, zero for unknown
527 * @param msg NULL on success, otherwise an error message
530 run_tests (void *cls,
532 struct GNUNET_TIME_Absolute min_expiration,
535 struct CpsRunContext *crc = cls;
537 if (success != GNUNET_YES)
540 "Test 'put' operation failed with error `%s' database likely not setup, skipping test.\n",
542 GNUNET_DATASTORE_disconnect (datastore,
547 GNUNET_SCHEDULER_add_now (&run_continuation,
553 * Beginning of the actual execution of the benchmark.
554 * Performs a first test operation (PUT) to verify that
555 * the plugin works at all.
558 * @param cfg configuration to use
559 * @param peer peer handle (unused)
563 const struct GNUNET_CONFIGURATION_Handle *cfg,
564 struct GNUNET_TESTING_Peer *peer)
566 struct CpsRunContext *crc;
567 static struct GNUNET_HashCode zkey;
569 datastore = GNUNET_DATASTORE_connect (cfg);
570 start_time = GNUNET_TIME_absolute_get ();
571 crc = GNUNET_new (struct CpsRunContext);
574 GNUNET_DATASTORE_put (datastore,
578 GNUNET_BLOCK_TYPE_TEST,
580 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS),
587 "Test 'put' operation failed.\n");
595 * Entry point into the test. Determines which configuration / plugin
596 * we are running with based on the name of the binary and starts
599 * @param argc should be 1
600 * @param argv used to determine plugin / configuration name.
601 * @return 0 on success
609 plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
610 GNUNET_snprintf (cfg_name,
612 "test_datastore_api_data_%s.conf",
615 GNUNET_TESTING_peer_run ("perf-gnunet-datastore",
620 FPRINTF (stderr, "%s", "\n");
624 /* end of perf_datastore_api.c */