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