error handling
[oweals/gnunet.git] / src / datastore / perf_plugin_datastore.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011 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 perf_plugin_datastore.c
22  * @brief Profile database plugin directly, focusing on iterators.
23  * @author Christian Grothoff
24  */
25
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_datastore_plugin.h"
30 #include "gnunet_testing_lib.h"
31 #include <gauger.h>
32
33 /**
34  * Target datastore size (in bytes).  Realistic sizes are
35  * more like 16 GB (not the default of 16 MB); however,
36  * those take too long to run them in the usual "make check"
37  * sequence.  Hence the value used for shipping is tiny.
38  */
39 #define MAX_SIZE 1024LL * 1024 * 16 * 1
40
41 #define ITERATIONS 2
42
43 /**
44  * Number of put operations equivalent to 1/10th of MAX_SIZE
45  */
46 #define PUT_10 (MAX_SIZE / 32 / 1024 / ITERATIONS)
47
48 static char category[256];
49
50 static unsigned int hits[PUT_10 / 8 + 1];
51
52 static unsigned long long stored_bytes;
53
54 static unsigned long long stored_entries;
55
56 static unsigned long long stored_ops;
57
58 static const char *plugin_name;
59
60 static int ok;
61
62 enum RunPhase
63 {
64   RP_ERROR = 0,
65   RP_PUT,
66   RP_REP_GET,
67   RP_ZA_GET,
68   RP_EXP_GET,
69   RP_DONE
70 };
71
72
73 struct CpsRunContext
74 {
75   unsigned int i;
76   struct GNUNET_TIME_Absolute start;
77   struct GNUNET_TIME_Absolute end;
78   const struct GNUNET_CONFIGURATION_Handle *cfg;
79   struct GNUNET_DATASTORE_PluginFunctions *api;
80   enum RunPhase phase;
81   unsigned int cnt;
82   unsigned int iter;
83   uint64_t offset;
84 };
85
86
87 /**
88  * Function called by plugins to notify us about a
89  * change in their disk utilization.
90  *
91  * @param cls closure (NULL)
92  * @param delta change in disk utilization,
93  *        0 for "reset to empty"
94  */
95 static void
96 disk_utilization_change_cb (void *cls, int delta)
97 {
98 }
99
100
101 static void
102 test (void *cls);
103
104
105 /**
106  * Put continuation.
107  *
108  * @param cls closure
109  * @param key key for the item stored
110  * @param size size of the item stored
111  * @param status #GNUNET_OK or #GNUNET_SYSERROR
112  * @param msg error message on error
113  */
114 static void
115 put_continuation (void *cls,
116                   const struct GNUNET_HashCode *key,
117                   uint32_t size,
118                   int status,
119                   const char *msg)
120 {
121   struct CpsRunContext *crc = cls;
122
123   if (GNUNET_OK != status)
124   {
125     fprintf (stderr, "ERROR: `%s'\n", msg);
126   }
127   else
128   {
129     stored_bytes += size;
130     stored_ops++;
131     stored_entries++;
132   }
133   GNUNET_SCHEDULER_add_now (&test, crc);
134 }
135
136
137 static void
138 do_put (struct CpsRunContext *crc)
139 {
140   char value[65536];
141   size_t size;
142   static struct GNUNET_HashCode key;
143   static int i;
144   unsigned int prio;
145
146   if (0 == i)
147     crc->start = GNUNET_TIME_absolute_get ();
148   if (PUT_10 == i)
149   {
150     i = 0;
151     crc->end = GNUNET_TIME_absolute_get ();
152     {
153       printf ("%s took %s for %llu items\n", "Storing an item",
154               GNUNET_STRINGS_relative_time_to_string (
155                 GNUNET_TIME_absolute_get_difference (crc->start,
156                                                      crc
157                                                      ->end),
158                 GNUNET_YES),
159               PUT_10);
160       if (PUT_10 > 0)
161         GAUGER (category, "Storing an item",
162                 (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
163                 / PUT_10,
164                 "ms/item");
165     }
166     crc->i++;
167     crc->start = GNUNET_TIME_absolute_get ();
168     crc->phase++;
169     GNUNET_SCHEDULER_add_now (&test, crc);
170     return;
171   }
172   /* most content is 32k */
173   size = 32 * 1024;
174   if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) == 0)   /* but some of it is less! */
175     size = 8 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
176   size = size - (size & 7);     /* always multiple of 8 */
177
178   /* generate random key */
179   key.bits[0] = (unsigned int) GNUNET_TIME_absolute_get ().abs_value_us;
180   GNUNET_CRYPTO_hash (&key, sizeof(struct GNUNET_HashCode), &key);
181   memset (value, i, size);
182   if (i > 255)
183     memset (value, i - 255, size / 2);
184   value[0] = crc->i;
185   GNUNET_memcpy (&value[4], &i, sizeof(i));
186   prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
187   crc->api->put (crc->api->cls,
188                  &key,
189                  false /* absent */,
190                  size,
191                  value,
192                  1 + i % 4 /* type */,
193                  prio,
194                  i % 4 /* anonymity */,
195                  0 /* replication */,
196                  GNUNET_TIME_relative_to_absolute
197                    (GNUNET_TIME_relative_multiply
198                      (GNUNET_TIME_UNIT_MILLISECONDS,
199                      60 * 60 * 60 * 1000
200                      + GNUNET_CRYPTO_random_u32
201                        (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
202                  put_continuation,
203                  crc);
204   i++;
205 }
206
207
208 static int
209 iterate_zeros (void *cls,
210                const struct GNUNET_HashCode *key,
211                uint32_t size,
212                const void *data,
213                enum GNUNET_BLOCK_Type type,
214                uint32_t priority,
215                uint32_t anonymity,
216                uint32_t replication,
217                struct GNUNET_TIME_Absolute expiration,
218                uint64_t uid)
219 {
220   struct CpsRunContext *crc = cls;
221   int i;
222   const char *cdata = data;
223
224   GNUNET_assert (key != NULL);
225   GNUNET_assert (size >= 8);
226   GNUNET_memcpy (&i, &cdata[4], sizeof(i));
227   hits[i / 8] |= (1 << (i % 8));
228
229   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
230               "Found result %d type=%u, priority=%u, size=%u, expire=%s\n",
231               i,
232               type, priority, size,
233               GNUNET_STRINGS_absolute_time_to_string (expiration));
234   crc->cnt++;
235   if (crc->cnt == PUT_10 / 4 - 1)
236   {
237     unsigned int bc;
238
239     bc = 0;
240     for (i = 0; i < PUT_10; i++)
241       if (0 != (hits[i / 8] & (1 << (i % 8))))
242         bc++;
243
244     crc->end = GNUNET_TIME_absolute_get ();
245     printf ("%s took %s yielding %u/%u items\n",
246             "Select random zero-anonymity item",
247             GNUNET_STRINGS_relative_time_to_string (
248               GNUNET_TIME_absolute_get_difference (crc->start,
249                                                    crc
250                                                    ->end),
251               GNUNET_YES),
252             bc, crc->cnt);
253     if (crc->cnt > 0)
254       GAUGER (category, "Select random zero-anonymity item",
255               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
256               / crc->cnt,
257               "ms/item");
258     memset (hits, 0, sizeof(hits));
259     crc->phase++;
260     crc->cnt = 0;
261     crc->start = GNUNET_TIME_absolute_get ();
262   }
263   GNUNET_SCHEDULER_add_now (&test, crc);
264   return GNUNET_OK;
265 }
266
267
268 static int
269 expiration_get (void *cls,
270                 const struct GNUNET_HashCode *key,
271                 uint32_t size,
272                 const void *data,
273                 enum GNUNET_BLOCK_Type type,
274                 uint32_t priority,
275                 uint32_t anonymity,
276                 uint32_t replication,
277                 struct GNUNET_TIME_Absolute expiration,
278                 uint64_t uid)
279 {
280   struct CpsRunContext *crc = cls;
281   int i;
282   const char *cdata = data;
283
284   GNUNET_assert (size >= 8);
285   GNUNET_memcpy (&i, &cdata[4], sizeof(i));
286   hits[i / 8] |= (1 << (i % 8));
287   crc->cnt++;
288   if (PUT_10 <= crc->cnt)
289   {
290     unsigned int bc;
291
292     bc = 0;
293     for (i = 0; i < PUT_10; i++)
294       if (0 != (hits[i / 8] & (1 << (i % 8))))
295         bc++;
296
297     crc->end = GNUNET_TIME_absolute_get ();
298     printf ("%s took %s yielding %u/%u items\n",
299             "Selecting and deleting by expiration",
300             GNUNET_STRINGS_relative_time_to_string (
301               GNUNET_TIME_absolute_get_difference (crc->start,
302                                                    crc
303                                                    ->end),
304               GNUNET_YES),
305             bc, (unsigned int) PUT_10);
306     if (crc->cnt > 0)
307       GAUGER (category, "Selecting and deleting by expiration",
308               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
309               / crc->cnt,
310               "ms/item");
311     memset (hits, 0, sizeof(hits));
312     if (++crc->iter == ITERATIONS)
313       crc->phase++;
314     else
315       crc->phase = RP_PUT;
316     crc->cnt = 0;
317     crc->start = GNUNET_TIME_absolute_get ();
318   }
319   GNUNET_SCHEDULER_add_now (&test, crc);
320   return GNUNET_NO;
321 }
322
323
324 static int
325 replication_get (void *cls,
326                  const struct GNUNET_HashCode *key,
327                  uint32_t size,
328                  const void *data,
329                  enum GNUNET_BLOCK_Type type,
330                  uint32_t priority,
331                  uint32_t anonymity,
332                  uint32_t replication,
333                  struct GNUNET_TIME_Absolute expiration,
334                  uint64_t uid)
335 {
336   struct CpsRunContext *crc = cls;
337   int i;
338   const char *cdata = data;
339
340   GNUNET_assert (NULL != key);
341   GNUNET_assert (size >= 8);
342   GNUNET_memcpy (&i, &cdata[4], sizeof(i));
343   hits[i / 8] |= (1 << (i % 8));
344   crc->cnt++;
345   if (PUT_10 <= crc->cnt)
346   {
347     unsigned int bc;
348
349     bc = 0;
350     for (i = 0; i < PUT_10; i++)
351       if (0 != (hits[i / 8] & (1 << (i % 8))))
352         bc++;
353
354     crc->end = GNUNET_TIME_absolute_get ();
355     printf ("%s took %s yielding %u/%u items\n",
356             "Selecting random item for replication",
357             GNUNET_STRINGS_relative_time_to_string (
358               GNUNET_TIME_absolute_get_difference (crc->start,
359                                                    crc
360                                                    ->end),
361               GNUNET_YES),
362             bc, (unsigned int) PUT_10);
363     if (crc->cnt > 0)
364       GAUGER (category, "Selecting random item for replication",
365               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL
366               / crc->cnt,
367               "ms/item");
368     memset (hits, 0, sizeof(hits));
369     crc->phase++;
370     crc->offset = 0;
371     crc->cnt = 0;
372     crc->start = GNUNET_TIME_absolute_get ();
373   }
374
375   GNUNET_SCHEDULER_add_now (&test, crc);
376   return GNUNET_OK;
377 }
378
379
380 /**
381  * Function called when the service shuts
382  * down.  Unloads our datastore plugin.
383  *
384  * @param api api to unload
385  * @param cfg configuration to use
386  */
387 static void
388 unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
389                const struct GNUNET_CONFIGURATION_Handle *cfg)
390 {
391   char *name;
392   char *libname;
393
394   if (GNUNET_OK !=
395       GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
396                                              &name))
397   {
398     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
399                 _ ("No `%s' specified for `%s' in configuration!\n"),
400                 "DATABASE",
401                 "DATASTORE");
402     return;
403   }
404   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
405   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
406   GNUNET_free (libname);
407   GNUNET_free (name);
408 }
409
410
411 /**
412  * Last task run during shutdown.  Disconnects us from
413  * the transport and core.
414  */
415 static void
416 cleaning_task (void *cls)
417 {
418   struct CpsRunContext *crc = cls;
419
420   unload_plugin (crc->api, crc->cfg);
421   GNUNET_free (crc);
422 }
423
424
425 static void
426 test (void *cls)
427 {
428   struct CpsRunContext *crc = cls;
429
430   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431               "In phase %d, iteration %u\n", crc->phase, crc->cnt);
432   switch (crc->phase)
433   {
434   case RP_ERROR:
435     GNUNET_break (0);
436     crc->api->drop (crc->api->cls);
437     ok = 1;
438     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
439                                         &cleaning_task, crc);
440     break;
441
442   case RP_PUT:
443     do_put (crc);
444     break;
445
446   case RP_REP_GET:
447     crc->api->get_replication (crc->api->cls, &replication_get, crc);
448     break;
449
450   case RP_ZA_GET:
451     crc->api->get_zero_anonymity (crc->api->cls, crc->offset++, 1,
452                                   &iterate_zeros, crc);
453     break;
454
455   case RP_EXP_GET:
456     crc->api->get_expiration (crc->api->cls, &expiration_get, crc);
457     break;
458
459   case RP_DONE:
460     crc->api->drop (crc->api->cls);
461     ok = 0;
462     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
463                                         &cleaning_task, crc);
464     break;
465   }
466 }
467
468
469 /**
470  * Load the datastore plugin.
471  */
472 static struct GNUNET_DATASTORE_PluginFunctions *
473 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
474 {
475   static struct GNUNET_DATASTORE_PluginEnvironment env;
476   struct GNUNET_DATASTORE_PluginFunctions *ret;
477   char *name;
478   char *libname;
479
480   if (GNUNET_OK !=
481       GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
482                                              &name))
483   {
484     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
485                 _ ("No `%s' specified for `%s' in configuration!\n"),
486                 "DATABASE",
487                 "DATASTORE");
488     return NULL;
489   }
490   env.cfg = cfg;
491   env.duc = &disk_utilization_change_cb;
492   env.cls = NULL;
493   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' datastore plugin\n"),
494               name);
495   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
496   if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
497   {
498     fprintf (stderr, "Failed to load plugin `%s'!\n", name);
499     GNUNET_free (name);
500     GNUNET_free (libname);
501     return NULL;
502   }
503   GNUNET_free (libname);
504   GNUNET_free (name);
505   return ret;
506 }
507
508
509 static void
510 run (void *cls, char *const *args, const char *cfgfile,
511      const struct GNUNET_CONFIGURATION_Handle *c)
512 {
513   struct GNUNET_DATASTORE_PluginFunctions *api;
514   struct CpsRunContext *crc;
515
516   if (NULL == c)
517   {
518     GNUNET_break (0);
519     return;
520   }
521   api = load_plugin (c);
522   if (api == NULL)
523   {
524     fprintf (stderr,
525              "%s",
526              "Could not initialize plugin, assuming database not configured. Test not run!\n");
527     return;
528   }
529   crc = GNUNET_new (struct CpsRunContext);
530   crc->api = api;
531   crc->cfg = c;
532   crc->phase = RP_PUT;
533   ok = 2;
534   GNUNET_SCHEDULER_add_now (&test, crc);
535 }
536
537
538 int
539 main (int argc, char *argv[])
540 {
541   char dir_name[PATH_MAX];
542   char cfg_name[PATH_MAX];
543   char *const xargv[] = {
544     "perf-plugin-datastore",
545     "-c",
546     cfg_name,
547     NULL
548   };
549   struct GNUNET_GETOPT_CommandLineOption options[] = {
550     GNUNET_GETOPT_OPTION_END
551   };
552
553   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
554   GNUNET_snprintf (dir_name, sizeof(dir_name), "/tmp/perf-gnunet-datastore-%s",
555                    plugin_name);
556   GNUNET_DISK_directory_remove (dir_name);
557   GNUNET_log_setup ("perf-plugin-datastore",
558                     "WARNING",
559                     NULL);
560   GNUNET_snprintf (category, sizeof(category), "DATASTORE-%s", plugin_name);
561   GNUNET_snprintf (cfg_name, sizeof(cfg_name),
562                    "perf_plugin_datastore_data_%s.conf", plugin_name);
563   GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, xargv,
564                       "perf-plugin-datastore", "nohelp", options, &run, NULL);
565   if (ok != 0)
566     fprintf (stderr, "Missed some testcases: %u\n", ok);
567   GNUNET_DISK_directory_remove (dir_name);
568
569   return ok;
570 }
571
572
573 /* end of perf_plugin_datastore.c */