diff options
author | Christian Grothoff <christian@grothoff.org> | 2009-07-24 22:04:41 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2009-07-24 22:04:41 +0000 |
commit | 70e6847205a9f9b9b660be2a173d5bc309eaa58d (patch) | |
tree | e2649865fbff3e7cbb2e85e381a6268b9aa01e0d /src/datastore/gnunet-service-datastore.c | |
parent | 0722614037876469e205546db5ab5fc892b5cf8c (diff) | |
download | gnunet-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.c | 285 |
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; | |||
134 | static unsigned long long quota; | 134 | static 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 | */ | ||
143 | static 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 | */ |
139 | static unsigned long long reserved; | 148 | static unsigned long long reserved; |
140 | 149 | ||
150 | /** | ||
151 | * Identity of the task that is used to delete | ||
152 | * expired content. | ||
153 | */ | ||
154 | static GNUNET_SCHEDULER_TaskIdentifier expired_kill_task; | ||
155 | |||
156 | /** | ||
157 | * Our configuration. | ||
158 | */ | ||
159 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
160 | |||
161 | /** | ||
162 | * Our scheduler. | ||
163 | */ | ||
164 | struct 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; | |||
149 | typedef void (*TransmitContinuation)(void *cls, | 173 | typedef void (*TransmitContinuation)(void *cls, |
150 | int status); | 174 | int status); |
151 | 175 | ||
176 | |||
152 | struct TransmitCallbackContext | 177 | struct 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 | */ | ||
217 | static void | ||
218 | delete_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 | */ | ||
243 | static int | ||
244 | expired_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 | */ | ||
292 | static void | ||
293 | delete_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 | */ | ||
322 | static int | ||
323 | manage (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 | */ | ||
364 | static void | ||
365 | manage_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 | */ |
896 | static struct DatastorePlugin * | 1112 | static struct DatastorePlugin * |
897 | load_plugin (struct GNUNET_CONFIGURATION_Handle *cfg, | 1113 | load_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 | */ |
993 | static void | 1231 | static void |
994 | run (void *cls, | 1232 | run (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", "a)) | 1244 | "DATASTORE", "QUOTA", "a)) |
@@ -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 | ||