Fix severe SIGHUP reload slowdown caused by store_name quadratic search

When reloading large hosts files (e.g. 1.7M addn-hosts entries) on SIGHUP,
free_names() previously only reset block->last = 0 without unlinking or
moving blocks to a spare pool. As a result, all existing blocks remained
in hostblocks. When reloading, the head block was filled first and stayed
at the head while full, forcing store_name() to linearly traverse all
previously filled blocks for every subsequent name insertion. This resulted
in O(N^2) complexity, taking ~10 minutes for 1.7M records.

Fix this by introducing nameblock_spare (matching config_spare / big_free
spare pool semantics), moving freed blocks to the spare pool in free_names(),
and restricting store_name() to only check the active head block before
popping from nameblock_spare (or mallocing). This restores O(1) per-entry
insertion and reduces reload time from 10 minutes to ~3 seconds.
This commit is contained in:
Pank authored and Simon Kelley committed 2026-09-02 20:58:04 +01:00
1 parent f031f5b4b3
commit a9cb3971c6
1 file changed
+32 -14
+32 -14
View File
@@ -217,6 +217,8 @@ static void free_config_crec(struct crec *p)
config_spare = p;
}
static struct nameblock *nameblock_spare = NULL;
static char *store_name(unsigned int namelen, unsigned int index)
{
struct nameblock *block;
@@ -225,21 +227,28 @@ static char *store_name(unsigned int namelen, unsigned int index)
if (namelen > NAMEBLOCK_CHARS)
return NULL;
for (block = hostblocks; block; block = block->next)
if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen)
break;
if (hostblocks && hostblocks->index == index && NAMEBLOCK_CHARS - hostblocks->last >= namelen)
{
ret = &hostblocks->data[hostblocks->last];
hostblocks->last += namelen;
return ret;
}
if (!block && ((block = whine_malloc(sizeof(struct nameblock)))))
if (nameblock_spare)
{
block = nameblock_spare;
nameblock_spare = nameblock_spare->next;
}
else
block = whine_malloc(sizeof(struct nameblock));
if (block)
{
block->next = hostblocks;
block->index = index;
block->last = namelen;
hostblocks = block;
}
if (block && NAMEBLOCK_CHARS - block->last >= namelen)
{
ret = &block->data[block->last];
block->last += namelen;
ret = &block->data[0];
}
return ret;
@@ -247,11 +256,20 @@ static char *store_name(unsigned int namelen, unsigned int index)
static void free_names(unsigned int index)
{
struct nameblock *block;
struct nameblock *block, *tmp, **up;
for (block = hostblocks; block; block = block->next)
if (index == UID_NONE || block->index == index)
block->last = 0;
for (up = &hostblocks, block = hostblocks; block; block = tmp)
{
tmp = block->next;
if (index == UID_NONE || block->index == index)
{
*up = tmp;
block->next = nameblock_spare;
nameblock_spare = block;
}
else
up = &block->next;
}
}
/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)