first step to remove plibc
[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 (GNUNET_TIME_absolute_get_difference (crc->start,
155                                                                                            crc->end),
156                                                       GNUNET_YES),
157               PUT_10);
158       if (PUT_10 > 0)
159         GAUGER (category, "Storing an item",
160                 (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / PUT_10,
161                 "ms/item");
162     }
163     crc->i++;
164     crc->start = GNUNET_TIME_absolute_get ();
165     crc->phase++;
166     GNUNET_SCHEDULER_add_now (&test, crc);
167     return;
168   }
169   /* most content is 32k */
170   size = 32 * 1024;
171   if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) == 0)   /* but some of it is less! */
172     size = 8 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
173   size = size - (size & 7);     /* always multiple of 8 */
174
175   /* generate random key */
176   key.bits[0] = (unsigned int) GNUNET_TIME_absolute_get ().abs_value_us;
177   GNUNET_CRYPTO_hash (&key, sizeof (struct GNUNET_HashCode), &key);
178   memset (value, i, size);
179   if (i > 255)
180     memset (value, i - 255, size / 2);
181   value[0] = crc->i;
182   GNUNET_memcpy (&value[4], &i, sizeof (i));
183   prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
184   crc->api->put (crc->api->cls,
185                  &key,
186                  false /* absent */,
187                  size,
188                  value,
189                  1 + i % 4 /* type */ ,
190                  prio,
191                  i % 4 /* anonymity */ ,
192                  0 /* replication */ ,
193                  GNUNET_TIME_relative_to_absolute
194                  (GNUNET_TIME_relative_multiply
195                    (GNUNET_TIME_UNIT_MILLISECONDS,
196                     60 * 60 * 60 * 1000 +
197                     GNUNET_CRYPTO_random_u32
198                       (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
199                  put_continuation,
200                  crc);
201   i++;
202 }
203
204
205 static int
206 iterate_zeros (void *cls,
207                const struct GNUNET_HashCode *key,
208                uint32_t size,
209                const void *data,
210                enum GNUNET_BLOCK_Type type,
211                uint32_t priority,
212                uint32_t anonymity,
213                uint32_t replication,
214                struct GNUNET_TIME_Absolute expiration,
215                uint64_t uid)
216 {
217   struct CpsRunContext *crc = cls;
218   int i;
219   const char *cdata = data;
220
221   GNUNET_assert (key != NULL);
222   GNUNET_assert (size >= 8);
223   GNUNET_memcpy (&i, &cdata[4], sizeof (i));
224   hits[i / 8] |= (1 << (i % 8));
225
226   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
227               "Found result %d type=%u, priority=%u, size=%u, expire=%s\n",
228               i,
229               type, priority, size,
230               GNUNET_STRINGS_absolute_time_to_string (expiration));
231   crc->cnt++;
232   if (crc->cnt == PUT_10 / 4 - 1)
233   {
234     unsigned int bc;
235
236     bc = 0;
237     for (i = 0; i < PUT_10; i++)
238       if (0 != (hits[i / 8] & (1 << (i % 8))))
239         bc++;
240
241     crc->end = GNUNET_TIME_absolute_get ();
242     printf ("%s took %s yielding %u/%u items\n",
243             "Select random zero-anonymity item",
244             GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_difference (crc->start,
245                                                                                          crc->end),
246                                                     GNUNET_YES),
247             bc, crc->cnt);
248     if (crc->cnt > 0)
249       GAUGER (category, "Select random zero-anonymity item",
250               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / crc->cnt,
251               "ms/item");
252     memset (hits, 0, sizeof (hits));
253     crc->phase++;
254     crc->cnt = 0;
255     crc->start = GNUNET_TIME_absolute_get ();
256   }
257   GNUNET_SCHEDULER_add_now (&test, crc);
258   return GNUNET_OK;
259 }
260
261
262 static int
263 expiration_get (void *cls,
264                 const struct GNUNET_HashCode *key,
265                 uint32_t size,
266                 const void *data,
267                 enum GNUNET_BLOCK_Type type,
268                 uint32_t priority,
269                 uint32_t anonymity,
270                 uint32_t replication,
271                 struct GNUNET_TIME_Absolute expiration,
272                 uint64_t uid)
273 {
274   struct CpsRunContext *crc = cls;
275   int i;
276   const char *cdata = data;
277
278   GNUNET_assert (size >= 8);
279   GNUNET_memcpy (&i, &cdata[4], sizeof (i));
280   hits[i / 8] |= (1 << (i % 8));
281   crc->cnt++;
282   if (PUT_10 <= crc->cnt)
283   {
284     unsigned int bc;
285
286     bc = 0;
287     for (i = 0; i < PUT_10; i++)
288       if (0 != (hits[i / 8] & (1 << (i % 8))))
289         bc++;
290
291     crc->end = GNUNET_TIME_absolute_get ();
292     printf ("%s took %s yielding %u/%u items\n",
293             "Selecting and deleting by expiration",
294             GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_difference (crc->start,
295                                                                                          crc->end),
296                                                     GNUNET_YES),
297             bc, (unsigned int) PUT_10);
298     if (crc->cnt > 0)
299       GAUGER (category, "Selecting and deleting by expiration",
300               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / crc->cnt,
301               "ms/item");
302     memset (hits, 0, sizeof (hits));
303     if (++crc->iter == ITERATIONS)
304       crc->phase++;
305     else
306       crc->phase = RP_PUT;
307     crc->cnt = 0;
308     crc->start = GNUNET_TIME_absolute_get ();
309   }
310   GNUNET_SCHEDULER_add_now (&test, crc);
311   return GNUNET_NO;
312 }
313
314
315 static int
316 replication_get (void *cls,
317                  const struct GNUNET_HashCode *key,
318                  uint32_t size,
319                  const void *data,
320                  enum GNUNET_BLOCK_Type type,
321                  uint32_t priority,
322                  uint32_t anonymity,
323                  uint32_t replication,
324                  struct GNUNET_TIME_Absolute expiration,
325                  uint64_t uid)
326 {
327   struct CpsRunContext *crc = cls;
328   int i;
329   const char *cdata = data;
330
331   GNUNET_assert (NULL != key);
332   GNUNET_assert (size >= 8);
333   GNUNET_memcpy (&i, &cdata[4], sizeof (i));
334   hits[i / 8] |= (1 << (i % 8));
335   crc->cnt++;
336   if (PUT_10 <= crc->cnt)
337   {
338     unsigned int bc;
339
340     bc = 0;
341     for (i = 0; i < PUT_10; i++)
342       if (0 != (hits[i / 8] & (1 << (i % 8))))
343         bc++;
344
345     crc->end = GNUNET_TIME_absolute_get ();
346     printf ("%s took %s yielding %u/%u items\n",
347             "Selecting random item for replication",
348             GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_difference (crc->start,
349                                                                                          crc->end),
350                                                     GNUNET_YES),
351             bc, (unsigned int) PUT_10);
352     if (crc->cnt > 0)
353       GAUGER (category, "Selecting random item for replication",
354               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / crc->cnt,
355               "ms/item");
356     memset (hits, 0, sizeof (hits));
357     crc->phase++;
358     crc->offset = 0;
359     crc->cnt = 0;
360     crc->start = GNUNET_TIME_absolute_get ();
361   }
362
363   GNUNET_SCHEDULER_add_now (&test, crc);
364   return GNUNET_OK;
365 }
366
367
368 /**
369  * Function called when the service shuts
370  * down.  Unloads our datastore plugin.
371  *
372  * @param api api to unload
373  * @param cfg configuration to use
374  */
375 static void
376 unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
377                const struct GNUNET_CONFIGURATION_Handle *cfg)
378 {
379   char *name;
380   char *libname;
381
382   if (GNUNET_OK !=
383       GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
384                                              &name))
385   {
386     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
387                 _("No `%s' specified for `%s' in configuration!\n"), "DATABASE",
388                 "DATASTORE");
389     return;
390   }
391   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
392   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
393   GNUNET_free (libname);
394   GNUNET_free (name);
395 }
396
397
398
399 /**
400  * Last task run during shutdown.  Disconnects us from
401  * the transport and core.
402  */
403 static void
404 cleaning_task (void *cls)
405 {
406   struct CpsRunContext *crc = cls;
407
408   unload_plugin (crc->api, crc->cfg);
409   GNUNET_free (crc);
410 }
411
412
413 static void
414 test (void *cls)
415 {
416   struct CpsRunContext *crc = cls;
417
418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419               "In phase %d, iteration %u\n", crc->phase, crc->cnt);
420   switch (crc->phase)
421   {
422   case RP_ERROR:
423     GNUNET_break (0);
424     crc->api->drop (crc->api->cls);
425     ok = 1;
426     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
427                                         &cleaning_task, crc);
428     break;
429   case RP_PUT:
430     do_put (crc);
431     break;
432   case RP_REP_GET:
433     crc->api->get_replication (crc->api->cls, &replication_get, crc);
434     break;
435   case RP_ZA_GET:
436     crc->api->get_zero_anonymity (crc->api->cls, crc->offset++, 1,
437                                   &iterate_zeros, crc);
438     break;
439   case RP_EXP_GET:
440     crc->api->get_expiration (crc->api->cls, &expiration_get, crc);
441     break;
442   case RP_DONE:
443     crc->api->drop (crc->api->cls);
444     ok = 0;
445     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
446                                         &cleaning_task, crc);
447     break;
448   }
449 }
450
451
452 /**
453  * Load the datastore plugin.
454  */
455 static struct GNUNET_DATASTORE_PluginFunctions *
456 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
457 {
458   static struct GNUNET_DATASTORE_PluginEnvironment env;
459   struct GNUNET_DATASTORE_PluginFunctions *ret;
460   char *name;
461   char *libname;
462
463   if (GNUNET_OK !=
464       GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
465                                              &name))
466   {
467     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
468                 _("No `%s' specified for `%s' in configuration!\n"), "DATABASE",
469                 "DATASTORE");
470     return NULL;
471   }
472   env.cfg = cfg;
473   env.duc = &disk_utilization_change_cb;
474   env.cls = NULL;
475   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' datastore plugin\n"),
476               name);
477   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
478   if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
479   {
480     fprintf (stderr, "Failed to load plugin `%s'!\n", name);
481     GNUNET_free (name);
482     GNUNET_free (libname);
483     return NULL;
484   }
485   GNUNET_free (libname);
486   GNUNET_free (name);
487   return ret;
488 }
489
490
491 static void
492 run (void *cls, char *const *args, const char *cfgfile,
493      const struct GNUNET_CONFIGURATION_Handle *c)
494 {
495   struct GNUNET_DATASTORE_PluginFunctions *api;
496   struct CpsRunContext *crc;
497
498   if (NULL == c)
499   {
500     GNUNET_break (0);
501     return;
502   }
503   api = load_plugin (c);
504   if (api == NULL)
505   {
506     fprintf (stderr,
507              "%s", "Could not initialize plugin, assuming database not configured. Test not run!\n");
508     return;
509   }
510   crc = GNUNET_new (struct CpsRunContext);
511   crc->api = api;
512   crc->cfg = c;
513   crc->phase = RP_PUT;
514   ok = 2;
515   GNUNET_SCHEDULER_add_now (&test, crc);
516 }
517
518
519 int
520 main (int argc, char *argv[])
521 {
522   char dir_name[PATH_MAX];
523   char cfg_name[PATH_MAX];
524   char *const xargv[] = {
525     "perf-plugin-datastore",
526     "-c",
527     cfg_name,
528     NULL
529   };
530   struct GNUNET_GETOPT_CommandLineOption options[] = {
531     GNUNET_GETOPT_OPTION_END
532   };
533
534   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
535   GNUNET_snprintf (dir_name, sizeof (dir_name), "/tmp/perf-gnunet-datastore-%s",
536                    plugin_name);
537   GNUNET_DISK_directory_remove (dir_name);
538   GNUNET_log_setup ("perf-plugin-datastore",
539                     "WARNING",
540                     NULL);
541   GNUNET_snprintf (category, sizeof (category), "DATASTORE-%s", plugin_name);
542   GNUNET_snprintf (cfg_name, sizeof (cfg_name),
543                    "perf_plugin_datastore_data_%s.conf", plugin_name);
544   GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv,
545                       "perf-plugin-datastore", "nohelp", options, &run, NULL);
546   if (ok != 0)
547     fprintf (stderr, "Missed some testcases: %u\n", ok);
548   GNUNET_DISK_directory_remove (dir_name);
549
550   return ok;
551 }
552
553 /* end of perf_plugin_datastore.c */