glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / namestore / perf_namestore_api_zone_iteration.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2013, 2018 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 /**
16  * @file namestore/perf_namestore_api_zone_iteration.c
17  * @brief testcase for zone iteration functionality: iterate all zones
18  * @author Christian Grothoff
19  */
20 #include "platform.h"
21 #include "gnunet_namestore_service.h"
22 #include "gnunet_testing_lib.h"
23 #include "namestore.h"
24 #include "gnunet_dnsparser_lib.h"
25
26 #define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT
27
28 /**
29  * A #BENCHMARK_SIZE of 1000 takes less than a minute on a reasonably
30  * modern system, so 30 minutes should be OK even for very, very
31  * slow systems.
32  */
33 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
34
35 /**
36  * The runtime of the benchmark is expected to be linear
37  * for the iteration phase with a *good* database.  The FLAT
38  * database uses a quadratic retrieval algorithm,
39  * hence it should be quadratic in the size.
40  */
41 #define BENCHMARK_SIZE 1000
42
43 /**
44  * Maximum record size
45  */
46 #define MAX_REC_SIZE 500
47
48 /**
49  * How big are the blocks we fetch? Note that the first block is
50  * always just 1 record set per current API.  Smaller block
51  * sizes will make quadratic iteration-by-offset penalties
52  * more pronounced.
53  */
54 #define BLOCK_SIZE 100
55
56 static struct GNUNET_NAMESTORE_Handle *nsh;
57
58 static struct GNUNET_SCHEDULER_Task *timeout_task;
59
60 static struct GNUNET_SCHEDULER_Task *t;
61
62 static struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
63
64 static struct GNUNET_NAMESTORE_ZoneIterator *zi;
65
66 static struct GNUNET_NAMESTORE_QueueEntry *qe;
67
68 static int res;
69
70 static unsigned int off;
71
72 static unsigned int left_until_next;
73
74 static uint8_t seen[1 + BENCHMARK_SIZE / 8];
75
76 static struct GNUNET_TIME_Absolute start;
77
78
79 /**
80  * Terminate everything
81  *
82  * @param cls NULL
83  */
84 static void
85 end (void *cls)
86 {
87   (void) cls;
88   if (NULL != qe)
89   {
90     GNUNET_NAMESTORE_cancel (qe);
91     qe = NULL;
92   }
93   if (NULL != zi)
94   {
95     GNUNET_NAMESTORE_zone_iteration_stop (zi);
96     zi = NULL;
97   }
98   if (NULL != nsh)
99   {
100     GNUNET_NAMESTORE_disconnect (nsh);
101     nsh = NULL;
102   }
103   if (NULL != t)
104   {
105     GNUNET_SCHEDULER_cancel (t);
106     t = NULL;
107   }
108   if (NULL != timeout_task)
109   {
110     GNUNET_SCHEDULER_cancel (timeout_task);
111     timeout_task = NULL;
112   }
113   if (NULL != privkey)
114   {
115     GNUNET_free (privkey);
116     privkey = NULL;
117   }
118 }
119
120
121 /**
122  * End with timeout. As this is a benchmark, we do not
123  * fail hard but return "skipped".
124  */
125 static void
126 timeout (void *cls)
127 {
128   (void) cls;
129   timeout_task = NULL;
130   GNUNET_SCHEDULER_shutdown ();
131   res = 77;
132 }
133
134
135 static struct GNUNET_GNSRECORD_Data *
136 create_record (unsigned int count)
137 {
138   struct GNUNET_GNSRECORD_Data *rd;
139
140   rd = GNUNET_malloc (count + sizeof (struct GNUNET_GNSRECORD_Data));
141   rd->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS).abs_value_us;
142   rd->record_type = TEST_RECORD_TYPE;
143   rd->data_size = count;
144   rd->data = (void *) &rd[1];
145   rd->flags = 0;
146   memset (&rd[1],
147           'a',
148           count);
149   return rd;
150 }
151
152
153 static void
154 zone_end (void *cls)
155 {
156   struct GNUNET_TIME_Relative delay;
157
158   zi = NULL;
159   delay = GNUNET_TIME_absolute_get_duration (start);
160   fprintf (stdout,
161            "Iterating over %u records took %s\n",
162            off,
163            GNUNET_STRINGS_relative_time_to_string (delay,
164                                                    GNUNET_YES));
165   if (BENCHMARK_SIZE == off)
166   {
167     res = 0;
168   }
169   else
170   {
171     GNUNET_break (0);
172     res = 1;
173   }
174   GNUNET_SCHEDULER_shutdown ();
175 }
176
177
178 static void
179 fail_cb (void *cls)
180 {
181   zi = NULL;
182   res = 2;
183   GNUNET_break (0);
184   GNUNET_SCHEDULER_shutdown ();
185 }
186
187
188 static void
189 zone_proc (void *cls,
190            const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
191            const char *label,
192            unsigned int rd_count,
193            const struct GNUNET_GNSRECORD_Data *rd)
194 {
195   struct GNUNET_GNSRECORD_Data *wrd;
196   unsigned int xoff;
197
198   GNUNET_assert (NULL != zone);
199   if (1 != sscanf (label,
200                    "l%u",
201                    &xoff))
202   {
203     res = 3;
204     GNUNET_break (0);
205     GNUNET_SCHEDULER_shutdown ();
206     return;
207   }
208   if ( (xoff > BENCHMARK_SIZE) ||
209        (0 != (seen[xoff / 8] & (1U << (xoff % 8)))) )
210   {
211     res = 3;
212     GNUNET_break (0);
213     GNUNET_SCHEDULER_shutdown ();
214     return;
215   }
216   seen[xoff / 8] |= (1U << (xoff % 8));
217   wrd = create_record (xoff % MAX_REC_SIZE);
218   if ( (rd->record_type != wrd->record_type) ||
219        (rd->data_size != wrd->data_size) ||
220        (rd->flags != wrd->flags) )
221   {
222     res = 4;
223     GNUNET_break (0);
224     GNUNET_SCHEDULER_shutdown ();
225     GNUNET_free (wrd);
226     return;
227   }
228   if (0 != memcmp (rd->data,
229                    wrd->data,
230                    wrd->data_size))
231   {
232     res = 4;
233     GNUNET_break (0);
234     GNUNET_SCHEDULER_shutdown ();
235     GNUNET_free (wrd);
236     return;
237   }
238   GNUNET_free (wrd);
239   if (0 != memcmp (zone,
240                    privkey,
241                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
242   {
243     res = 5;
244     GNUNET_break (0);
245     GNUNET_SCHEDULER_shutdown ();
246     return;
247   }
248   off++;
249   left_until_next--;
250   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251               "Obtained record %u, expecting %u more until asking for mor explicitly\n",
252               off,
253               left_until_next);
254   if (0 == left_until_next)
255   {
256     left_until_next = BLOCK_SIZE;
257     GNUNET_NAMESTORE_zone_iterator_next (zi,
258                                          left_until_next);
259   }
260 }
261
262
263 static void
264 publish_record (void *cls);
265
266
267 static void
268 put_cont (void *cls,
269           int32_t success,
270           const char *emsg)
271 {
272   (void) cls;
273   qe = NULL;
274   GNUNET_assert (GNUNET_OK == success);
275   t = GNUNET_SCHEDULER_add_now (&publish_record,
276                                 NULL);
277 }
278
279
280 static void
281 publish_record (void *cls)
282 {
283   struct GNUNET_GNSRECORD_Data *rd;
284   char *label;
285
286   (void) cls;
287   t = NULL;
288   if (BENCHMARK_SIZE == off)
289   {
290     struct GNUNET_TIME_Relative delay;
291
292     delay = GNUNET_TIME_absolute_get_duration (start);
293     fprintf (stdout,
294              "Inserting %u records took %s\n",
295              off,
296              GNUNET_STRINGS_relative_time_to_string (delay,
297                                                      GNUNET_YES));
298     start = GNUNET_TIME_absolute_get ();
299     off = 0;
300     left_until_next = 1;
301     zi = GNUNET_NAMESTORE_zone_iteration_start (nsh,
302                                                 NULL,
303                                                 &fail_cb,
304                                                 NULL,
305                                                 &zone_proc,
306                                                 NULL,
307                                                 &zone_end,
308                                                 NULL);
309     GNUNET_assert (NULL != zi);
310     return;
311   }
312   rd = create_record ((++off) % MAX_REC_SIZE);
313   GNUNET_asprintf (&label,
314                    "l%u",
315                    off);
316   qe = GNUNET_NAMESTORE_records_store (nsh,
317                                        privkey,
318                                        label,
319                                        1, rd,
320                                        &put_cont,
321                                        NULL);
322   GNUNET_free (label);
323   GNUNET_free (rd);
324 }
325
326
327 static void
328 run (void *cls,
329      const struct GNUNET_CONFIGURATION_Handle *cfg,
330      struct GNUNET_TESTING_Peer *peer)
331 {
332   GNUNET_SCHEDULER_add_shutdown (&end,
333                                  NULL);
334   timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
335                                                &timeout,
336                                                NULL);
337   nsh = GNUNET_NAMESTORE_connect (cfg);
338   GNUNET_assert (NULL != nsh);
339   privkey = GNUNET_CRYPTO_ecdsa_key_create ();
340   GNUNET_assert (NULL != privkey);
341   start = GNUNET_TIME_absolute_get ();
342   t = GNUNET_SCHEDULER_add_now (&publish_record,
343                                 NULL);
344 }
345
346
347 int
348 main (int argc,
349       char *argv[])
350 {
351   const char *plugin_name;
352   char *cfg_name;
353
354   plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
355   GNUNET_asprintf (&cfg_name,
356                    "perf_namestore_api_%s.conf",
357                    plugin_name);
358   res = 1;
359   GNUNET_DISK_purge_cfg_dir (cfg_name,
360                              "GNUNET_TEST_HOME");
361   if (0 !=
362       GNUNET_TESTING_peer_run ("perf-namestore-api-zone-iteration",
363                                cfg_name,
364                                &run,
365                                NULL))
366   {
367     res = 1;
368   }
369   GNUNET_DISK_purge_cfg_dir (cfg_name,
370                              "GNUNET_TEST_HOME");
371   GNUNET_free (cfg_name);
372   return res;
373 }
374
375
376 /* end of perf_namestore_api_zone_iteration.c */