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