-remove trailing whitespace
[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 "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 putValue (struct GNUNET_DATASTORE_PluginFunctions *api, int i, int k)
103 {
104   char value[65536];
105   size_t size;
106   static struct 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_us;
119   GNUNET_CRYPTO_hash (&key, sizeof (struct 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, &key, size, value, 1 + i % 4 /* type */ ,
128                              prio, i % 4 /* anonymity */ ,
129                              0 /* replication */ ,
130                              GNUNET_TIME_relative_to_absolute
131                              (GNUNET_TIME_relative_multiply
132                               (GNUNET_TIME_UNIT_MILLISECONDS,
133                                60 * 60 * 60 * 1000 +
134                                GNUNET_CRYPTO_random_u32
135                                (GNUNET_CRYPTO_QUALITY_WEAK, 1000))), &msg))
136   {
137     FPRINTF (stderr, "ERROR: `%s'\n", msg);
138     GNUNET_free_non_null (msg);
139     return;
140   }
141   ic++;
142   stored_bytes += size;
143   stored_ops++;
144   stored_entries++;
145 }
146
147 static void
148 test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
149
150
151 static int
152 iterate_zeros (void *cls, const struct GNUNET_HashCode * key, uint32_t size,
153                const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
154                uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
155                uint64_t uid)
156 {
157   struct CpsRunContext *crc = cls;
158   int i;
159   const char *cdata = data;
160
161   GNUNET_assert (key != NULL);
162   GNUNET_assert (size >= 8);
163   memcpy (&i, &cdata[4], sizeof (i));
164   hits[i / 8] |= (1 << (i % 8));
165
166   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
167               "Found result %d type=%u, priority=%u, size=%u, expire=%s\n",
168               i,
169               type, priority, size,
170               GNUNET_STRINGS_absolute_time_to_string (expiration));
171   crc->cnt++;
172   if (crc->cnt == PUT_10 / 4 - 1)
173   {
174     unsigned int bc;
175
176     bc = 0;
177     for (i = 0; i < PUT_10; i++)
178       if (0 != (hits[i / 8] & (1 << (i % 8))))
179         bc++;
180
181     crc->end = GNUNET_TIME_absolute_get ();
182     printf ("%s took %s yielding %u/%u items\n",
183             "Select random zero-anonymity item",
184             GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_difference (crc->start,
185                                                                                          crc->end),
186                                                     GNUNET_YES),
187             bc, crc->cnt);
188     if (crc->cnt > 0)
189       GAUGER (category, "Select random zero-anonymity item",
190               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / crc->cnt,
191               "ms/item");
192     memset (hits, 0, sizeof (hits));
193     crc->phase++;
194     crc->cnt = 0;
195     crc->start = GNUNET_TIME_absolute_get ();
196   }
197   GNUNET_SCHEDULER_add_now (&test, crc);
198   return GNUNET_OK;
199 }
200
201
202 static int
203 expiration_get (void *cls, const struct GNUNET_HashCode * key, uint32_t size,
204                 const void *data, enum GNUNET_BLOCK_Type type,
205                 uint32_t priority, uint32_t anonymity,
206                 struct GNUNET_TIME_Absolute expiration, uint64_t uid)
207 {
208   struct CpsRunContext *crc = cls;
209   int i;
210   const char *cdata = data;
211
212   GNUNET_assert (size >= 8);
213   memcpy (&i, &cdata[4], sizeof (i));
214   hits[i / 8] |= (1 << (i % 8));
215   crc->cnt++;
216   if (PUT_10 <= crc->cnt)
217   {
218     unsigned int bc;
219
220     bc = 0;
221     for (i = 0; i < PUT_10; i++)
222       if (0 != (hits[i / 8] & (1 << (i % 8))))
223         bc++;
224
225     crc->end = GNUNET_TIME_absolute_get ();
226     printf ("%s took %s yielding %u/%u items\n",
227             "Selecting and deleting by expiration",
228             GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_difference (crc->start,
229                                                                                          crc->end),
230                                                     GNUNET_YES),
231             bc, (unsigned int) PUT_10);
232     if (crc->cnt > 0)
233       GAUGER (category, "Selecting and deleting by expiration",
234               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / crc->cnt,
235               "ms/item");
236     memset (hits, 0, sizeof (hits));
237     if (++crc->iter == ITERATIONS)
238       crc->phase++;
239     else
240       crc->phase = RP_PUT;
241     crc->cnt = 0;
242     crc->start = GNUNET_TIME_absolute_get ();
243   }
244   GNUNET_SCHEDULER_add_now (&test, crc);
245   return GNUNET_NO;
246 }
247
248
249 static int
250 replication_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 (NULL != key);
260   GNUNET_assert (size >= 8);
261   memcpy (&i, &cdata[4], sizeof (i));
262   hits[i / 8] |= (1 << (i % 8));
263   crc->cnt++;
264   if (PUT_10 <= crc->cnt)
265   {
266     unsigned int bc;
267
268     bc = 0;
269     for (i = 0; i < PUT_10; i++)
270       if (0 != (hits[i / 8] & (1 << (i % 8))))
271         bc++;
272
273     crc->end = GNUNET_TIME_absolute_get ();
274     printf ("%s took %s yielding %u/%u items\n",
275             "Selecting random item for replication",
276             GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_difference (crc->start,
277                                                                                          crc->end),
278                                                     GNUNET_YES),
279             bc, (unsigned int) PUT_10);
280     if (crc->cnt > 0)
281       GAUGER (category, "Selecting random item for replication",
282               (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / crc->cnt,
283               "ms/item");
284     memset (hits, 0, sizeof (hits));
285     crc->phase++;
286     crc->offset = 0;
287     crc->cnt = 0;
288     crc->start = GNUNET_TIME_absolute_get ();
289   }
290
291   GNUNET_SCHEDULER_add_now (&test, crc);
292   return GNUNET_OK;
293 }
294
295
296 /**
297  * Function called when the service shuts
298  * down.  Unloads our datastore plugin.
299  *
300  * @param api api to unload
301  * @param cfg configuration to use
302  */
303 static void
304 unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
305                const struct GNUNET_CONFIGURATION_Handle *cfg)
306 {
307   char *name;
308   char *libname;
309
310   if (GNUNET_OK !=
311       GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
312                                              &name))
313   {
314     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
315                 _("No `%s' specified for `%s' in configuration!\n"), "DATABASE",
316                 "DATASTORE");
317     return;
318   }
319   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
320   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
321   GNUNET_free (libname);
322   GNUNET_free (name);
323 }
324
325
326
327 /**
328  * Last task run during shutdown.  Disconnects us from
329  * the transport and core.
330  */
331 static void
332 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
333 {
334   struct CpsRunContext *crc = cls;
335
336   unload_plugin (crc->api, crc->cfg);
337   GNUNET_free (crc);
338 }
339
340
341 static void
342 test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
343 {
344   struct CpsRunContext *crc = cls;
345   int j;
346
347   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
348   {
349     GNUNET_break (0);
350     crc->phase = RP_ERROR;
351   }
352   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353               "In phase %d, iteration %u\n", crc->phase, crc->cnt);
354   switch (crc->phase)
355   {
356   case RP_ERROR:
357     GNUNET_break (0);
358     crc->api->drop (crc->api->cls);
359     ok = 1;
360     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
361                                         &cleaning_task, crc);
362     break;
363   case RP_PUT:
364     crc->start = GNUNET_TIME_absolute_get ();
365     for (j = 0; j < PUT_10; j++)
366       putValue (crc->api, j, crc->i);
367     crc->end = GNUNET_TIME_absolute_get ();
368     {
369       printf ("%s took %s for %llu items\n", "Storing an item",
370               GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_difference (crc->start,
371                                                                                            crc->end),
372                                                       GNUNET_YES),
373               PUT_10);
374       if (PUT_10 > 0)
375         GAUGER (category, "Storing an item",
376                 (crc->end.abs_value_us - crc->start.abs_value_us) / 1000LL / PUT_10,
377                 "ms/item");
378     }
379     crc->i++;
380     crc->start = GNUNET_TIME_absolute_get ();
381     crc->phase++;
382     GNUNET_SCHEDULER_add_now (&test, crc);
383     break;
384   case RP_REP_GET:
385     crc->api->get_replication (crc->api->cls, &replication_get, crc);
386     break;
387   case RP_ZA_GET:
388     crc->api->get_zero_anonymity (crc->api->cls, crc->offset++, 1,
389                                   &iterate_zeros, crc);
390     break;
391   case RP_EXP_GET:
392     crc->api->get_expiration (crc->api->cls, &expiration_get, crc);
393     break;
394   case RP_DONE:
395     crc->api->drop (crc->api->cls);
396     ok = 0;
397     GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
398                                         &cleaning_task, crc);
399     break;
400   }
401 }
402
403
404 /**
405  * Load the datastore plugin.
406  */
407 static struct GNUNET_DATASTORE_PluginFunctions *
408 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
409 {
410   static struct GNUNET_DATASTORE_PluginEnvironment env;
411   struct GNUNET_DATASTORE_PluginFunctions *ret;
412   char *name;
413   char *libname;
414
415   if (GNUNET_OK !=
416       GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
417                                              &name))
418   {
419     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
420                 _("No `%s' specified for `%s' in configuration!\n"), "DATABASE",
421                 "DATASTORE");
422     return NULL;
423   }
424   env.cfg = cfg;
425   env.duc = &disk_utilization_change_cb;
426   env.cls = NULL;
427   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' datastore plugin\n"),
428               name);
429   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
430   if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
431   {
432     FPRINTF (stderr, "Failed to load plugin `%s'!\n", name);
433     GNUNET_free (name);
434     GNUNET_free (libname);
435     return NULL;
436   }
437   GNUNET_free (libname);
438   GNUNET_free (name);
439   return ret;
440 }
441
442
443 static void
444 run (void *cls, char *const *args, const char *cfgfile,
445      const struct GNUNET_CONFIGURATION_Handle *c)
446 {
447   struct GNUNET_DATASTORE_PluginFunctions *api;
448   struct CpsRunContext *crc;
449
450   if (NULL == c)
451   {
452     GNUNET_break (0);
453     return;
454   }
455   api = load_plugin (c);
456   if (api == NULL)
457   {
458     FPRINTF (stderr,
459              "%s", "Could not initialize plugin, assuming database not configured. Test not run!\n");
460     return;
461   }
462   crc = GNUNET_malloc (sizeof (struct CpsRunContext));
463   crc->api = api;
464   crc->cfg = c;
465   crc->phase = RP_PUT;
466   ok = 2;
467   GNUNET_SCHEDULER_add_now (&test, crc);
468 }
469
470
471 int
472 main (int argc, char *argv[])
473 {
474   char dir_name[128];
475   char cfg_name[128];
476   char *const xargv[] = {
477     "perf-plugin-datastore",
478     "-c",
479     cfg_name,
480     NULL
481   };
482   struct GNUNET_GETOPT_CommandLineOption options[] = {
483     GNUNET_GETOPT_OPTION_END
484   };
485
486   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
487   GNUNET_snprintf (dir_name, sizeof (dir_name), "/tmp/perf-gnunet-datastore-%s",
488                    plugin_name);
489   GNUNET_DISK_directory_remove (dir_name);
490   GNUNET_log_setup ("perf-plugin-datastore",
491                     "WARNING",
492                     NULL);
493   GNUNET_snprintf (category, sizeof (category), "DATASTORE-%s", plugin_name);
494   GNUNET_snprintf (cfg_name, sizeof (cfg_name),
495                    "perf_plugin_datastore_data_%s.conf", plugin_name);
496   GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv,
497                       "perf-plugin-datastore", "nohelp", options, &run, NULL);
498   if (ok != 0)
499     FPRINTF (stderr, "Missed some testcases: %u\n", ok);
500   GNUNET_DISK_directory_remove (dir_name);
501
502   return ok;
503 }
504
505 /* end of perf_plugin_datastore.c */