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