chiark / gitweb /
better stats for missing
[inn-innduct.git] / nnrpd / cache.c
1 /*  $Id: cache.c 6169 2003-01-21 06:31:40Z alexk $
2 **
3 **  MessageID to storage token cache
4 **
5 **  Written by Alex Kiernan (alex.kiernan@thus.net)
6 **
7 **  Implementation of a message ID to storage token cache which can be
8 **  built during XOVER/XHDR/NEWNEWS.  If we hit in the cache when
9 **  retrieving articles the (relatively) expensive cost of a trip
10 **  through the history database is saved.
11 */
12
13 #include "config.h"
14 #include "clibrary.h"
15
16 #include "inn/innconf.h"
17 #include "inn/tst.h"
18 #include "inn/list.h"
19 #include "libinn.h"
20 #include "storage.h"
21
22 #include "cache.h"
23
24 /*
25 **  Pointer to the message ID to storage token ternary search tree
26 */
27 static struct tst *msgidcache;
28
29 /*
30 **  Count of message IDs in the cache so that someone doing GROUP,
31 **  XOVER, GROUP, XOVER etc. for example doesn't blow up with out of
32 **  memory
33 */
34 static int msgcachecount;
35
36 struct cache_entry {
37     struct node node;
38     HASH hash;
39     TOKEN token;
40 };
41
42 static struct list unused, used;
43
44 /*
45 **  Add a translation from HASH, h, to TOKEN, t, to the message ID
46 **  cache
47 */
48 void
49 cache_add(const HASH h, const TOKEN t)
50 {
51     if (innconf->msgidcachesize != 0) {
52         struct cache_entry *entry;
53         const unsigned char *p;
54         struct cache_entry *exist;
55
56         if (!msgidcache) {
57             msgidcache = tst_init((innconf->msgidcachesize + 9) / 10);
58             list_new(&unused);
59             list_new(&used);
60         }
61
62         entry = xmalloc(sizeof *entry);
63         entry->hash = h;
64         entry->token = t;
65         p = (unsigned char *) HashToText(h);
66         if (tst_insert(msgidcache, p, entry,
67                        0, (void **)&exist) == TST_DUPLICATE_KEY) {
68             free(entry);
69             list_remove(&exist->node);
70             list_addtail(&unused, &exist->node);
71         } else {
72             list_addtail(&unused, &entry->node);
73             ++msgcachecount;
74         }
75         if (msgcachecount >= innconf->msgidcachesize) {
76             /* need to throw away a node */
77             entry = (struct cache_entry *)list_remhead(&used);
78             if (entry == NULL)
79                 entry = (struct cache_entry *)list_remhead(&unused);
80             if (entry != NULL) {
81                 tst_delete(msgidcache,
82                            (unsigned char *) HashToText(entry->hash));
83                 free(entry);
84             }
85         }
86     }
87 }
88
89
90 /*
91 **  Lookup (and remove if found) a MessageID to TOKEN mapping. If this
92 **  is a final lookup (ARTICLE or BODY) we remove it if we find it
93 **  since this matches the observed behaviour of most clients, but
94 **  cache it just in case we can reuse it if they issue multiple
95 **  commands against the same message ID (e.g. HEAD, BODY).
96 */
97 TOKEN
98 cache_get(const HASH h, bool final)
99 {
100     static HASH last_hash;
101     static TOKEN last_token;
102     static const TOKEN empty_token = { TOKEN_EMPTY, 0, "" };
103
104     if (HashCompare(&h, &last_hash) == 0 && !HashEmpty(last_hash))
105         return last_token;
106
107     if (msgidcache) {
108         struct cache_entry *entry;
109
110         entry = tst_search(msgidcache, (unsigned char *) HashToText(h));
111         if (entry != NULL) {
112             list_remove(&entry->node);
113             if (!final)
114                 list_addtail(&unused, &entry->node);
115             else
116                 list_addtail(&used, &entry->node);
117             last_hash = entry->hash;
118             last_token = entry->token;
119             return last_token;
120         }
121     }
122     return empty_token;
123 }