-ensure stats queues do not grow too big
[oweals/gnunet.git] / src / datacache / plugin_datacache_heap.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2012, 2015 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 /**
22  * @file datacache/plugin_datacache_heap.c
23  * @brief heap-only implementation of a database backend for the datacache
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_datacache_plugin.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "datacache-heap", __VA_ARGS__)
31
32 #define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-heap", op, fn)
33
34
35
36 /**
37  * Context for all functions in this plugin.
38  */
39 struct Plugin
40 {
41   /**
42    * Our execution environment.
43    */
44   struct GNUNET_DATACACHE_PluginEnvironment *env;
45
46   /**
47    * Our hash map.
48    */
49   struct GNUNET_CONTAINER_MultiHashMap *map;
50
51   /**
52    * Heap for expirations.
53    */
54   struct GNUNET_CONTAINER_Heap *heap;
55
56 };
57
58
59 /**
60  * Entry in the hash map.
61  */
62 struct Value
63 {
64   /**
65    * Key for the entry.
66    */
67   struct GNUNET_HashCode key;
68
69   /**
70    * Expiration time.
71    */
72   struct GNUNET_TIME_Absolute discard_time;
73
74   /**
75    * Corresponding node in the heap.
76    */
77   struct GNUNET_CONTAINER_HeapNode *hn;
78
79   /**
80    * Path information.
81    */
82   struct GNUNET_PeerIdentity *path_info;
83
84   /**
85    * Payload (actual payload follows this struct)
86    */
87   size_t size;
88
89   /**
90    * Number of entries in @e path_info.
91    */
92   unsigned int path_info_len;
93
94   /**
95    * Type of the block.
96    */
97   enum GNUNET_BLOCK_Type type;
98
99 };
100
101
102 #define OVERHEAD (sizeof (struct Value) + 64)
103
104
105 /**
106  * Closure for #put_cb().
107  */
108 struct PutContext
109 {
110   /**
111    * Expiration time for the new value.
112    */
113   struct GNUNET_TIME_Absolute discard_time;
114
115   /**
116    * Data for the new value.
117    */
118   const char *data;
119
120   /**
121    * Heap from the plugin.
122    */
123   struct GNUNET_CONTAINER_Heap *heap;
124
125   /**
126    * Path information.
127    */
128   const struct GNUNET_PeerIdentity *path_info;
129
130   /**
131    * Number of bytes in @e data.
132    */
133   size_t size;
134
135   /**
136    * Type of the node.
137    */
138   enum GNUNET_BLOCK_Type type;
139
140   /**
141    * Number of entries in @e path_info.
142    */
143   unsigned int path_info_len;
144
145   /**
146    * Value to set to #GNUNET_YES if an equivalent block was found.
147    */
148   int found;
149 };
150
151
152 /**
153  * Function called during PUT to detect if an equivalent block
154  * already exists.
155  *
156  * @param cls the `struct PutContext`
157  * @param key the key for the value(s)
158  * @param value an existing value
159  * @return #GNUNET_YES if not found (to continue to iterate)
160  */
161 static int
162 put_cb (void *cls,
163         const struct GNUNET_HashCode *key,
164         void *value)
165 {
166   struct PutContext *put_ctx = cls;
167   struct Value *val = value;
168
169   if ( (val->size == put_ctx->size) &&
170        (val->type == put_ctx->type) &&
171        (0 == memcmp (&val[1], put_ctx->data, put_ctx->size)) )
172   {
173     put_ctx->found = GNUNET_YES;
174     val->discard_time = GNUNET_TIME_absolute_max (val->discard_time,
175                                                   put_ctx->discard_time);
176     /* replace old path with new path */
177     GNUNET_array_grow (val->path_info,
178                        val->path_info_len,
179                        put_ctx->path_info_len);
180     memcpy (val->path_info,
181             put_ctx->path_info,
182             put_ctx->path_info_len * sizeof (struct GNUNET_PeerIdentity));
183     GNUNET_CONTAINER_heap_update_cost (put_ctx->heap,
184                                        val->hn,
185                                        val->discard_time.abs_value_us);
186     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
187                 "Got same value for key %s and type %d (size %u vs %u)\n",
188                 GNUNET_h2s (key),
189                 val->type,
190                 (unsigned int) val->size,
191                 (unsigned int) put_ctx->size);
192     return GNUNET_NO;
193   }
194   return GNUNET_YES;
195 }
196
197
198 /**
199  * Store an item in the datastore.
200  *
201  * @param cls closure (our `struct Plugin`)
202  * @param key key to store data under
203  * @param size number of bytes in @a data
204  * @param data data to store
205  * @param type type of the value
206  * @param discard_time when to discard the value in any case
207  * @param path_info_len number of entries in @a path_info
208  * @param path_info a path through the network
209  * @return 0 if duplicate, -1 on error, number of bytes used otherwise
210  */
211 static ssize_t
212 heap_plugin_put (void *cls,
213                  const struct GNUNET_HashCode *key,
214                  size_t size,
215                  const char *data,
216                  enum GNUNET_BLOCK_Type type,
217                  struct GNUNET_TIME_Absolute discard_time,
218                  unsigned int path_info_len,
219                  const struct GNUNET_PeerIdentity *path_info)
220 {
221   struct Plugin *plugin = cls;
222   struct Value *val;
223   struct PutContext put_ctx;
224
225   put_ctx.found = GNUNET_NO;
226   put_ctx.heap = plugin->heap;
227   put_ctx.data = data;
228   put_ctx.size = size;
229   put_ctx.path_info = path_info;
230   put_ctx.path_info_len = path_info_len;
231   put_ctx.discard_time = discard_time;
232   put_ctx.type = type;
233   GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
234                                               key,
235                                               &put_cb,
236                                               &put_ctx);
237   if (GNUNET_YES == put_ctx.found)
238     return 0;
239   val = GNUNET_malloc (sizeof (struct Value) + size);
240   memcpy (&val[1], data, size);
241   val->key = *key;
242   val->type = type;
243   val->discard_time = discard_time;
244   val->size = size;
245   GNUNET_array_grow (val->path_info,
246                      val->path_info_len,
247                      path_info_len);
248   memcpy (val->path_info, path_info,
249           path_info_len * sizeof (struct GNUNET_PeerIdentity));
250   (void) GNUNET_CONTAINER_multihashmap_put (plugin->map,
251                                             &val->key,
252                                             val,
253                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
254   val->hn = GNUNET_CONTAINER_heap_insert (plugin->heap,
255                                           val,
256                                           val->discard_time.abs_value_us);
257   return size + OVERHEAD;
258 }
259
260
261 /**
262  * Closure for #get_cb().
263  */
264 struct GetContext
265 {
266   /**
267    * Function to call for each result.
268    */
269   GNUNET_DATACACHE_Iterator iter;
270
271   /**
272    * Closure for @e iter.
273    */
274   void *iter_cls;
275
276   /**
277    * Number of results found.
278    */
279   unsigned int cnt;
280
281   /**
282    * Block type requested.
283    */
284   enum GNUNET_BLOCK_Type type;
285 };
286
287
288
289 /**
290  * Function called during GET to find matching blocks.
291  * Only matches by type.
292  *
293  * @param cls the `struct GetContext`
294  * @param key the key for the value(s)
295  * @param value an existing value
296  * @return #GNUNET_YES to continue to iterate
297  */
298 static int
299 get_cb (void *cls,
300         const struct GNUNET_HashCode *key,
301         void *value)
302 {
303   struct GetContext *get_ctx = cls;
304   struct Value *val = value;
305   int ret;
306
307   if ( (get_ctx->type != val->type) &&
308        (GNUNET_BLOCK_TYPE_ANY != get_ctx->type) )
309     return GNUNET_OK;
310   if (NULL != get_ctx->iter)
311     ret = get_ctx->iter (get_ctx->iter_cls,
312                          key,
313                          val->size,
314                          (const char *) &val[1],
315                          val->type,
316                          val->discard_time,
317                          val->path_info_len,
318                          val->path_info);
319   else
320     ret = GNUNET_YES;
321   get_ctx->cnt++;
322   return ret;
323 }
324
325
326 /**
327  * Iterate over the results for a particular key
328  * in the datastore.
329  *
330  * @param cls closure (our `struct Plugin`)
331  * @param key
332  * @param type entries of which type are relevant?
333  * @param iter maybe NULL (to just count)
334  * @param iter_cls closure for @a iter
335  * @return the number of results found
336  */
337 static unsigned int
338 heap_plugin_get (void *cls,
339                  const struct GNUNET_HashCode *key,
340                  enum GNUNET_BLOCK_Type type,
341                  GNUNET_DATACACHE_Iterator iter,
342                  void *iter_cls)
343 {
344   struct Plugin *plugin = cls;
345   struct GetContext get_ctx;
346
347   get_ctx.type = type;
348   get_ctx.iter = iter;
349   get_ctx.iter_cls = iter_cls;
350   get_ctx.cnt = 0;
351   GNUNET_CONTAINER_multihashmap_get_multiple (plugin->map,
352                                               key,
353                                               &get_cb,
354                                               &get_ctx);
355   return get_ctx.cnt;
356 }
357
358
359 /**
360  * Delete the entry with the lowest expiration value
361  * from the datacache right now.
362  *
363  * @param cls closure (our `struct Plugin`)
364  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
365  */
366 static int
367 heap_plugin_del (void *cls)
368 {
369   struct Plugin *plugin = cls;
370   struct Value *val;
371
372   val = GNUNET_CONTAINER_heap_remove_root (plugin->heap);
373   if (NULL == val)
374     return GNUNET_SYSERR;
375   GNUNET_assert (GNUNET_YES ==
376                  GNUNET_CONTAINER_multihashmap_remove (plugin->map,
377                                                        &val->key,
378                                                        val));
379   plugin->env->delete_notify (plugin->env->cls,
380                               &val->key,
381                               val->size + OVERHEAD);
382   GNUNET_free_non_null (val->path_info);
383   GNUNET_free (val);
384   return GNUNET_OK;
385 }
386
387
388 /**
389  * Return a random value from the datastore.
390  *
391  * @param cls closure (our `struct Plugin`)
392  * @param iter maybe NULL (to just count)
393  * @param iter_cls closure for @a iter
394  * @return the number of results found
395  */
396 static unsigned int
397 heap_plugin_get_random (void *cls,
398                         GNUNET_DATACACHE_Iterator iter,
399                         void *iter_cls)
400 {
401   struct Plugin *plugin = cls;
402   struct GetContext get_ctx;
403
404   get_ctx.type = GNUNET_BLOCK_TYPE_ANY;
405   get_ctx.iter = iter;
406   get_ctx.iter_cls = iter_cls;
407   get_ctx.cnt = 0;
408   GNUNET_CONTAINER_multihashmap_get_random (plugin->map,
409                                             &get_cb,
410                                             &get_ctx);
411   return get_ctx.cnt;
412 }
413
414
415 /**
416  * Iterate over the results that are "close" to a particular key in
417  * the datacache.  "close" is defined as numerically larger than @a
418  * key (when interpreted as a circular address space), with small
419  * distance.
420  *
421  * @param cls closure (internal context for the plugin)
422  * @param key area of the keyspace to look into
423  * @param num_results number of results that should be returned to @a iter
424  * @param iter maybe NULL (to just count)
425  * @param iter_cls closure for @a iter
426  * @return the number of results found
427  */
428 static unsigned int
429 heap_plugin_get_closest (void *cls,
430                          const struct GNUNET_HashCode *key,
431                          unsigned int num_results,
432                          GNUNET_DATACACHE_Iterator iter,
433                          void *iter_cls)
434 {
435   GNUNET_break (0); // not implemented!
436   return 0;
437 }
438
439
440 /**
441  * Entry point for the plugin.
442  *
443  * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
444  * @return the plugin's closure (our `struct Plugin`)
445  */
446 void *
447 libgnunet_plugin_datacache_heap_init (void *cls)
448 {
449   struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
450   struct GNUNET_DATACACHE_PluginFunctions *api;
451   struct Plugin *plugin;
452
453   plugin = GNUNET_new (struct Plugin);
454   plugin->map = GNUNET_CONTAINER_multihashmap_create (1024,  /* FIXME: base on quota! */
455                                                       GNUNET_YES);
456   plugin->heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
457   plugin->env = env;
458   api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
459   api->cls = plugin;
460   api->get = &heap_plugin_get;
461   api->put = &heap_plugin_put;
462   api->del = &heap_plugin_del;
463   api->get_random = &heap_plugin_get_random;
464   api->get_closest = &heap_plugin_get_closest;
465   LOG (GNUNET_ERROR_TYPE_INFO,
466        _("Heap datacache running\n"));
467   return api;
468 }
469
470
471 /**
472  * Exit point from the plugin.
473  *
474  * @param cls closure (our "struct Plugin")
475  * @return NULL
476  */
477 void *
478 libgnunet_plugin_datacache_heap_done (void *cls)
479 {
480   struct GNUNET_DATACACHE_PluginFunctions *api = cls;
481   struct Plugin *plugin = api->cls;
482   struct Value *val;
483
484   while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heap)))
485   {
486     GNUNET_assert (GNUNET_YES ==
487                    GNUNET_CONTAINER_multihashmap_remove (plugin->map,
488                                                          &val->key,
489                                                          val));
490     GNUNET_free_non_null (val->path_info);
491     GNUNET_free (val);
492   }
493   GNUNET_CONTAINER_heap_destroy (plugin->heap);
494   GNUNET_CONTAINER_multihashmap_destroy (plugin->map);
495   GNUNET_free (plugin);
496   GNUNET_free (api);
497   return NULL;
498 }
499
500
501
502 /* end of plugin_datacache_heap.c */