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