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