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