improving datastore API --- not working yet
[oweals/gnunet.git] / src / datastore / perf_plugin_datastore.c
1 /*
2      This file is part of GNUnet.
3      (C) 2004, 2005, 2006, 2007, 2009 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
31 #define VERBOSE GNUNET_NO
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 * 128
40
41 #define ITERATIONS 10
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 unsigned long long stored_bytes;
49
50 static unsigned long long stored_entries;
51
52 static unsigned long long stored_ops;
53
54 static const char *plugin_name;
55
56 static int ok;
57
58 enum RunPhase
59   {
60     RP_DONE = 0,
61     RP_PUT,
62     RP_LP_GET,
63     RP_AE_GET,
64     RP_ZA_GET,
65     RP_AN_GET
66   };
67
68
69 struct CpsRunContext
70 {
71   unsigned int i;
72   struct GNUNET_TIME_Absolute start;
73   struct GNUNET_TIME_Absolute end;
74   const struct GNUNET_CONFIGURATION_Handle *cfg;
75   struct GNUNET_DATASTORE_PluginFunctions * api;
76   const char *msg;
77   enum RunPhase phase;
78   unsigned int cnt;
79 };
80
81
82 /**
83  * Function called by plugins to notify us about a
84  * change in their disk utilization.
85  *
86  * @param cls closure (NULL)
87  * @param delta change in disk utilization, 
88  *        0 for "reset to empty"
89  */
90 static void
91 disk_utilization_change_cb (void *cls,
92                             int delta)
93 {
94 }
95
96              
97 static void
98 putValue (struct GNUNET_DATASTORE_PluginFunctions * api, int i, int k)
99 {
100   char value[65536];
101   size_t size;
102   static GNUNET_HashCode key;
103   static int ic;
104   char *msg;
105   unsigned int prio;
106
107   /* most content is 32k */
108   size = 32 * 1024;
109
110   if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) == 0)  /* but some of it is less! */
111     size = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
112   size = size - (size & 7);     /* always multiple of 8 */
113
114   /* generate random key */
115   key.bits[0] = (unsigned int) GNUNET_TIME_absolute_get ().abs_value;
116   GNUNET_CRYPTO_hash (&key, sizeof (GNUNET_HashCode), &key);
117   memset (value, i, size);
118   if (i > 255)
119     memset (value, i - 255, size / 2);
120   value[0] = k;
121   msg = NULL;
122   prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
123   if (GNUNET_OK != api->put (api->cls,
124                              &key, 
125                              size,
126                              value,
127                              i /* type */,
128                              prio,
129                              i /* anonymity */,
130                              0 /* replication */,
131                              GNUNET_TIME_relative_to_absolute 
132                              (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
133                                                              60 * 60 * 60 * 1000 +
134                                                              GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000))),
135                              &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,
149       const struct GNUNET_SCHEDULER_TaskContext *tc);
150
151
152 static int
153 iterateDummy (void *cls,
154               void *next_cls,
155               const GNUNET_HashCode * key,
156               uint32_t size,
157               const void *data,
158               enum GNUNET_BLOCK_Type type,
159               uint32_t priority,
160               uint32_t anonymity,
161               struct GNUNET_TIME_Absolute
162               expiration, 
163               uint64_t uid)
164 {
165   struct CpsRunContext *crc = cls;
166   
167   if (key == NULL)
168     {
169       crc->end = GNUNET_TIME_absolute_get();
170       printf (crc->msg,
171               crc->i,
172               (unsigned long long) (crc->end.abs_value - crc->start.abs_value),
173               crc->cnt);
174       if (crc->phase != RP_AN_GET)
175         {
176           crc->phase++;
177         }
178       else
179         {
180           if (crc->i == ITERATIONS)
181             crc->phase = RP_DONE;
182           else
183             crc->phase = RP_PUT;
184         }
185       crc->cnt = 0;
186       crc->start = GNUNET_TIME_absolute_get ();      
187       GNUNET_SCHEDULER_add_now (&test, crc);
188       return GNUNET_OK;
189     }
190 #if VERBOSE
191   fprintf (stderr, "Found result type=%u, priority=%u, size=%u, expire=%llu\n",
192            type, priority, size,
193            (unsigned long long) expiration.abs_value);
194 #endif
195   crc->cnt++;
196   crc->api->next_request (next_cls,
197                           GNUNET_NO);
198   return GNUNET_OK;
199 }
200
201
202
203 static int
204 dummy_get (void *cls,
205            void *next_cls,
206            const GNUNET_HashCode * key,
207            uint32_t size,
208            const void *data,
209            enum GNUNET_BLOCK_Type type,
210            uint32_t priority,
211            uint32_t anonymity,
212            struct GNUNET_TIME_Absolute
213            expiration, 
214            uint64_t uid)
215 {
216   struct CpsRunContext *crc = cls;
217
218   crc->cnt++;
219   if (1000 == crc->cnt)
220     {
221       crc->end = GNUNET_TIME_absolute_get();
222       printf (crc->msg,
223               crc->i,
224               (unsigned long long) (crc->end.abs_value - crc->start.abs_value),
225               crc->cnt);
226       crc->phase++;
227       crc->cnt = 0;
228       crc->start = GNUNET_TIME_absolute_get ();      
229     }
230   GNUNET_SCHEDULER_add_now (&test, crc);
231   return GNUNET_OK;
232 }
233
234 /**
235  * Function called when the service shuts
236  * down.  Unloads our datastore plugin.
237  *
238  * @param api api to unload
239  * @param cfg configuration to use
240  */
241 static void
242 unload_plugin (struct GNUNET_DATASTORE_PluginFunctions * api,
243                const struct GNUNET_CONFIGURATION_Handle *cfg)
244 {
245   char *name;
246   char *libname;
247
248   if (GNUNET_OK !=
249       GNUNET_CONFIGURATION_get_value_string (cfg,
250                                              "DATASTORE", "DATABASE", &name))
251     {
252       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
253                   _("No `%s' specified for `%s' in configuration!\n"),
254                   "DATABASE",
255                   "DATASTORE");
256       return;
257     }
258   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
259   GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
260   GNUNET_free (libname);
261   GNUNET_free (name);
262 }
263
264
265
266 /**
267  * Last task run during shutdown.  Disconnects us from
268  * the transport and core.
269  */
270 static void
271 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
272 {
273   struct CpsRunContext *crc = cls;
274
275   unload_plugin (crc->api, crc->cfg);
276   GNUNET_free (crc);
277 }
278
279
280 static void
281 test (void *cls,
282       const struct GNUNET_SCHEDULER_TaskContext *tc)
283 {  
284   struct CpsRunContext *crc = cls;
285   int j;
286
287   switch (crc->phase)
288     {
289     case RP_PUT:      
290       crc->start = GNUNET_TIME_absolute_get ();
291       for (j=0;j<PUT_10;j++)
292         putValue (crc->api, j, crc->i);
293       crc->end = GNUNET_TIME_absolute_get ();
294       printf ("%3u insertion took                      %20llums for %u\n",
295               crc->i,
296               (unsigned long long) (crc->end.abs_value - crc->start.abs_value),
297               (unsigned int) PUT_10);
298       crc->i++;
299       crc->start = GNUNET_TIME_absolute_get ();      
300       crc->phase = RP_LP_GET;
301       GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK,
302                                   &test, crc);
303       break;
304     case RP_LP_GET:
305       crc->msg = "%3u replication iteration took %20llums for %u\n";
306       crc->api->replication_get (crc->api->cls, 
307                                  &dummy_get,
308                                  crc);
309       break;
310     case RP_AE_GET:
311       crc->msg = "%3u expiration iteration took %20llums for %u\n";
312       crc->api->expiration_get (crc->api->cls, 
313                                 &dummy_get,
314                                 crc);
315       break;
316     case RP_ZA_GET:
317       crc->msg = "%3u zero anonymity iteration took %20llums for %u\n";
318       crc->api->iter_zero_anonymity (crc->api->cls, 0, 
319                                      &iterateDummy,
320                                      crc);
321       break;
322     case RP_AN_GET:
323       crc->msg = "%3u all now iteration took %20llums for %u\n";
324       crc->api->iter_all_now (crc->api->cls, 0,
325                               &iterateDummy,
326                               crc);
327       break;
328     case RP_DONE:
329       crc->api->drop (crc->api->cls);
330       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
331                                           &cleaning_task, crc);
332       break;
333     }
334 }
335
336
337 /**
338  * Load the datastore plugin.
339  */
340 static struct GNUNET_DATASTORE_PluginFunctions *
341 load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
342 {
343   static struct GNUNET_DATASTORE_PluginEnvironment env;
344   struct GNUNET_DATASTORE_PluginFunctions * ret; 
345   char *name;
346   char *libname;
347
348   if (GNUNET_OK !=
349       GNUNET_CONFIGURATION_get_value_string (cfg,
350                                              "DATASTORE", "DATABASE", &name))
351     {
352       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
353                   _("No `%s' specified for `%s' in configuration!\n"),
354                   "DATABASE",
355                   "DATASTORE");
356       return NULL;
357     }
358   env.cfg = cfg;
359   env.duc = &disk_utilization_change_cb;
360   env.cls = NULL;
361   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
362               _("Loading `%s' datastore plugin\n"), name);
363   GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
364   if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
365     {
366       fprintf (stderr,
367                "Failed to load plugin `%s'!\n",
368                name);
369       return NULL;
370     }
371   GNUNET_free (libname);
372   GNUNET_free (name);
373   return ret;
374 }
375
376
377 static void
378 run (void *cls,
379      char *const *args,
380      const char *cfgfile,
381      const struct GNUNET_CONFIGURATION_Handle *c)
382 {
383   struct GNUNET_DATASTORE_PluginFunctions *api;
384   struct CpsRunContext *crc;
385
386   api = load_plugin (c);
387   if (api == NULL)
388     {
389       fprintf (stderr, 
390                "Could not initialize plugin, assuming database not configured. Test not run!\n");
391       return;
392     }
393   crc = GNUNET_malloc(sizeof(struct CpsRunContext));
394   crc->api = api;
395   crc->cfg = c;
396   crc->phase = RP_PUT;
397   GNUNET_SCHEDULER_add_now (&test, crc);
398 }
399
400
401 static int
402 check ()
403 {
404   char cfg_name[128];
405   char *const argv[] = { 
406     "perf-plugin-datastore",
407     "-c",
408     cfg_name,
409 #if VERBOSE
410     "-L", "DEBUG",
411 #endif
412     NULL
413   };
414   struct GNUNET_GETOPT_CommandLineOption options[] = {
415     GNUNET_GETOPT_OPTION_END
416   };
417
418   GNUNET_snprintf (cfg_name,
419                    sizeof (cfg_name),
420                    "perf_plugin_datastore_data_%s.conf",
421                    plugin_name);
422   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
423                       argv, "perf-plugin-datastore", "nohelp",
424                       options, &run, NULL);
425   if (ok != 0)
426     fprintf (stderr, "Missed some testcases: %u\n", ok);
427   return ok;
428 }
429
430
431 int
432 main (int argc, char *argv[])
433 {
434   int ret;
435   char *pos;
436   char dir_name[128];
437
438   /* determine name of plugin to use */
439   plugin_name = argv[0];
440   while (NULL != (pos = strstr(plugin_name, "_")))
441     plugin_name = pos+1;
442   if (NULL != (pos = strstr(plugin_name, ".")))
443     pos[0] = 0;
444   else
445     pos = (char *) plugin_name;
446
447   GNUNET_snprintf (dir_name,
448                    sizeof (dir_name),
449                    "/tmp/perf-gnunet-datastore-%s",
450                    plugin_name);
451   GNUNET_DISK_directory_remove (dir_name);
452   GNUNET_log_setup ("perf-plugin-datastore",
453 #if VERBOSE
454                     "DEBUG",
455 #else
456                     "WARNING",
457 #endif
458                     NULL);
459   ret = check ();
460   if (pos != plugin_name)
461     pos[0] = '.';
462   GNUNET_DISK_directory_remove (dir_name);
463
464   return ret;
465 }
466
467 /* end of perf_plugin_datastore.c */
468
469