mirror of
https://github.com/neondatabase/neon.git
synced 2026-06-04 22:10:39 +00:00
Relsize hash lru eviction (#6353)
## Problem Currently relation hash size is limited by "neon.relsize_hash_size" GUC with default value 64k. 64k relations is not so small number... but it is enough to create 376 databases to exhaust it. ## Summary of changes Use LRU replacement algorithm to prevent hash overflow ## Checklist before requesting a review - [ ] I have performed a self-review of my code. - [ ] If it is a core feature, I have added thorough tests. - [ ] Do we need to implement analytics? if so did you add the relevant metrics to the dashboard? - [ ] If this PR requires public announcement, mark it with /release-notes label and add several sentences in this section. ## Checklist before merging - [ ] Do not forget to reformat commit message to not include the above checklist --------- Co-authored-by: Konstantin Knizhnik <knizhnik@neon.tech>
This commit is contained in:
committed by
GitHub
parent
b6ec11ad78
commit
0dc4c9b0b8
@@ -40,11 +40,23 @@ typedef struct
|
||||
{
|
||||
RelTag tag;
|
||||
BlockNumber size;
|
||||
dlist_node lru_node; /* LRU list node */
|
||||
} RelSizeEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t size;
|
||||
uint64 hits;
|
||||
uint64 misses;
|
||||
uint64 writes;
|
||||
dlist_head lru; /* double linked list for LRU replacement
|
||||
* algorithm */
|
||||
} RelSizeHashControl;
|
||||
|
||||
static HTAB *relsize_hash;
|
||||
static LWLockId relsize_lock;
|
||||
static int relsize_hash_size;
|
||||
static RelSizeHashControl* relsize_ctl;
|
||||
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
|
||||
#if PG_VERSION_NUM >= 150000
|
||||
static shmem_request_hook_type prev_shmem_request_hook = NULL;
|
||||
@@ -52,7 +64,7 @@ static void relsize_shmem_request(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Size of a cache entry is 20 bytes. So this default will take about 1.2 MB,
|
||||
* Size of a cache entry is 36 bytes. So this default will take about 2.3 MB,
|
||||
* which seems reasonable.
|
||||
*/
|
||||
#define DEFAULT_RELSIZE_HASH_SIZE (64 * 1024)
|
||||
@@ -61,19 +73,29 @@ static void
|
||||
neon_smgr_shmem_startup(void)
|
||||
{
|
||||
static HASHCTL info;
|
||||
bool found;
|
||||
|
||||
if (prev_shmem_startup_hook)
|
||||
prev_shmem_startup_hook();
|
||||
|
||||
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
|
||||
relsize_lock = (LWLockId) GetNamedLWLockTranche("neon_relsize");
|
||||
info.keysize = sizeof(RelTag);
|
||||
info.entrysize = sizeof(RelSizeEntry);
|
||||
relsize_hash = ShmemInitHash("neon_relsize",
|
||||
relsize_hash_size, relsize_hash_size,
|
||||
&info,
|
||||
HASH_ELEM | HASH_BLOBS);
|
||||
LWLockRelease(AddinShmemInitLock);
|
||||
relsize_ctl = (RelSizeHashControl *) ShmemInitStruct("relsize_hash", sizeof(RelSizeHashControl), &found);
|
||||
if (!found)
|
||||
{
|
||||
relsize_lock = (LWLockId) GetNamedLWLockTranche("neon_relsize");
|
||||
info.keysize = sizeof(RelTag);
|
||||
info.entrysize = sizeof(RelSizeEntry);
|
||||
relsize_hash = ShmemInitHash("neon_relsize",
|
||||
relsize_hash_size, relsize_hash_size,
|
||||
&info,
|
||||
HASH_ELEM | HASH_BLOBS);
|
||||
LWLockRelease(AddinShmemInitLock);
|
||||
relsize_ctl->size = 0;
|
||||
relsize_ctl->hits = 0;
|
||||
relsize_ctl->misses = 0;
|
||||
relsize_ctl->writes = 0;
|
||||
dlist_init(&relsize_ctl->lru);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -93,7 +115,15 @@ get_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber *size)
|
||||
if (entry != NULL)
|
||||
{
|
||||
*size = entry->size;
|
||||
relsize_ctl->hits += 1;
|
||||
found = true;
|
||||
/* Move entry to the LRU list tail */
|
||||
dlist_delete(&entry->lru_node);
|
||||
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
relsize_ctl->misses += 1;
|
||||
}
|
||||
LWLockRelease(relsize_lock);
|
||||
}
|
||||
@@ -107,12 +137,43 @@ set_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
|
||||
{
|
||||
RelTag tag;
|
||||
RelSizeEntry *entry;
|
||||
bool found = false;
|
||||
|
||||
tag.rinfo = rinfo;
|
||||
tag.forknum = forknum;
|
||||
LWLockAcquire(relsize_lock, LW_EXCLUSIVE);
|
||||
entry = hash_search(relsize_hash, &tag, HASH_ENTER, NULL);
|
||||
/*
|
||||
* This should actually never happen! Below we check if hash is full and delete least recently user item in this case.
|
||||
* But for further safety we also perform check here.
|
||||
*/
|
||||
while ((entry = hash_search(relsize_hash, &tag, HASH_ENTER_NULL, &found)) == NULL)
|
||||
{
|
||||
RelSizeEntry *victim = dlist_container(RelSizeEntry, lru_node, dlist_pop_head_node(&relsize_ctl->lru));
|
||||
hash_search(relsize_hash, &victim->tag, HASH_REMOVE, NULL);
|
||||
Assert(relsize_ctl->size > 0);
|
||||
relsize_ctl->size -= 1;
|
||||
}
|
||||
entry->size = size;
|
||||
if (!found)
|
||||
{
|
||||
if (++relsize_ctl->size == relsize_hash_size)
|
||||
{
|
||||
/*
|
||||
* Remove least recently used elment from the hash.
|
||||
* Hash size after is becomes `relsize_hash_size-1`.
|
||||
* But it is not considered to be a problem, because size of this hash is expecrted large enough and +-1 doesn't matter.
|
||||
*/
|
||||
RelSizeEntry *victim = dlist_container(RelSizeEntry, lru_node, dlist_pop_head_node(&relsize_ctl->lru));
|
||||
hash_search(relsize_hash, &victim->tag, HASH_REMOVE, NULL);
|
||||
relsize_ctl->size -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dlist_delete(&entry->lru_node);
|
||||
}
|
||||
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
|
||||
relsize_ctl->writes += 1;
|
||||
LWLockRelease(relsize_lock);
|
||||
}
|
||||
}
|
||||
@@ -132,6 +193,21 @@ update_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
|
||||
entry = hash_search(relsize_hash, &tag, HASH_ENTER, &found);
|
||||
if (!found || entry->size < size)
|
||||
entry->size = size;
|
||||
if (!found)
|
||||
{
|
||||
if (++relsize_ctl->size == relsize_hash_size)
|
||||
{
|
||||
RelSizeEntry *victim = dlist_container(RelSizeEntry, lru_node, dlist_pop_head_node(&relsize_ctl->lru));
|
||||
hash_search(relsize_hash, &victim->tag, HASH_REMOVE, NULL);
|
||||
relsize_ctl->size -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dlist_delete(&entry->lru_node);
|
||||
}
|
||||
relsize_ctl->writes += 1;
|
||||
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
|
||||
LWLockRelease(relsize_lock);
|
||||
}
|
||||
}
|
||||
@@ -142,11 +218,16 @@ forget_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum)
|
||||
if (relsize_hash_size > 0)
|
||||
{
|
||||
RelTag tag;
|
||||
|
||||
RelSizeEntry *entry;
|
||||
tag.rinfo = rinfo;
|
||||
tag.forknum = forknum;
|
||||
LWLockAcquire(relsize_lock, LW_EXCLUSIVE);
|
||||
hash_search(relsize_hash, &tag, HASH_REMOVE, NULL);
|
||||
entry = hash_search(relsize_hash, &tag, HASH_REMOVE, NULL);
|
||||
if (entry)
|
||||
{
|
||||
dlist_delete(&entry->lru_node);
|
||||
relsize_ctl->size -= 1;
|
||||
}
|
||||
LWLockRelease(relsize_lock);
|
||||
}
|
||||
}
|
||||
@@ -191,7 +272,7 @@ relsize_shmem_request(void)
|
||||
if (prev_shmem_request_hook)
|
||||
prev_shmem_request_hook();
|
||||
|
||||
RequestAddinShmemSpace(hash_estimate_size(relsize_hash_size, sizeof(RelSizeEntry)));
|
||||
RequestAddinShmemSpace(sizeof(RelSizeHashControl) + hash_estimate_size(relsize_hash_size, sizeof(RelSizeEntry)));
|
||||
RequestNamedLWLockTranche("neon_relsize", 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user