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