forked from huawei/openGauss-server
403 lines
15 KiB
C++
403 lines
15 KiB
C++
/*
|
|
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
|
|
*
|
|
* openGauss is licensed under Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
*
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PSL v2 for more details.
|
|
* ---------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "access/transam.h"
|
|
#include "executor/executor.h"
|
|
#include "miscadmin.h"
|
|
#include "postgres.h"
|
|
#include "utils/knl_catcache.h"
|
|
#include "utils/knl_globalbasedefcache.h"
|
|
#include "utils/knl_globaldbstatmanager.h"
|
|
#include "utils/knl_partcache.h"
|
|
#include "utils/memutils.h"
|
|
|
|
static uint64 GetBitmapSetSize(int nwords)
|
|
{
|
|
return (offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword));
|
|
}
|
|
|
|
static uint64 GetRelEstimateSize(GlobalRelationEntry *entry)
|
|
{
|
|
uint64 rel_size =
|
|
((AllocSet)entry->rel_mem_manager)->totalSpace + /* memcxt total space */
|
|
sizeof(GlobalRelationEntry) + CHUNK_ALGIN_PAD + /* palloc GlobalRelationEntry with chunk head */
|
|
sizeof(AllocSetContext) + CHUNK_ALGIN_PAD; /* create MemoryContext with chunk head */
|
|
return rel_size;
|
|
}
|
|
|
|
static uint64 GetPartEstimateSize(GlobalPartitionEntry *entry)
|
|
{
|
|
/* every palloc attached with a chunk head */
|
|
uint64 part_size =
|
|
sizeof(GlobalPartitionEntry) + CHUNK_ALGIN_PAD + /* palloc GlobalPartitionEntry with chunk head */
|
|
sizeof(PartitionData) + CHUNK_ALGIN_PAD + /* palloc PartitionData with chunk head */
|
|
PARTITION_TUPLE_SIZE + CHUNK_ALGIN_PAD + /* palloc pd_part with chunk head */
|
|
(entry->part->rd_options == NULL ? 0 : (VARSIZE(entry->part->rd_options) + CHUNK_ALGIN_PAD)) +
|
|
/* palloc rd_options with chunk head */
|
|
(entry->part->pd_indexattr == NULL ? 0 : /* palloc pd_indexattr with chunk head */
|
|
(GetBitmapSetSize(entry->part->pd_indexattr->nwords) + CHUNK_ALGIN_PAD)) +
|
|
(entry->part->pd_indexlist == NULL ? 0 : /* palloc pd_indexlist with chunk head, and its elements */
|
|
sizeof(List) + CHUNK_ALGIN_PAD +
|
|
(sizeof(ListCell) + CHUNK_ALGIN_PAD) * entry->part->pd_indexlist->length);
|
|
return part_size;
|
|
}
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::RemoveElemFromBucket(GlobalBaseEntry *base)
|
|
{
|
|
if (is_relation) {
|
|
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
|
uint64 rel_size = GetRelEstimateSize(entry);
|
|
pg_atomic_fetch_sub_u64(&m_base_space, rel_size);
|
|
m_db_entry->MemoryEstimateSub(rel_size);
|
|
} else {
|
|
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
|
uint64 part_size = GetPartEstimateSize(entry);
|
|
pg_atomic_fetch_sub_u64(&m_base_space, part_size);
|
|
m_db_entry->MemoryEstimateSub(part_size);
|
|
}
|
|
m_bucket_list.RemoveElemFromBucket(&base->cache_elem);
|
|
}
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::AddHeadToBucket(Index hash_index, GlobalBaseEntry *base)
|
|
{
|
|
if (is_relation) {
|
|
GlobalRelationEntry *entry = (GlobalRelationEntry *)base;
|
|
uint64 rel_size = GetRelEstimateSize(entry);
|
|
pg_atomic_fetch_add_u64(&m_base_space, rel_size);
|
|
m_db_entry->MemoryEstimateAdd(rel_size);
|
|
} else {
|
|
GlobalPartitionEntry *entry = (GlobalPartitionEntry *)base;
|
|
uint64 part_size = GetPartEstimateSize(entry);
|
|
pg_atomic_fetch_add_u64(&m_base_space, part_size);
|
|
m_db_entry->MemoryEstimateAdd(part_size);
|
|
}
|
|
m_bucket_list.AddHeadToBucket(hash_index, &base->cache_elem);
|
|
}
|
|
|
|
template void GlobalBaseDefCache::AddHeadToBucket<true>(Index hash_index, GlobalBaseEntry *base);
|
|
template void GlobalBaseDefCache::AddHeadToBucket<false>(Index hash_index, GlobalBaseEntry *base);
|
|
template void GlobalBaseDefCache::RemoveElemFromBucket<true>(GlobalBaseEntry *base);
|
|
template void GlobalBaseDefCache::RemoveElemFromBucket<false>(GlobalBaseEntry *base);
|
|
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseEntry::Free(GlobalBaseEntry *entry)
|
|
{
|
|
Assert(entry->refcount == 0);
|
|
if (is_relation) {
|
|
Assert(entry->type == GLOBAL_RELATION_ENTRY);
|
|
if (((GlobalRelationEntry *)entry)->rel_mem_manager != NULL) {
|
|
MemoryContextDelete(((GlobalRelationEntry *)entry)->rel_mem_manager);
|
|
}
|
|
} else {
|
|
Assert(entry->type == GLOBAL_PARTITION_ENTRY);
|
|
if (((GlobalPartitionEntry *)entry)->part != NULL) {
|
|
PartitionDestroyPartition(((GlobalPartitionEntry *)entry)->part);
|
|
}
|
|
}
|
|
pfree(entry);
|
|
}
|
|
template void GlobalBaseEntry::Free<true>(GlobalBaseEntry *entry);
|
|
template void GlobalBaseEntry::Free<false>(GlobalBaseEntry *entry);
|
|
|
|
void GlobalBaseEntry::Release()
|
|
{
|
|
/* we dont free entry here, free when search */
|
|
Assert(this->refcount > 0);
|
|
(void)pg_atomic_fetch_sub_u64(&this->refcount, 1);
|
|
}
|
|
|
|
void GlobalBaseDefCache::InitHashTable()
|
|
{
|
|
MemoryContext old = MemoryContextSwitchTo(m_db_entry->GetRandomMemCxt());
|
|
m_bucket_list.Init(m_nbuckets);
|
|
m_obj_locks = (pthread_rwlock_t *)palloc0(sizeof(pthread_rwlock_t) * m_nbuckets);
|
|
for (int i = 0; i < m_nbuckets; i++) {
|
|
PthreadRwLockInit(&m_obj_locks[i], NULL);
|
|
}
|
|
|
|
m_is_swappingouts = (volatile uint32 *)palloc0(sizeof(volatile uint32) * m_nbuckets);
|
|
|
|
/* acquire more oid locks for concurrent ddl */
|
|
if (!m_is_shared) {
|
|
m_oid_locks = (pthread_rwlock_t *)palloc0(sizeof(pthread_rwlock_t) * m_nbuckets);
|
|
for (int i = 0; i < m_nbuckets; i++) {
|
|
PthreadRwLockInit(&m_oid_locks[i], NULL);
|
|
}
|
|
} else {
|
|
m_oid_locks = NULL;
|
|
}
|
|
(void)MemoryContextSwitchTo(old);
|
|
}
|
|
|
|
void GlobalBaseDefCache::Init(int nbucket)
|
|
{
|
|
Assert(!m_is_inited);
|
|
m_nbuckets = ResizeHashBucket(nbucket, g_instance.global_sysdbcache.dynamic_hash_bucket_strategy);
|
|
InitHashTable();
|
|
}
|
|
|
|
GlobalBaseEntry *GlobalBaseDefCache::SearchReadOnly(Oid obj_oid, uint32 hash_value)
|
|
{
|
|
pg_atomic_fetch_add_u64(m_searches, 1);
|
|
Index hash_index = HASH_INDEX(hash_value, (uint32)m_nbuckets);
|
|
pthread_rwlock_t *obj_lock = &m_obj_locks[hash_index];
|
|
|
|
int location = INVALID_LOCATION;
|
|
PthreadRWlockRdlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
GlobalBaseEntry *entry = FindEntryWithIndex(obj_oid, hash_index, &location);
|
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
if (entry == NULL) {
|
|
return NULL;
|
|
}
|
|
pg_atomic_fetch_add_u64(m_hits, 1);
|
|
TopnLruMoveToFront(&entry->cache_elem, m_bucket_list.GetBucket(hash_index), obj_lock, location);
|
|
return entry;
|
|
}
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::FreeDeadEntrys()
|
|
{
|
|
while (m_dead_entries.GetLength() > 0) {
|
|
Dlelem *elt = m_dead_entries.RemoveHead();
|
|
if (elt == NULL) {
|
|
break;
|
|
}
|
|
GlobalBaseEntry *entry = (GlobalBaseEntry *)DLE_VAL(elt);
|
|
if (entry->refcount != 0) {
|
|
/* we move the active entry to tail of list and let next call free it */
|
|
m_dead_entries.AddTail(&entry->cache_elem);
|
|
break;
|
|
} else {
|
|
entry->Free<is_relation>(entry);
|
|
}
|
|
}
|
|
}
|
|
template void GlobalBaseDefCache::FreeDeadEntrys<false>();
|
|
template void GlobalBaseDefCache::FreeDeadEntrys<true>();
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::Invalidate(Oid dbid, Oid obj_oid)
|
|
{
|
|
uint32 hash_value = oid_hash((void *)&(obj_oid), sizeof(Oid));
|
|
pthread_rwlock_t *oid_lock = NULL;
|
|
bool need_oid_lock = !is_relation || !IsSystemObjOid(obj_oid);
|
|
if (need_oid_lock) {
|
|
oid_lock = GetHashValueLock(hash_value);
|
|
PthreadRWlockWrlock(LOCAL_SYSDB_RESOWNER, oid_lock);
|
|
}
|
|
|
|
Index hash_index = HASH_INDEX(hash_value, (uint32)m_nbuckets);
|
|
pthread_rwlock_t *obj_lock = &m_obj_locks[hash_index];
|
|
PthreadRWlockWrlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
for (Dlelem *elt = DLGetHead(m_bucket_list.GetBucket(hash_index)); elt != NULL;) {
|
|
GlobalBaseEntry *entry = (GlobalBaseEntry *)DLE_VAL(elt);
|
|
elt = DLGetSucc(elt);
|
|
if (entry->oid != obj_oid) {
|
|
continue;
|
|
}
|
|
HandleDeadEntry<is_relation>(entry);
|
|
}
|
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
if (need_oid_lock) {
|
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, oid_lock);
|
|
}
|
|
}
|
|
template void GlobalBaseDefCache::Invalidate<true>(Oid dbid, Oid obj_oid);
|
|
template void GlobalBaseDefCache::Invalidate<false>(Oid dbid, Oid obj_oid);
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::InvalidateRelationNodeListBy(bool (*IsInvalidEntry)(GlobalBaseEntry *))
|
|
{
|
|
for (int hash_index = 0; hash_index < m_nbuckets; hash_index++) {
|
|
pthread_rwlock_t *obj_lock = &m_obj_locks[hash_index];
|
|
PthreadRWlockWrlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
for (Dlelem *elt = DLGetHead(m_bucket_list.GetBucket(hash_index)); elt != NULL;) {
|
|
GlobalBaseEntry *entry = (GlobalBaseEntry *)DLE_VAL(elt);
|
|
elt = DLGetSucc(elt);
|
|
if (IsInvalidEntry(entry)) {
|
|
HandleDeadEntry<is_relation>(entry);
|
|
}
|
|
}
|
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
}
|
|
}
|
|
template void GlobalBaseDefCache::InvalidateRelationNodeListBy<true>(bool (*IsInvalidEntry)(GlobalBaseEntry *));
|
|
|
|
template <bool is_relation, bool force>
|
|
void GlobalBaseDefCache::ResetCaches()
|
|
{
|
|
for (int hash_index = 0; hash_index < m_nbuckets; hash_index++) {
|
|
/* if not force, we are swappingout, oid lock is not needed */
|
|
if (force && !m_is_shared) {
|
|
PthreadRWlockWrlock(LOCAL_SYSDB_RESOWNER, &m_oid_locks[hash_index]);
|
|
}
|
|
|
|
pthread_rwlock_t *obj_lock = &m_obj_locks[hash_index];
|
|
PthreadRWlockWrlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
for (Dlelem *elt = DLGetHead(m_bucket_list.GetBucket(hash_index)); elt;) {
|
|
GlobalBaseEntry *entry = (GlobalBaseEntry *)DLE_VAL(elt);
|
|
elt = DLGetSucc(elt);
|
|
if (force || entry->refcount == 0) {
|
|
HandleDeadEntry<is_relation>(entry);
|
|
}
|
|
}
|
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, obj_lock);
|
|
|
|
if (force && !m_is_shared) {
|
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, &m_oid_locks[hash_index]);
|
|
}
|
|
}
|
|
}
|
|
template void GlobalBaseDefCache::ResetCaches<false, false>();
|
|
template void GlobalBaseDefCache::ResetCaches<false, true>();
|
|
template void GlobalBaseDefCache::ResetCaches<true, false>();
|
|
template void GlobalBaseDefCache::ResetCaches<true, true>();
|
|
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::HandleDeadEntry(GlobalBaseEntry *entry)
|
|
{
|
|
RemoveElemFromBucket<is_relation>(entry);
|
|
if (entry->refcount == 0) {
|
|
m_dead_entries.AddHead(&entry->cache_elem);
|
|
} else {
|
|
m_dead_entries.AddTail(&entry->cache_elem);
|
|
}
|
|
}
|
|
template void GlobalBaseDefCache::HandleDeadEntry<false>(GlobalBaseEntry *entry);
|
|
template void GlobalBaseDefCache::HandleDeadEntry<true>(GlobalBaseEntry *entry);
|
|
|
|
GlobalBaseEntry *GlobalBaseDefCache::FindEntryWithIndex(Oid obj_oid, Index hash_index, int *location)
|
|
{
|
|
int index = 0;
|
|
for (Dlelem *elt = DLGetHead(m_bucket_list.GetBucket(hash_index)); elt != NULL; elt = DLGetSucc(elt)) {
|
|
index++;
|
|
GlobalBaseEntry *entry = (GlobalBaseEntry *)DLE_VAL(elt);
|
|
if (entry->oid != obj_oid) {
|
|
continue;
|
|
}
|
|
pg_atomic_fetch_add_u64(&entry->refcount, 1);
|
|
*location = index;
|
|
return entry;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool GlobalBaseDefCache::EntryExist(Oid obj_oid, Index hash_index)
|
|
{
|
|
for (Dlelem *elt = DLGetHead(m_bucket_list.GetBucket(hash_index)); elt != NULL; elt = DLGetSucc(elt)) {
|
|
GlobalBaseEntry *entry = (GlobalBaseEntry *)DLE_VAL(elt);
|
|
if (entry->oid != obj_oid) {
|
|
continue;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::RemoveTailElements(Index hash_index)
|
|
{
|
|
/* shared db never do lru on tabdef */
|
|
if (m_is_shared) {
|
|
return;
|
|
}
|
|
|
|
/* only one thread can do swapout for the bucket */
|
|
ResourceOwnerEnlargeGlobalIsExclusive(LOCAL_SYSDB_RESOWNER);
|
|
if (!atomic_compare_exchange_u32(&m_is_swappingouts[hash_index], 0, 1)) {
|
|
return;
|
|
}
|
|
ResourceOwnerRememberGlobalIsExclusive(LOCAL_SYSDB_RESOWNER, &m_is_swappingouts[hash_index]);
|
|
|
|
bool listBelowThreshold = m_bucket_list.GetBucket(hash_index)->dll_len < MAX_GSC_LIST_LENGTH;
|
|
|
|
uint64 swapout_count_once = 0;
|
|
PthreadRWlockWrlock(LOCAL_SYSDB_RESOWNER, &m_obj_locks[hash_index]);
|
|
uint64 max_swapout_count_once = GetSwapOutNum(listBelowThreshold, m_bucket_list.GetBucket(hash_index)->dll_len);
|
|
for (Dlelem *elt = DLGetTail(m_bucket_list.GetBucket(hash_index)); elt != NULL;) {
|
|
Dlelem *tmp = elt;
|
|
elt = DLGetPred(elt);
|
|
if (is_relation) {
|
|
GlobalRelationEntry *entry = (GlobalRelationEntry *)DLE_VAL(tmp);
|
|
if (g_instance.global_sysdbcache.RelationHasSysCache(entry->rel->rd_id) ||
|
|
unlikely(!RelationHasReferenceCountZero(entry->rel))) {
|
|
Assert(entry->type == GLOBAL_RELATION_ENTRY);
|
|
DLMoveToFront(&entry->cache_elem);
|
|
break;
|
|
}
|
|
}
|
|
HandleDeadEntry<is_relation>((GlobalBaseEntry *)DLE_VAL(tmp));
|
|
swapout_count_once++;
|
|
|
|
/* keep elements as many as possible */
|
|
if (swapout_count_once == max_swapout_count_once) {
|
|
break;
|
|
}
|
|
}
|
|
PthreadRWlockUnlock(LOCAL_SYSDB_RESOWNER, &m_obj_locks[hash_index]);
|
|
|
|
Assert(m_is_swappingouts[hash_index] == 1);
|
|
atomic_compare_exchange_u32(&m_is_swappingouts[hash_index], 1, 0);
|
|
ResourceOwnerForgetGlobalIsExclusive(LOCAL_SYSDB_RESOWNER, &m_is_swappingouts[hash_index]);
|
|
}
|
|
template void GlobalBaseDefCache::RemoveTailElements<false>(Index hash_index);
|
|
template void GlobalBaseDefCache::RemoveTailElements<true>(Index hash_index);
|
|
|
|
template <bool is_relation>
|
|
void GlobalBaseDefCache::RemoveAllTailElements()
|
|
{
|
|
/* shared db never do lru on tabdef */
|
|
if (m_is_shared) {
|
|
return;
|
|
}
|
|
for (int hash_index = 0; hash_index < m_nbuckets; hash_index++) {
|
|
RemoveTailElements<is_relation>(hash_index);
|
|
#ifndef ENABLE_LITE_MODE
|
|
/* memory is under control, so stop swapout */
|
|
if (g_instance.global_sysdbcache.MemoryUnderControl()) {
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
template void GlobalBaseDefCache::RemoveAllTailElements<false>();
|
|
template void GlobalBaseDefCache::RemoveAllTailElements<true>();
|
|
|
|
GlobalBaseDefCache::GlobalBaseDefCache(Oid db_oid, bool is_shared, GlobalSysDBCacheEntry *entry, char relkind)
|
|
{
|
|
m_db_oid = db_oid;
|
|
m_is_shared = is_shared;
|
|
m_is_inited = false;
|
|
|
|
m_relkind = relkind;
|
|
if (m_relkind == PARTTYPE_PARTITIONED_RELATION) {
|
|
m_searches = &entry->m_dbstat->part_searches;
|
|
m_hits = &entry->m_dbstat->part_hits;
|
|
m_newloads = &entry->m_dbstat->part_newloads;
|
|
} else {
|
|
Assert(m_relkind == RELKIND_RELATION);
|
|
m_searches = &entry->m_dbstat->rel_searches;
|
|
m_hits = &entry->m_dbstat->rel_hits;
|
|
m_newloads = &entry->m_dbstat->rel_newloads;
|
|
}
|
|
m_base_space = 0;
|
|
m_obj_locks = NULL;
|
|
m_db_entry = entry;
|
|
} |