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 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.
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.
16 * @file datastore/perf_datastore_api.c
17 * @brief performance measurement for the datastore implementation
18 * @author Christian Grothoff
20 * This testcase inserts a bunch of (variable size) data and then
21 * deletes data until the (reported) database size drops below a given
22 * threshold. This is iterated 10 times, with the actual size of the
23 * content stored and the number of operations performed being printed
24 * for each iteration. The code also prints a "I" for every 40 blocks
25 * inserted and a "D" for every 40 blocks deleted. The deletion
26 * strategy uses the "random" iterator. Priorities and expiration
27 * dates are set using a pseudo-random value within a realistic range.
30 #include "gnunet_util_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_datastore_service.h"
33 #include "gnunet_testing_lib.h"
37 * How long until we give up on transmitting the message?
39 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
42 * Target datastore size (in bytes).
44 #define MAX_SIZE (1024LL * 1024 * 4)
47 * Report progress outside of major reports? Should probably be #GNUNET_YES if
50 #define REPORT_ID GNUNET_YES
53 * Number of put operations equivalent to 1/3rd of #MAX_SIZE
55 #define PUT_10 MAX_SIZE / 32 / 1024 / 3
58 * Total number of iterations (each iteration doing
59 * PUT_10 put operations); we report full status every
60 * 10 iterations. Abort with CTRL-C.
65 * Total number of iterations to do to go beyond the quota.
66 * The quota is set to 10 MB or 2.5 times #MAX_SIZE,
67 * so we got 16 times #MAX_SIZE to be sure to hit it a LOT.
69 #define QUOTA_PUTS (MAX_SIZE / 32 / 1024 * 16LL)
73 * Number of bytes stored in the datastore in total.
75 static unsigned long long stored_bytes;
78 * Number of entries stored in the datastore in total.
80 static unsigned long long stored_entries;
83 * Number of database operations performed. Inserting
84 * counts as one operation, deleting as two (as deletion
85 * requires selecting a value for deletion first).
87 static unsigned long long stored_ops;
90 * Start time of the benchmark.
92 static struct GNUNET_TIME_Absolute start_time;
95 * Database backend we use.
97 static const char *plugin_name;
100 * Handle to the datastore.
102 static struct GNUNET_DATASTORE_Handle *datastore;
105 * Value we return from #main().
110 * Which phase of the process are we in?
115 * We are done (shutting down normally).
120 * We are adding new entries to the datastore.
125 * We are deleting entries from the datastore.
130 * We are putting as much as we can to see how the database performs
131 * when it reaches the quota and has to auto-delete (see #3903).
136 * We are generating a report.
141 * Execution failed with some kind of error.
148 * Closure we give to all of the functions executing the
149 * benchmark. Could right now be global, but this allows
150 * us to theoretically run multiple clients "in parallel".
155 * Execution phase we are in.
160 * Size of the value we are currently storing (during #RP_PUT).
165 * Current iteration counter, we are done with the benchmark
166 * once it hits #ITERATIONS.
171 * Counts the number of items put in the current phase.
172 * Once it hits #PUT_10, we progress tot he #RP_CUT phase
173 * or are done if @e i reaches #ITERATIONS.
180 * Main state machine. Executes the next step of the benchmark
181 * depending on the current state.
183 * @param cls the `struct CpsRunContext`
186 run_continuation (void *cls);
190 * Continuation called to notify client about result of the insertion
191 * operation. Checks for errors, updates our iteration counters and
192 * continues execution with #run_continuation().
194 * @param cls the `struct CpsRunContext`
195 * @param success #GNUNET_SYSERR on failure
196 * @param min_expiration minimum expiration time required for content to be stored
197 * by the datacache at this time, zero for unknown
198 * @param msg NULL on success, otherwise an error message
201 check_success (void *cls,
203 struct GNUNET_TIME_Absolute min_expiration,
206 struct CpsRunContext *crc = cls;
209 FPRINTF (stderr, "%s", (GNUNET_OK == success) ? "I" : "i");
211 if (GNUNET_OK != success)
213 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
214 "Check success failed: `%s'\n",
216 crc->phase = RP_ERROR;
217 GNUNET_SCHEDULER_add_now (&run_continuation,
221 stored_bytes += crc->size;
228 if (crc->j >= PUT_10)
232 if (crc->i == ITERATIONS)
233 crc->phase = RP_PUT_QUOTA;
239 if (crc->j >= QUOTA_PUTS)
242 crc->phase = RP_DONE;
248 GNUNET_SCHEDULER_add_now (&run_continuation,
254 * Continuation called to notify client about result of the
255 * deletion operation. Checks for errors and continues
256 * execution with #run_continuation().
258 * @param cls the `struct CpsRunContext`
259 * @param success #GNUNET_SYSERR on failure
260 * @param min_expiration minimum expiration time required for content to be stored
261 * by the datacache at this time, zero for unknown
262 * @param msg NULL on success, otherwise an error message
265 remove_next (void *cls,
267 struct GNUNET_TIME_Absolute min_expiration,
270 struct CpsRunContext *crc = cls;
272 if (GNUNET_OK != success)
274 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
275 "remove_next failed: `%s'\n",
277 crc->phase = RP_ERROR;
278 GNUNET_SCHEDULER_add_now (&run_continuation,
283 FPRINTF (stderr, "%s", "D");
285 GNUNET_assert (GNUNET_OK == success);
286 GNUNET_SCHEDULER_add_now (&run_continuation,
292 * We have selected a value for deletion, trigger removal.
294 * @param cls the `struct CpsRunContext`
295 * @param key key for the content
296 * @param size number of bytes in data
297 * @param data content stored
298 * @param type type of the content
299 * @param priority priority of the content
300 * @param anonymity anonymity-level for the content
301 * @param replication replication-level for the content
302 * @param expiration expiration time for the content
303 * @param uid unique identifier for the datum;
304 * maybe 0 if no unique identifier is available
307 delete_value (void *cls,
308 const struct GNUNET_HashCode *key,
311 enum GNUNET_BLOCK_Type type,
314 uint32_t replication,
315 struct GNUNET_TIME_Absolute expiration,
318 struct CpsRunContext *crc = cls;
320 GNUNET_assert (NULL != key);
322 stored_bytes -= size;
325 if (stored_bytes < MAX_SIZE)
327 GNUNET_assert (NULL !=
328 GNUNET_DATASTORE_remove (datastore,
337 * Main state machine. Executes the next step of the benchmark
338 * depending on the current state.
340 * @param cls the `struct CpsRunContext`
343 run_continuation (void *cls)
345 struct CpsRunContext *crc = cls;
347 static struct GNUNET_HashCode key;
348 static char data[65536];
351 ok = (int) crc->phase;
357 sizeof (struct GNUNET_HashCode));
358 /* most content is 32k */
361 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
362 16)) /* but some of it is less! */
363 size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
365 crc->size = size = size - (size & 7); /* always multiple of 8 */
366 GNUNET_CRYPTO_hash (&key,
367 sizeof (struct GNUNET_HashCode),
374 (int) (crc->j - 255),
377 GNUNET_assert (NULL !=
378 GNUNET_DATASTORE_put (datastore,
384 GNUNET_CRYPTO_random_u32
385 (GNUNET_CRYPTO_QUALITY_WEAK, 100),
388 GNUNET_TIME_relative_to_absolute
389 (GNUNET_TIME_relative_multiply
390 (GNUNET_TIME_UNIT_SECONDS,
391 GNUNET_CRYPTO_random_u32
392 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
395 &check_success, crc));
398 /* trim down below MAX_SIZE again */
399 GNUNET_assert (NULL !=
400 GNUNET_DATASTORE_get_for_replication (datastore,
410 "Stored %llu kB / %lluk ops / %llu ops/s\n",
411 stored_bytes / 1024, /* used size in k */
412 stored_ops / 1024, /* total operations (in k) */
413 1000LL * 1000LL * stored_ops / (1 +
414 GNUNET_TIME_absolute_get_duration
415 (start_time).rel_value_us));
418 GNUNET_SCHEDULER_add_now (&run_continuation,
424 sizeof (struct GNUNET_HashCode));
425 /* most content is 32k */
428 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
429 16)) /* but some of it is less! */
430 size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
432 crc->size = size = size - (size & 7); /* always multiple of 8 */
433 GNUNET_CRYPTO_hash (&key,
434 sizeof (struct GNUNET_HashCode),
441 (int) (crc->j - 255),
444 GNUNET_assert (NULL !=
445 GNUNET_DATASTORE_put (datastore,
446 0, /* reservation ID */
450 crc->j + 1, /* type */
451 GNUNET_CRYPTO_random_u32
452 (GNUNET_CRYPTO_QUALITY_WEAK,
454 crc->j, /* anonymity */
456 GNUNET_TIME_relative_to_absolute
457 (GNUNET_TIME_relative_multiply
458 (GNUNET_TIME_UNIT_SECONDS,
459 GNUNET_CRYPTO_random_u32
460 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
463 &check_success, crc));
467 GNUNET_snprintf (gstr,
471 if ((crc->i == ITERATIONS) && (stored_ops > 0))
474 "PUT operation duration",
475 GNUNET_TIME_absolute_get_duration (start_time).rel_value_us / 1000LL /
479 "\nPUT performance: %s for %llu operations\n",
480 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (start_time),
484 "PUT performance: %llu ms/operation\n",
485 GNUNET_TIME_absolute_get_duration (start_time).rel_value_us / 1000LL /
488 GNUNET_DATASTORE_disconnect (datastore,
494 GNUNET_DATASTORE_disconnect (datastore, GNUNET_YES);
505 * Function called with the result of the initial PUT operation. If
506 * the PUT succeeded, we start the actual benchmark loop, otherwise we
507 * bail out with an error.
511 * @param success #GNUNET_SYSERR on failure
512 * @param min_expiration minimum expiration time required for content to be stored
513 * by the datacache at this time, zero for unknown
514 * @param msg NULL on success, otherwise an error message
517 run_tests (void *cls,
519 struct GNUNET_TIME_Absolute min_expiration,
522 struct CpsRunContext *crc = cls;
524 if (success != GNUNET_YES)
527 "Test 'put' operation failed with error `%s' database likely not setup, skipping test.\n",
529 GNUNET_DATASTORE_disconnect (datastore,
534 GNUNET_SCHEDULER_add_now (&run_continuation,
540 * Beginning of the actual execution of the benchmark.
541 * Performs a first test operation (PUT) to verify that
542 * the plugin works at all.
545 * @param cfg configuration to use
546 * @param peer peer handle (unused)
550 const struct GNUNET_CONFIGURATION_Handle *cfg,
551 struct GNUNET_TESTING_Peer *peer)
553 struct CpsRunContext *crc;
554 static struct GNUNET_HashCode zkey;
556 datastore = GNUNET_DATASTORE_connect (cfg);
557 start_time = GNUNET_TIME_absolute_get ();
558 crc = GNUNET_new (struct CpsRunContext);
561 GNUNET_DATASTORE_put (datastore,
565 GNUNET_BLOCK_TYPE_TEST,
567 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS),
573 "Test 'put' operation failed.\n");
581 * Entry point into the test. Determines which configuration / plugin
582 * we are running with based on the name of the binary and starts
585 * @param argc should be 1
586 * @param argv used to determine plugin / configuration name.
587 * @return 0 on success
595 plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
596 GNUNET_snprintf (cfg_name,
598 "test_datastore_api_data_%s.conf",
601 GNUNET_TESTING_peer_run ("perf-gnunet-datastore",
606 FPRINTF (stderr, "%s", "\n");
610 /* end of perf_datastore_api.c */