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