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