]> git.meshlink.io Git - catta/blob - cache.c
* add cache management
[catta] / cache.c
1 #include <string.h>
2
3 #include "cache.h"
4
5 static void remove_entry(flxCache *c, flxCacheEntry *e, gboolean remove_from_hash_table) {
6     g_assert(c);
7     g_assert(e);
8
9     if (remove_from_hash_table) {
10         flxCacheEntry *t;
11         t = g_hash_table_lookup(c->hash_table, e->record->key);
12         FLX_LLIST_REMOVE(flxCacheEntry, by_name, t, e);
13         if (t)
14             g_hash_table_replace(c->hash_table, t->record->key, t);
15         else
16             g_hash_table_remove(c->hash_table, e->record->key);
17     }
18         
19     flx_record_unref(e->record);
20
21     if (e->time_event)
22         flx_time_event_queue_remove(c->server->time_event_queue, e->time_event);
23     
24     g_free(e);
25 }
26
27 flxCache *flx_cache_new(flxServer *server, flxInterface *iface, guchar protocol) {
28     flxCache *c;
29     g_assert(server);
30
31     c = g_new(flxCache, 1);
32     c->server = server;
33     c->interface = iface;
34     c->protocol = protocol;
35     c->hash_table = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal);
36
37     return c;
38 }
39
40 gboolean remove_func(gpointer key, gpointer value, gpointer user_data) {
41     flxCacheEntry *e, *next;
42
43     for (e = value; e; e = next) {
44         next = e->by_name_next;
45         remove_entry(user_data, e, FALSE);
46     }
47     
48     return TRUE;
49 }
50
51 void flx_cache_free(flxCache *c) {
52     g_assert(c);
53
54     g_hash_table_foreach_remove(c->hash_table, remove_func, c);
55     g_hash_table_destroy(c->hash_table);
56     
57     g_free(c);
58 }
59
60 flxCacheEntry *flx_cache_lookup_key(flxCache *c, flxKey *k) {
61     g_assert(c);
62     g_assert(k);
63
64     return g_hash_table_lookup(c->hash_table, k);
65 }
66
67 flxCacheEntry *flx_cache_lookup_record(flxCache *c, flxRecord *r) {
68     flxCacheEntry *e;
69     g_assert(c);
70     g_assert(r);
71
72     for (e = flx_cache_lookup_key(c, r->key); e; e = e->by_name_next)
73         if (e->record->size == r->size && !memcmp(e->record->data, r->data, r->size))
74             return e;
75
76     return NULL;
77 }
78
79 static void next_expiry(flxCache *c, flxCacheEntry *e, guint percent);
80
81 static void elapse_func(flxTimeEvent *t, void *userdata) {
82     flxCacheEntry *e = userdata;
83     
84     g_assert(t);
85     g_assert(e);
86
87     if (e->state == FLX_CACHE_FINAL) {
88         remove_entry(e->cache, e, TRUE);
89         g_message("Removing entry from cache due to expiration");
90     } else {
91         guint percent = 0;
92     
93         switch (e->state) {
94             case FLX_CACHE_VALID:
95                 e->state = FLX_CACHE_EXPIRY1;
96                 percent = 85;
97                 break;
98                 
99             case FLX_CACHE_EXPIRY1:
100                 e->state = FLX_CACHE_EXPIRY2;
101                 percent = 90;
102                 break;
103             case FLX_CACHE_EXPIRY2:
104                 e->state = FLX_CACHE_EXPIRY3;
105                 percent = 95;
106                 break;
107                 
108             case FLX_CACHE_EXPIRY3:
109                 e->state = FLX_CACHE_FINAL;
110                 percent = 100;
111                 break;
112
113             default:
114                 ;
115         }
116
117         g_assert(percent > 0);
118
119         g_message("Requesting cache entry update at %i%%.", percent);
120
121         /* Request a cache update */
122         flx_interface_post_query(e->cache->interface, e->cache->protocol, e->record->key);
123
124         /* Check again later */
125         next_expiry(e->cache, e, percent);
126     }
127 }
128
129 static void next_expiry(flxCache *c, flxCacheEntry *e, guint percent) {
130     gulong usec;
131
132     g_assert(c);
133     g_assert(e);
134     g_assert(percent > 0 && percent <= 100);
135
136     e->expiry = e->timestamp;
137
138     usec = e->record->ttl * 10000;
139
140     /* 2% jitter */
141     usec = g_random_int_range(usec*percent, usec*(percent+2));
142     
143     g_time_val_add(&e->expiry, usec);
144
145     if (e->time_event)
146         flx_time_event_queue_update(c->server->time_event_queue, e->time_event, &e->expiry);
147     else
148         e->time_event = flx_time_event_queue_add(c->server->time_event_queue, &e->expiry, elapse_func, e);
149 }
150
151 flxCacheEntry *flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddress *a) {
152     flxCacheEntry *e, *t;
153     gchar *txt;
154     
155     g_assert(c);
156     g_assert(r && r->ref >= 1);
157
158     g_message("cache update: %s", (txt = flx_record_to_string(r)));
159     g_free(txt);
160
161     if ((t = e = flx_cache_lookup_key(c, r->key))) {
162
163 /*         g_message("found prev cache entry"); */
164
165         if (unique) {
166             /* Drop all entries but the first which we replace */
167             while (e->by_name_next)
168                 remove_entry(c, e->by_name_next, TRUE);
169
170         } else {
171             /* Look for exactly the same entry */
172             for (; e; e = e->by_name_next)
173                 if (flx_record_equal(e->record, r))
174                     break;
175         }
176     }
177     
178     if (e) {
179
180 /*         g_message("found matching cache entry"); */
181
182         /* We are the first in the linked list so let's replace the hash table key with the new one */
183         if (e->by_name_prev == NULL)
184             g_hash_table_replace(c->hash_table, r->key, e);
185         
186         /* Update the record */
187         flx_record_unref(e->record);
188         e->record = flx_record_ref(r);
189
190         
191     } else {
192         /* No entry found, therefore we create a new one */
193
194 /*         g_message("couldn't find matching cache entry"); */
195         
196         e = g_new(flxCacheEntry, 1);
197         e->cache = c;
198         e->time_event = NULL;
199         e->record = flx_record_ref(r);
200         FLX_LLIST_PREPEND(flxCacheEntry, by_name, t, e);
201         g_hash_table_replace(c->hash_table, e->record->key, t);
202     } 
203
204     e->origin = *a;
205     g_get_current_time(&e->timestamp);
206     next_expiry(c, e, 80);
207     e->state = FLX_CACHE_VALID;
208
209     return e;
210 }
211
212 void flx_cache_drop_key(flxCache *c, flxKey *k) {
213     flxCacheEntry *e;
214     
215     g_assert(c);
216     g_assert(k);
217
218     while ((e = flx_cache_lookup_key(c, k)))
219         remove_entry(c, e, TRUE);
220 }
221
222 void flx_cache_drop_record(flxCache *c, flxRecord *r) {
223     flxCacheEntry *e;
224     
225     g_assert(c);
226     g_assert(r);
227
228     if ((e = flx_cache_lookup_record(c, r))) 
229         remove_entry(c, e, TRUE);
230 }
231
232 static void func(gpointer key, gpointer data, gpointer userdata) {
233     flxCacheEntry *e = data;
234     flxKey *k = key;
235
236     gchar *s, *t;
237
238     s = flx_key_to_string(k);
239     t = flx_record_to_string(e->record);
240
241     fprintf((FILE*) userdata, "%s %s\n", s, t);
242     
243     g_free(s);
244     g_free(t);
245 }
246
247 void flx_cache_dump(flxCache *c, FILE *f) {
248     g_assert(c);
249     g_assert(f);
250
251     fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n");
252     g_hash_table_foreach(c->hash_table, func, f);
253 }