Pin information about unlogged relations in relsize cache until end of the build

This commit is contained in:
Konstantin Knizhnik
2024-04-25 17:23:21 +03:00
parent 1bd86c5c6a
commit 520101170f
2 changed files with 124 additions and 36 deletions

View File

@@ -1535,6 +1535,11 @@ neon_wallog_page(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, co
SetLastWrittenLSNForBlock(lsn, InfoFromSMgrRel(reln), forknum, blocknum);
}
/*
* Check if unlogged build is in progress for specified relation
* and stop it if so. It is used as callback for log_newpage_range( function
* which is called at the end of unlogged build.
*/
static void
neon_log_newpage_range_callback(Relation rel, ForkNumber forknum)
{
@@ -1548,6 +1553,8 @@ neon_log_newpage_range_callback(Relation rel, ForkNumber forknum)
}
/*
* neon_init() -- Initialize private state
*/

View File

@@ -118,9 +118,12 @@ get_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber *size)
*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);
if (!entry->unlogged) /* entries of relation involved in unlogged build are pinned */
{
/* Move entry to the LRU list tail */
dlist_delete(&entry->lru_node);
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
}
}
else
{
@@ -149,32 +152,52 @@ set_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
*/
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;
if (dlist_is_empty(&relsize_ctl->lru))
{
elog(FATAL, "No more free relsize cache entries");
}
else
{
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)
{
entry->unlogged = false;
if (++relsize_ctl->size == relsize_hash_size)
if (relsize_ctl->size+1 == 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;
if (dlist_is_empty(&relsize_ctl->lru))
{
elog(FATAL, "No more free relsize cache entries");
}
else
{
RelSizeEntry *victim = dlist_container(RelSizeEntry, lru_node, dlist_pop_head_node(&relsize_ctl->lru));
hash_search(relsize_hash, &victim->tag, HASH_REMOVE, NULL);
}
}
else
{
relsize_ctl->size += 1;
}
}
else
else if (!entry->unlogged) /* entries of relation involved in unlogged build are pinned */
{
dlist_delete(&entry->lru_node);
}
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
if (!entry->unlogged) /* entries of relation involved in unlogged build are pinned */
{
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
}
relsize_ctl->writes += 1;
LWLockRelease(relsize_lock);
}
@@ -197,11 +220,21 @@ update_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
entry->unlogged = false;
entry->size = size;
if (++relsize_ctl->size == relsize_hash_size)
if (relsize_ctl->size+1 == 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;
if (dlist_is_empty(&relsize_ctl->lru))
{
elog(FATAL, "No more free relsize cache entries");
}
else
{
RelSizeEntry *victim = dlist_container(RelSizeEntry, lru_node, dlist_pop_head_node(&relsize_ctl->lru));
hash_search(relsize_hash, &victim->tag, HASH_REMOVE, NULL);
}
}
else
{
relsize_ctl->size += 1;
}
}
else
@@ -209,10 +242,16 @@ update_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
if (entry->size < size)
entry->size = size;
dlist_delete(&entry->lru_node);
if (!entry->unlogged) /* entries of relation involved in unlogged build are pinned */
{
dlist_delete(&entry->lru_node);
}
}
relsize_ctl->writes += 1;
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
if (!entry->unlogged) /* entries of relation involved in unlogged build are pinned */
{
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
}
LWLockRelease(relsize_lock);
}
}
@@ -230,13 +269,28 @@ forget_cached_relsize(NRelFileInfo rinfo, ForkNumber forknum)
entry = hash_search(relsize_hash, &tag, HASH_REMOVE, NULL);
if (entry)
{
dlist_delete(&entry->lru_node);
if (!entry->unlogged)
{
/* Entried of relations involved in unlogged build are pinned */
dlist_delete(&entry->lru_node);
}
relsize_ctl->size -= 1;
}
LWLockRelease(relsize_lock);
}
}
/*
* This function starts unlogged build if it was not yet started.
* The criteria for starting iunlogged build is writing page without normal LSN.
* It can happen in any backend when page is evicted from shared buffers.
* Or can not happen at all if index fits in shared buffers.
*
* If this function really starts unlogged build, then it returns true, remove entry from LRU list
* (protecting it from eviction until the end of unlogged build) and keeps lock on relsize hash.
* This lock should be later released using resume_unlogged_build(). It allows caller to perform some actions
* in critical section, for example right now it create relation on the disk using mdcreate
*/
bool
start_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
{
@@ -255,11 +309,21 @@ start_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
entry->size = size;
start = true;
if (++relsize_ctl->size == relsize_hash_size)
if (relsize_ctl->size+1 == 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;
if (dlist_is_empty(&relsize_ctl->lru))
{
elog(FATAL, "No more free relsize cache entries");
}
else
{
RelSizeEntry *victim = dlist_container(RelSizeEntry, lru_node, dlist_pop_head_node(&relsize_ctl->lru));
hash_search(relsize_hash, &victim->tag, HASH_REMOVE, NULL);
}
}
else
{
relsize_ctl->size += 1;
}
}
else
@@ -269,11 +333,19 @@ start_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
if (entry->size < size)
entry->size = size;
dlist_delete(&entry->lru_node);
if (start)
{
/* relation involved in unlogged build are pinned until the end of the build */
dlist_delete(&entry->lru_node);
}
}
entry->unlogged = true;
relsize_ctl->writes += 1;
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
/*
* We are not putting entry in LRU least to prevent it fro eviction until the end of unlogged build
*/
if (!start)
LWLockRelease(relsize_lock);
else
@@ -284,6 +356,12 @@ start_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum, BlockNumber size)
return start;
}
/*
* Check if unlogged build is in progress.
* If so, true is returns and lock on relsize cache is hold.
* It should be later released by called using resume_unlogged_build().
* It allows to read page from local file without risk that it is removed by stop_unlogged_build by some other backend.
*/
bool
is_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum)
{
@@ -302,9 +380,6 @@ is_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum)
{
unlogged = entry->unlogged;
relsize_ctl->hits += 1;
/* Move entry to the LRU list tail */
dlist_delete(&entry->lru_node);
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
}
else
{
@@ -316,6 +391,9 @@ is_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum)
return unlogged;
}
/*
* Check if unlogged build is in progress and if so, clear th flag, return entry to LRU list and return true.
*/
bool
stop_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum)
{
@@ -333,15 +411,15 @@ stop_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum)
if (entry != NULL)
{
unlogged = entry->unlogged;
if (unlogged)
elog(LOG, "Stop unlogged build for %u/%u/%u.%u",
RelFileInfoFmt(rinfo), forknum);
entry->unlogged = false;
relsize_ctl->hits += 1;
/* Move entry to the LRU list tail */
dlist_delete(&entry->lru_node);
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
/* use isRedo == true, so that we drop it immediately */
if (unlogged)
{
elog(LOG, "Stop unlogged build for %u/%u/%u.%u",
RelFileInfoFmt(rinfo), forknum);
/* Return entry to the LRU list */
dlist_push_tail(&relsize_ctl->lru, &entry->lru_node);
}
}
else
{
@@ -352,6 +430,9 @@ stop_unlogged_build(NRelFileInfo rinfo, ForkNumber forknum)
return unlogged;
}
/*
* Release lock obtained by start_unlogged_build or is_unlogged-build functions
*/
void
resume_unlogged_build(void)
{