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