Commit bf480410 authored by Pekka Pessi's avatar Pekka Pessi

sres_cache.c: using heap in order to determine which the cached entries should expire

Previous implementation locked cache and examined all the cached entries
every 30 second.

darcs-hash:20070702152701-65a35-7ce1ec70118e2262f0a584810f207369c2bfc662.gz
parent 4d449d7e
......@@ -60,7 +60,7 @@ typedef union sres_record sres_record_t;
enum {
/** Cache cleanup interval in seconds. */
SRES_CACHE_TIMER_INTERVAL = 30,
SRES_CACHE_TIMER_INTERVAL = 5,
#define SRES_CACHE_TIMER_INTERVAL (SRES_CACHE_TIMER_INTERVAL)
};
......
......@@ -65,6 +65,7 @@ typedef unsigned _int32 uint32_t;
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_strlst.h>
#include <sofia-sip/htable.h>
#include <sofia-sip/heap.h>
#include <stdlib.h>
#include <stdarg.h>
......@@ -86,19 +87,29 @@ typedef struct sres_rr_hash_entry_s sres_rr_hash_entry_t;
HTABLE_DECLARE_WITH(sres_htable, ht, sres_rr_hash_entry_t, unsigned, size_t);
typedef struct sres_heap sres_heap_t;
HEAP_DECLARE(sres_heap, he_, sres_rr_hash_entry_t *);
HEAP_PROTOS(static inline, sres_heap_t, sres_heap_, sres_rr_hash_entry_t *);
struct sres_rr_hash_entry_s {
unsigned int rr_hash_key;
time_t rr_received;
sres_record_t *rr;
size_t rr_heap_index;
time_t rr_expires;
unsigned int rr_hash_key;
};
#define SRES_HENTRY_HASH(e) ((e)->rr_hash_key)
/* ---------------------------------------------------------------------- */
/* Heap */
struct sres_cache
{
su_home_t cache_home[1];
time_t cache_cleaned;
sres_htable_t cache_hash[1];
sres_heap_t cache_heap[1];
};
#define sr_refcount sr_record->r_refcount
......@@ -109,7 +120,6 @@ struct sres_cache
#define sr_class sr_record->r_class
#define sr_ttl sr_record->r_ttl
#define sr_rdlen sr_record->r_rdlen
#define sr_rdata sr_generic->g_data
/* ---------------------------------------------------------------------- */
/* Internal prototypes */
......@@ -140,7 +150,9 @@ sres_cache_t *sres_cache_new(int n)
if (cache) {
su_home_threadsafe(cache->cache_home);
if (sres_htable_resize(cache->cache_home, cache->cache_hash, n) < 0)
if (sres_htable_resize(cache->cache_home, cache->cache_hash, n) < 0 ||
sres_heap_resize(cache->cache_home, cache->cache_heap,
cache->cache_hash->ht_size) < 0)
su_home_unref(cache->cache_home), cache = NULL;
}
......@@ -197,7 +209,7 @@ int sres_cache_get(sres_cache_t *cache,
rr = (*rr_iter)->rr;
if (rr != NULL &&
(uint32_t)(now - (*rr_iter)->rr_received) <= rr->sr_ttl &&
now <= (*rr_iter)->rr_expires &&
(type == sres_qtype_any || rr->sr_type == type) &&
rr->sr_name != NULL &&
strcasecmp(rr->sr_name, domain) == 0)
......@@ -224,7 +236,7 @@ int sres_cache_get(sres_cache_t *cache,
rr = (*rr_iter)->rr;
if (rr != NULL &&
(uint32_t)(now - (*rr_iter)->rr_received) <= rr->sr_ttl &&
now <= (*rr_iter)->rr_expires &&
(type == sres_qtype_any || rr->sr_type == type) &&
rr->sr_name != NULL &&
strcasecmp(rr->sr_name, domain) == 0) {
......@@ -278,13 +290,13 @@ sres_cache_alloc_record(sres_cache_t *cache,
}
/** Free a record that has not been stored. */
void sres_cache_free_record(sres_cache_t *cache, void *rr)
void sres_cache_free_record(sres_cache_t *cache, void *_sr)
{
sres_record_t *sr = rr;
sres_record_t *sr = _sr;
if (sr) {
assert(sr->sr_refcount == 0);
su_free(cache->cache_home, rr);
su_free(cache->cache_home, sr);
}
}
......@@ -306,6 +318,12 @@ sres_cache_store(sres_cache_t *cache, sres_record_t *rr, time_t now)
if (sres_htable_is_full(cache->cache_hash))
sres_htable_resize(cache->cache_home, cache->cache_hash, 0);
if (sres_heap_is_full(cache->cache_heap))
if (sres_heap_resize(cache->cache_home, cache->cache_heap, 0) < 0) {
UNLOCK(cache);
return;
}
for (rr_iter = sres_htable_hash(cache->cache_hash, hash);
(rr_hash_entry = *rr_iter);
rr_iter = sres_htable_next(cache->cache_hash, rr_iter)) {
......@@ -327,10 +345,12 @@ sres_cache_store(sres_cache_t *cache, sres_record_t *rr, time_t now)
continue;
/* There was an old entry in the cache.. Zap it, replace this with it */
rr_hash_entry->rr_received = now;
sres_heap_remove(cache->cache_heap, rr_hash_entry->rr_heap_index);
rr_hash_entry->rr_expires = now + rr->sr_ttl;
rr_hash_entry->rr = rr;
rr->sr_refcount++;
sres_heap_add(cache->cache_heap, rr_hash_entry);
_sres_cache_free_one(cache, or);
UNLOCK(cache);
......@@ -339,16 +359,19 @@ sres_cache_store(sres_cache_t *cache, sres_record_t *rr, time_t now)
}
rr_hash_entry = su_zalloc(cache->cache_home, sizeof(*rr_hash_entry));
if (rr_hash_entry) {
rr_hash_entry->rr_hash_key = hash;
rr_hash_entry->rr_received = now;
rr_hash_entry->rr_expires = now + rr->sr_ttl;
rr_hash_entry->rr = rr;
rr->sr_refcount++;
sres_heap_add(cache->cache_heap, rr_hash_entry);
cache->cache_hash->ht_used++;
*rr_iter = rr_hash_entry;
}
*rr_iter = rr_hash_entry;
UNLOCK(cache);
}
......@@ -417,6 +440,7 @@ sres_hash_key(const char *string)
return result;
}
/** Remove entries from cache. */
void sres_cache_clean(sres_cache_t *cache, time_t now)
{
size_t i;
......@@ -430,17 +454,25 @@ void sres_cache_clean(sres_cache_t *cache, time_t now)
/* Clean cache from old entries */
cache->cache_cleaned = now;
for (i = 0; i < cache->cache_hash->ht_size; i++) {
sres_rr_hash_entry_t *e;
while (cache->cache_heap->he_used > 0 &&
cache->cache_heap->he_heap[0]->rr_expires < now) {
for (i = 0; i < 100; i++) {
sres_rr_hash_entry_t *e = cache->cache_heap->he_heap[0];
if (cache->cache_heap->he_used == 0 || e->rr_expires >= now) {
UNLOCK(cache);
return;
}
while ((e = cache->cache_hash->ht_table[i]) != NULL) {
if ((uint32_t)(now - e->rr_received) <= e->rr->sr_ttl)
break;
sres_heap_remove(cache->cache_heap, 0);
sres_htable_remove(cache->cache_hash, e);
_sres_cache_free_one(cache, e->rr);
su_free(cache->cache_home, e);
}
UNLOCK(cache);
if (!LOCK(cache))
return;
}
UNLOCK(cache);
......@@ -448,3 +480,30 @@ void sres_cache_clean(sres_cache_t *cache, time_t now)
HTABLE_BODIES_WITH(sres_htable, ht, sres_rr_hash_entry_t, SRES_HENTRY_HASH,
unsigned, size_t);
static inline
int sres_heap_cmp_entry(sres_rr_hash_entry_t const *a,
sres_rr_hash_entry_t *b)
{
if (a->rr_expires < b->rr_expires)
return -1;
else if (a->rr_expires > b->rr_expires)
return 1;
else
return 0;
}
static inline
void sres_heap_set_entry(sres_rr_hash_entry_t **heap,
size_t index,
sres_rr_hash_entry_t *entry)
{
entry->rr_heap_index = index;
heap[index] = entry;
}
HEAP_BODIES(static inline, sres_heap_t, sres_heap_, he_,
sres_rr_hash_entry_t *,
sres_heap_cmp_entry,
sres_heap_set_entry,
su_realloc);
......@@ -79,7 +79,7 @@ typedef unsigned _int32 uint32_t;
#endif
#define TSTFLAGS tstflags
int tstflags;
int tstflags, o_timing;
#include <sofia-sip/tstdef.h>
......@@ -222,6 +222,139 @@ int test_api_errors(void)
END();
}
extern void sres_cache_clean(sres_cache_t *cache, time_t now);
static
int test_cache(void)
{
BEGIN();
sres_a_record_t *a, a0[1], **all;
char host[128];
sres_cache_t *cache;
time_t now, base;
struct timespec t0, t1, t2;
size_t i, N, N1 = 1000, N3 = 1000000;
time(&base);
cache = sres_cache_new(N1);
TEST_1(cache);
all = calloc(N3, sizeof *all); if (!all) perror("calloc"), exit(2);
memset(a0, 0, sizeof a0);
a0->a_record->r_refcount = 1;
a0->a_record->r_size = sizeof *a;
a0->a_record->r_type = sres_type_a;
a0->a_record->r_class = sres_class_in;
a0->a_record->r_ttl = 3600;
a0->a_record->r_rdlen = sizeof a->a_addr;
a0->a_record->r_parsed = 1;
for (i = 0, N = N3; i < N; i++) {
a0->a_record->r_name = host;
snprintf(host, sizeof host, "%u.example.com.", (unsigned)i);
a = (sres_a_record_t *)
sres_cache_alloc_record(cache, (sres_record_t *)a0, 0);
if (!a)
perror("sres_cache_alloc_record"), exit(2);
all[i] = a, a->a_record->r_refcount = 1;
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
for (i = 0, N = N3; i < N; i++) {
now = base + (3600 * i + N / 2) / N;
a->a_record->r_ttl = 60 + (i * 60) % 3600;
sres_cache_store(cache, (sres_record_t *)all[i], now);
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
if (o_timing) {
t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
if (t1.tv_nsec < t0.tv_nsec)
t2.tv_sec--, t2.tv_nsec += 1000000000;
printf("sres_cache: stored %u entries: %lu.%09lu sec\n",
N, t2.tv_sec, t2.tv_nsec);
}
for (i = 0, N; i < N; i++)
TEST(all[i]->a_record->r_refcount, 2);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
for (now = base; now <= base + 3660; now += 30)
sres_cache_clean(cache, now + 3600);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
if (o_timing) {
t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
if (t1.tv_nsec < t0.tv_nsec)
t2.tv_sec--, t2.tv_nsec += 1000000000;
printf("sres_cache: cleaned %u entries: %lu.%09lu sec\n",
N, t2.tv_sec, t2.tv_nsec);
}
for (i = 0, N; i < N; i++)
TEST(all[i]->a_record->r_refcount, 1);
base += 24 * 3600;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
for (i = 0, N; i < N; i++) {
now = base + (3600 * i + N / 2) / N;
a->a_record->r_ttl = 60 + (i * 60) % 3600;
sres_cache_store(cache, (sres_record_t *)all[i], now);
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
if (o_timing) {
t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
if (t1.tv_nsec < t0.tv_nsec)
t2.tv_sec--, t2.tv_nsec += 1000000000;
printf("sres_cache: stored %u entries: %lu.%09lu sec\n",
N, t2.tv_sec, t2.tv_nsec);
}
for (i = 0, N; i < N; i++)
TEST(all[i]->a_record->r_refcount, 2);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t0);
for (now = base; now <= base + 3660; now += 1)
sres_cache_clean(cache, now + 3600);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1);
if (o_timing) {
t2.tv_sec = t1.tv_sec - t0.tv_sec, t2.tv_nsec = t1.tv_nsec - t0.tv_nsec;
if (t1.tv_nsec < t0.tv_nsec)
t2.tv_sec--, t2.tv_nsec += 1000000000;
printf("sres_cache: cleaned %u entries: %lu.%09lu sec\n",
N, t2.tv_sec, t2.tv_nsec);
}
for (i = 0, N; i < N; i++) {
TEST(all[i]->a_record->r_refcount, 1);
sres_cache_free_one(cache, (sres_record_t *)all[i]);
}
sres_cache_unref(cache);
free(all);
END();
}
#if HAVE_ALARM
static RETSIGTYPE sig_alarm(int s)
{
......@@ -264,6 +397,8 @@ int main(int argc, char **argv)
tstflags |= tst_verbatim;
else if (strcmp(argv[i], "-a") == 0)
tstflags |= tst_abort;
else if (strcmp(argv[i], "-t") == 0)
o_timing = 1;
else if (strcmp(argv[i], "--no-alarm") == 0) {
o_alarm = 0;
}
......@@ -299,6 +434,7 @@ int main(int argc, char **argv)
}
error |= test_api_errors();
error |= test_cache();
return error;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment