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