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