aboutsummaryrefslogtreecommitdiff
path: root/src/datastore/gnunet-service-datastore.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-07-24 22:04:41 +0000
committerChristian Grothoff <christian@grothoff.org>2009-07-24 22:04:41 +0000
commit70e6847205a9f9b9b660be2a173d5bc309eaa58d (patch)
treee2649865fbff3e7cbb2e85e381a6268b9aa01e0d /src/datastore/gnunet-service-datastore.c
parent0722614037876469e205546db5ab5fc892b5cf8c (diff)
downloadgnunet-70e6847205a9f9b9b660be2a173d5bc309eaa58d.tar.gz
gnunet-70e6847205a9f9b9b660be2a173d5bc309eaa58d.zip
quota management and better name for NO_TASK'
'
Diffstat (limited to 'src/datastore/gnunet-service-datastore.c')
-rw-r--r--src/datastore/gnunet-service-datastore.c285
1 files changed, 267 insertions, 18 deletions
diff --git a/src/datastore/gnunet-service-datastore.c b/src/datastore/gnunet-service-datastore.c
index 89c5d6af8..cedad3649 100644
--- a/src/datastore/gnunet-service-datastore.c
+++ b/src/datastore/gnunet-service-datastore.c
@@ -22,13 +22,6 @@
22 * @file datastore/gnunet-service-datastore.c 22 * @file datastore/gnunet-service-datastore.c
23 * @brief Management for the datastore for files stored on a GNUnet node 23 * @brief Management for the datastore for files stored on a GNUnet node
24 * @author Christian Grothoff 24 * @author Christian Grothoff
25 *
26 * TODO:
27 * quota management code:
28 * - track storage use
29 * - refuse above-quota
30 * - content expiration job
31 * - near-quota low-priority content discard job
32 */ 25 */
33 26
34#include "platform.h" 27#include "platform.h"
@@ -42,6 +35,13 @@
42 */ 35 */
43#define MAX_PENDING 1024 36#define MAX_PENDING 1024
44 37
38/**
39 * How long are we at most keeping "expired" content
40 * past the expiration date in the database?
41 */
42#define MAX_EXPIRE_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
43
44
45 45
46/** 46/**
47 * Our datastore plugin. 47 * Our datastore plugin.
@@ -134,10 +134,34 @@ static int reservation_gen;
134static unsigned long long quota; 134static unsigned long long quota;
135 135
136/** 136/**
137 * How much space are we using for the cache?
138 * (space available for insertions that will be
139 * instantly reclaimed by discarding less
140 * important content --- or possibly whatever
141 * we just inserted into the "cache").
142 */
143static unsigned long long cache_size;
144
145/**
137 * How much space have we currently reserved? 146 * How much space have we currently reserved?
138 */ 147 */
139static unsigned long long reserved; 148static unsigned long long reserved;
140 149
150/**
151 * Identity of the task that is used to delete
152 * expired content.
153 */
154static GNUNET_SCHEDULER_TaskIdentifier expired_kill_task;
155
156/**
157 * Our configuration.
158 */
159struct GNUNET_CONFIGURATION_Handle *cfg;
160
161/**
162 * Our scheduler.
163 */
164struct GNUNET_SCHEDULER_Handle *sched;
141 165
142/** 166/**
143 * Function called once the transmit operation has 167 * Function called once the transmit operation has
@@ -149,6 +173,7 @@ static unsigned long long reserved;
149typedef void (*TransmitContinuation)(void *cls, 173typedef void (*TransmitContinuation)(void *cls,
150 int status); 174 int status);
151 175
176
152struct TransmitCallbackContext 177struct TransmitCallbackContext
153{ 178{
154 /** 179 /**
@@ -181,6 +206,176 @@ struct TransmitCallbackContext
181 206
182 207
183/** 208/**
209 * Task that is used to remove expired entries from
210 * the datastore. This task will schedule itself
211 * again automatically to always delete all expired
212 * content quickly.
213 *
214 * @param cls not used
215 * @param tc task context
216 */
217static void
218delete_expired (void *cls,
219 const struct GNUNET_SCHEDULER_TaskContext *tc);
220
221
222/**
223 * Iterate over the expired items stored in the datastore.
224 * Delete all expired items; once we have processed all
225 * expired items, re-schedule the "delete_expired" task.
226 *
227 * @param cls not used
228 * @param next_cls closure to pass to the "next" function.
229 * @param key key for the content
230 * @param size number of bytes in data
231 * @param data content stored
232 * @param type type of the content
233 * @param priority priority of the content
234 * @param anonymity anonymity-level for the content
235 * @param expiration expiration time for the content
236 * @param uid unique identifier for the datum;
237 * maybe 0 if no unique identifier is available
238 *
239 * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
240 * (continue on call to "next", of course),
241 * GNUNET_NO to delete the item and continue (if supported)
242 */
243static int
244expired_processor (void *cls,
245 void *next_cls,
246 const GNUNET_HashCode * key,
247 uint32_t size,
248 const void *data,
249 uint32_t type,
250 uint32_t priority,
251 uint32_t anonymity,
252 struct GNUNET_TIME_Absolute
253 expiration,
254 uint64_t uid)
255{
256 struct GNUNET_TIME_Absolute now;
257
258 expired_kill_task = GNUNET_SCHEDULER_NO_TASK;
259 if (key == NULL)
260 {
261 expired_kill_task
262 = GNUNET_SCHEDULER_add_delayed (sched,
263 GNUNET_NO,
264 GNUNET_SCHEDULER_PRIORITY_IDLE,
265 GNUNET_SCHEDULER_NO_TASK,
266 MAX_EXPIRE_DELAY,
267 &delete_expired,
268 NULL);
269 return GNUNET_SYSERR;
270 }
271 now = GNUNET_TIME_absolute_get ();
272 if (expiration.value > now.value)
273 {
274 /* finished processing */
275 plugin->api->next_request (next_cls, GNUNET_YES);
276 return GNUNET_SYSERR;
277 }
278 plugin->api->next_request (next_cls, GNUNET_NO);
279 return GNUNET_NO; /* delete */
280}
281
282
283/**
284 * Task that is used to remove expired entries from
285 * the datastore. This task will schedule itself
286 * again automatically to always delete all expired
287 * content quickly.
288 *
289 * @param cls not used
290 * @param tc task context
291 */
292static void
293delete_expired (void *cls,
294 const struct GNUNET_SCHEDULER_TaskContext *tc)
295{
296 plugin->api->iter_ascending_expiration (plugin->api->cls,
297 0,
298 &expired_processor,
299 NULL);
300}
301
302
303/**
304 * An iterator over a set of items stored in the datastore.
305 *
306 * @param cls closure
307 * @param next_cls closure to pass to the "next" function.
308 * @param key key for the content
309 * @param size number of bytes in data
310 * @param data content stored
311 * @param type type of the content
312 * @param priority priority of the content
313 * @param anonymity anonymity-level for the content
314 * @param expiration expiration time for the content
315 * @param uid unique identifier for the datum;
316 * maybe 0 if no unique identifier is available
317 *
318 * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
319 * (continue on call to "next", of course),
320 * GNUNET_NO to delete the item and continue (if supported)
321 */
322static int
323manage (void *cls,
324 void *next_cls,
325 const GNUNET_HashCode * key,
326 uint32_t size,
327 const void *data,
328 uint32_t type,
329 uint32_t priority,
330 uint32_t anonymity,
331 struct GNUNET_TIME_Absolute
332 expiration,
333 uint64_t uid)
334{
335 unsigned long long *need = cls;
336
337 if (NULL == key)
338 {
339 GNUNET_free (need);
340 return GNUNET_SYSERR;
341 }
342 if (size + GNUNET_DATASTORE_ENTRY_OVERHEAD > *need)
343 *need = 0;
344 else
345 *need -= size + GNUNET_DATASTORE_ENTRY_OVERHEAD;
346 plugin->api->next_request (next_cls,
347 (0 == *need) ? GNUNET_YES : GNUNET_NO);
348 return GNUNET_NO;
349}
350
351
352/**
353 * Manage available disk space by running tasks
354 * that will discard content if necessary. This
355 * function will be run whenever a request for
356 * "need" bytes of storage could only be satisfied
357 * by eating into the "cache" (and we want our cache
358 * space back).
359 *
360 * @param need number of bytes of content that were
361 * placed into the "cache" (and hence the
362 * number of bytes that should be removed).
363 */
364static void
365manage_space (unsigned long long need)
366{
367 unsigned long long *n;
368
369 n = GNUNET_malloc (sizeof(unsigned long long));
370 *n = need;
371 plugin->api->iter_low_priority (plugin->api->cls,
372 0,
373 &manage,
374 n);
375}
376
377
378/**
184 * Function called to notify a client about the socket 379 * Function called to notify a client about the socket
185 * begin ready to queue more data. "buf" will be 380 * begin ready to queue more data. "buf" will be
186 * NULL and "size" zero if the socket was closed for 381 * NULL and "size" zero if the socket was closed for
@@ -446,13 +641,32 @@ handle_reserve (void *cls,
446 if (used + req > quota) 641 if (used + req > quota)
447 { 642 {
448 if (quota < used) 643 if (quota < used)
449 used = quota; /* cheat a bit */ 644 used = quota; /* cheat a bit for error message (to avoid negative numbers) */
450 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 645 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
451 _("Insufficient space (%llu bytes are available) to satisfy `%s' request for %llu bytes\n"), 646 _("Insufficient space (%llu bytes are available) to satisfy `%s' request for %llu bytes\n"),
452 quota - used, 647 quota - used,
453 "RESERVE", 648 "RESERVE",
454 req); 649 req);
455 transmit_status (client, 0, gettext_noop ("Insufficient space to satisfy request")); 650 if (cache_size < req)
651 {
652 /* TODO: document this in the FAQ; essentially, if this
653 message happens, the insertion request could be blocked
654 by less-important content from migration because it is
655 larger than 1/8th of the overall available space, and
656 we only reserve 1/8th for "fresh" insertions */
657 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
658 _("The requested amount (%llu bytes) is larger than the cache size (%llu bytes)\n"),
659 req,
660 cache_size);
661 transmit_status (client, 0,
662 gettext_noop ("Insufficient space to satisfy request and "
663 "requested amount is larger than cache size"));
664 }
665 else
666 {
667 transmit_status (client, 0,
668 gettext_noop ("Insufficient space to satisfy request"));
669 }
456 return; 670 return;
457 } 671 }
458 reserved += req; 672 reserved += req;
@@ -623,6 +837,8 @@ handle_put (void *cls,
623 (GNUNET_SYSERR == ret) ? GNUNET_SYSERR : GNUNET_OK, 837 (GNUNET_SYSERR == ret) ? GNUNET_SYSERR : GNUNET_OK,
624 msg); 838 msg);
625 GNUNET_free_non_null (msg); 839 GNUNET_free_non_null (msg);
840 if (quota - reserved - cache_size < plugin->api->get_size (plugin->api->cls))
841 manage_space (size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
626} 842}
627 843
628 844
@@ -894,8 +1110,7 @@ static struct GNUNET_SERVER_MessageHandler handlers[] = {
894 * Load the datastore plugin. 1110 * Load the datastore plugin.
895 */ 1111 */
896static struct DatastorePlugin * 1112static struct DatastorePlugin *
897load_plugin (struct GNUNET_CONFIGURATION_Handle *cfg, 1113load_plugin ()
898 struct GNUNET_SCHEDULER_Handle *sched)
899{ 1114{
900 struct DatastorePlugin *ret; 1115 struct DatastorePlugin *ret;
901 char *libname; 1116 char *libname;
@@ -978,7 +1193,30 @@ cleanup_reservations (void *cls,
978 struct GNUNET_SERVER_Client 1193 struct GNUNET_SERVER_Client
979 * client) 1194 * client)
980{ 1195{
981 /* FIXME */ 1196 struct ReservationList *pos;
1197 struct ReservationList *prev;
1198 struct ReservationList *next;
1199
1200 prev = NULL;
1201 pos = reservations;
1202 while (NULL != pos)
1203 {
1204 next = pos->next;
1205 if (pos->client == client)
1206 {
1207 if (prev == NULL)
1208 reservations = next;
1209 else
1210 prev->next = next;
1211 reserved -= pos->amount + pos->entries * GNUNET_DATASTORE_ENTRY_OVERHEAD;
1212 GNUNET_free (pos);
1213 }
1214 else
1215 {
1216 prev = pos;
1217 }
1218 pos = next;
1219 }
982} 1220}
983 1221
984 1222
@@ -986,19 +1224,21 @@ cleanup_reservations (void *cls,
986 * Process datastore requests. 1224 * Process datastore requests.
987 * 1225 *
988 * @param cls closure 1226 * @param cls closure
989 * @param sched scheduler to use 1227 * @param s scheduler to use
990 * @param server the initialized server 1228 * @param server the initialized server
991 * @param cfg configuration to use 1229 * @param c configuration to use
992 */ 1230 */
993static void 1231static void
994run (void *cls, 1232run (void *cls,
995 struct GNUNET_SCHEDULER_Handle *sched, 1233 struct GNUNET_SCHEDULER_Handle *s,
996 struct GNUNET_SERVER_Handle *server, 1234 struct GNUNET_SERVER_Handle *server,
997 struct GNUNET_CONFIGURATION_Handle *cfg) 1235 struct GNUNET_CONFIGURATION_Handle *c)
998{ 1236{
999 char *fn; 1237 char *fn;
1000 unsigned int bf_size; 1238 unsigned int bf_size;
1001 1239
1240 sched = s;
1241 cfg = c;
1002 if (GNUNET_OK != 1242 if (GNUNET_OK !=
1003 GNUNET_CONFIGURATION_get_value_number (cfg, 1243 GNUNET_CONFIGURATION_get_value_number (cfg,
1004 "DATASTORE", "QUOTA", &quota)) 1244 "DATASTORE", "QUOTA", &quota))
@@ -1009,6 +1249,7 @@ run (void *cls,
1009 "DATASTORE"); 1249 "DATASTORE");
1010 return; 1250 return;
1011 } 1251 }
1252 cache_size = quota / 8; /* Or should we make this an option? */
1012 bf_size = quota / 32; /* 8 bit per entry, 1 bit per 32 kb in DB */ 1253 bf_size = quota / 32; /* 8 bit per entry, 1 bit per 32 kb in DB */
1013 fn = NULL; 1254 fn = NULL;
1014 if ( (GNUNET_OK != 1255 if ( (GNUNET_OK !=
@@ -1033,7 +1274,7 @@ run (void *cls,
1033 _("Failed to initialize bloomfilter.\n")); 1274 _("Failed to initialize bloomfilter.\n"));
1034 return; 1275 return;
1035 } 1276 }
1036 plugin = load_plugin (cfg, sched); 1277 plugin = load_plugin ();
1037 if (NULL == plugin) 1278 if (NULL == plugin)
1038 { 1279 {
1039 GNUNET_CONTAINER_bloomfilter_free (filter); 1280 GNUNET_CONTAINER_bloomfilter_free (filter);
@@ -1041,12 +1282,20 @@ run (void *cls,
1041 } 1282 }
1042 GNUNET_SERVER_disconnect_notify (server, &cleanup_reservations, NULL); 1283 GNUNET_SERVER_disconnect_notify (server, &cleanup_reservations, NULL);
1043 GNUNET_SERVER_add_handlers (server, handlers); 1284 GNUNET_SERVER_add_handlers (server, handlers);
1285 expired_kill_task
1286 = GNUNET_SCHEDULER_add_delayed (sched,
1287 GNUNET_NO,
1288 GNUNET_SCHEDULER_PRIORITY_IDLE,
1289 GNUNET_SCHEDULER_NO_TASK,
1290 GNUNET_TIME_UNIT_ZERO,
1291 &delete_expired, NULL);
1044 GNUNET_SCHEDULER_add_delayed (sched, 1292 GNUNET_SCHEDULER_add_delayed (sched,
1045 GNUNET_YES, 1293 GNUNET_YES,
1046 GNUNET_SCHEDULER_PRIORITY_IDLE, 1294 GNUNET_SCHEDULER_PRIORITY_IDLE,
1047 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, 1295 GNUNET_SCHEDULER_NO_TASK,
1048 GNUNET_TIME_UNIT_FOREVER_REL, 1296 GNUNET_TIME_UNIT_FOREVER_REL,
1049 &cleaning_task, NULL); 1297 &cleaning_task, NULL);
1298
1050} 1299}
1051 1300
1052 1301