mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 10:00:38 +01:00
DROP PROCEDURE support, added plumb cleanup of erased objects from cache, fixed gc in cache
This commit is contained in:
parent
cacea5d54b
commit
38ffaf4a1f
@ -1844,9 +1844,6 @@ public:
|
||||
static void deallocate(void* block) noexcept;
|
||||
bool validate(char* buf, FB_SIZE_T size);
|
||||
|
||||
// Create memory pool instance
|
||||
// static MemPool* createPool(MemPool* parent, MemoryStats& stats ALLOC_PARAMS);
|
||||
|
||||
MemoryStats& getStatsGroup() noexcept
|
||||
{
|
||||
return *stats;
|
||||
|
@ -1006,4 +1006,6 @@ namespace Firebird {
|
||||
static IMessageMetadata* const DELAYED_OUT_FORMAT = reinterpret_cast<IMessageMetadata*>(1);
|
||||
}
|
||||
|
||||
//#define DEBUG_LOST_POOLS 1
|
||||
|
||||
#endif /* COMMON_COMMON_H */
|
||||
|
@ -3387,9 +3387,13 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
|
||||
AutoSavePoint savePoint(tdbb, transaction);
|
||||
bool found = false;
|
||||
|
||||
//MetadataCache::oldVersion(tdbb, obj_procedure, id); missing ID in the node
|
||||
MetadataCache::lookup_procedure(tdbb, QualifiedName(name, package), CacheFlag::AUTOCREATE);
|
||||
|
||||
dropParameters(tdbb, transaction, name, package);
|
||||
|
||||
AutoCacheRequest requestHandle(tdbb, drq_e_prcs2, DYN_REQUESTS);
|
||||
MetaId id;
|
||||
|
||||
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
|
||||
PRC IN RDB$PROCEDURES
|
||||
@ -3409,12 +3413,12 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
|
||||
DDL_TRIGGER_DROP_PROCEDURE, name, NULL);
|
||||
}
|
||||
|
||||
id = PRC.RDB$PROCEDURE_ID;
|
||||
ERASE PRC;
|
||||
found = true;
|
||||
|
||||
if (!PRC.RDB$SECURITY_CLASS.NULL)
|
||||
deleteSecurityClass(tdbb, transaction, PRC.RDB$SECURITY_CLASS);
|
||||
|
||||
found = true;
|
||||
}
|
||||
END_FOR
|
||||
|
||||
@ -3437,10 +3441,15 @@ void DropProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
|
||||
END_FOR
|
||||
}
|
||||
|
||||
if (found && package.isEmpty())
|
||||
if (found)
|
||||
{
|
||||
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE,
|
||||
name, NULL);
|
||||
MetadataCache::erase(tdbb, obj_procedure, id);
|
||||
|
||||
if (package.isEmpty())
|
||||
{
|
||||
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PROCEDURE,
|
||||
name, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
savePoint.release(); // everything is ok
|
||||
|
@ -110,3 +110,9 @@ MemoryPool& CachePool::get(thread_db* tdbb)
|
||||
family, name ? name : "", name ? " " : "", id);
|
||||
}
|
||||
|
||||
void ElementBase::commitErase(thread_db* tdbb)
|
||||
{
|
||||
auto* mdc = tdbb->getDatabase()->dbb_mdc;
|
||||
mdc->objectCleanup(TransactionNumber::current(tdbb), this);
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,6 @@
|
||||
#ifndef JRD_HAZARDPTR_H
|
||||
#define JRD_HAZARDPTR_H
|
||||
|
||||
#define HZ_DEB(A)
|
||||
|
||||
#include "../common/classes/alloc.h"
|
||||
#include "../common/classes/array.h"
|
||||
#include "../common/gdsassert.h"
|
||||
@ -436,13 +434,14 @@ public:
|
||||
public:
|
||||
virtual ~ElementBase();
|
||||
virtual void resetDependentObject(thread_db* tdbb, ResetType rt) = 0;
|
||||
virtual void eraseObject(thread_db* tdbb) = 0; // erase object
|
||||
virtual void cleanup(thread_db* tdbb) = 0;
|
||||
|
||||
public:
|
||||
void resetDependentObjects(thread_db* tdbb, TraNumber olderThan);
|
||||
void addDependentObject(thread_db* tdbb, ElementBase* dep);
|
||||
void removeDependentObject(thread_db* tdbb, ElementBase* dep);
|
||||
[[noreturn]] void busyError(thread_db* tdbb, MetaId id, const char* name, const char* family);
|
||||
void commitErase(thread_db* tdbb);
|
||||
};
|
||||
|
||||
namespace CacheFlag
|
||||
@ -453,8 +452,7 @@ namespace CacheFlag
|
||||
static const ObjectBase::Flag AUTOCREATE = 0x08;
|
||||
static const ObjectBase::Flag NOCOMMIT = 0x10;
|
||||
static const ObjectBase::Flag RET_ERASED = 0x20;
|
||||
|
||||
static const ObjectBase::Flag IGNORE_MASK = COMMITTED | ERASED;
|
||||
static const ObjectBase::Flag RETIRED = 0x40;
|
||||
}
|
||||
|
||||
|
||||
@ -578,13 +576,15 @@ public:
|
||||
~ListEntry()
|
||||
{
|
||||
fb_assert(!object);
|
||||
fb_assert(!next);
|
||||
}
|
||||
|
||||
void cleanup(thread_db* tdbb)
|
||||
{
|
||||
OBJ::destroy(tdbb, object);
|
||||
object = nullptr;
|
||||
if (object) // take into an account ERASED entries
|
||||
{
|
||||
OBJ::destroy(tdbb, object);
|
||||
object = nullptr;
|
||||
}
|
||||
|
||||
auto* ptr = next.load(atomics::memory_order_relaxed);
|
||||
if (ptr)
|
||||
@ -682,24 +682,33 @@ public:
|
||||
}
|
||||
|
||||
// remove too old objects - they are anyway can't be in use
|
||||
static TraNumber gc(thread_db* tdbb, atomics::atomic<ListEntry*>& list, const TraNumber oldest)
|
||||
static TraNumber gc(thread_db* tdbb, atomics::atomic<ListEntry*>* list, const TraNumber oldest)
|
||||
{
|
||||
TraNumber rc = 0;
|
||||
for (HazardPtr<ListEntry> entry(list); entry; entry.set(entry->next))
|
||||
for (HazardPtr<ListEntry> entry(*list); entry; list = &entry->next, entry.set(*list))
|
||||
{
|
||||
if ((entry->getFlags() & CacheFlag::COMMITTED) && entry->traNumber < oldest)
|
||||
if (!(entry->getFlags() & CacheFlag::COMMITTED))
|
||||
continue;
|
||||
|
||||
if (rc && entry->traNumber < oldest)
|
||||
{
|
||||
if (entry->cacheFlags.fetch_or(CacheFlag::ERASED) & CacheFlag::ERASED)
|
||||
if (entry->cacheFlags.fetch_or(CacheFlag::RETIRED) & CacheFlag::RETIRED)
|
||||
break; // someone else also performs GC
|
||||
|
||||
// split remaining list off
|
||||
if (entry.replace(list, nullptr))
|
||||
if (entry.replace(*list, nullptr))
|
||||
{
|
||||
while (entry && !(entry->cacheFlags.fetch_or(CacheFlag::ERASED) & CacheFlag::ERASED))
|
||||
while (entry)// && !(entry->cacheFlags.fetch_or(CacheFlag::RETIRED) & CacheFlag::RETIRED))
|
||||
{
|
||||
if (entry->object)
|
||||
{
|
||||
OBJ::destroy(tdbb, entry->object);
|
||||
entry->object = nullptr;
|
||||
}
|
||||
entry->retire();
|
||||
OBJ::destroy(tdbb, entry->object);
|
||||
entry.set(entry->next);
|
||||
if (entry && (entry->cacheFlags.fetch_or(CacheFlag::RETIRED) & CacheFlag::RETIRED))
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -709,18 +718,20 @@ public:
|
||||
rc = entry->traNumber;
|
||||
}
|
||||
|
||||
return rc; // 0 is returned in a case when list becomes empty
|
||||
return rc; // 0 is returned in a case when list was empty
|
||||
}
|
||||
|
||||
// created earlier object is OK and should become visible to the world
|
||||
void commit(thread_db* tdbb, TraNumber currentTrans, TraNumber nextTrans)
|
||||
// created (erased) earlier object is OK and should become visible to the world
|
||||
// return true if object was erased
|
||||
bool commit(thread_db* tdbb, TraNumber currentTrans, TraNumber nextTrans)
|
||||
{
|
||||
fb_assert((getFlags() & CacheFlag::IGNORE_MASK) == 0);
|
||||
fb_assert((getFlags() & CacheFlag::COMMITTED) == 0);
|
||||
fb_assert(traNumber == currentTrans);
|
||||
|
||||
traNumber = nextTrans;
|
||||
version = VersionSupport::next(tdbb);
|
||||
cacheFlags |= CacheFlag::COMMITTED;
|
||||
auto flags = cacheFlags.fetch_or(CacheFlag::COMMITTED);
|
||||
return flags & CacheFlag::ERASED;
|
||||
}
|
||||
|
||||
// created earlier object is bad and should be destroyed
|
||||
@ -801,12 +812,14 @@ public:
|
||||
typedef V Versioned;
|
||||
typedef P Permanent;
|
||||
|
||||
typedef atomics::atomic<CacheElement*> AtomicElementPointer;
|
||||
|
||||
CacheElement(thread_db* tdbb, MemoryPool& p, MetaId id, MakeLock* makeLock) :
|
||||
Permanent(tdbb, p, id, makeLock), list(nullptr), resetAt(0)
|
||||
Permanent(tdbb, p, id, makeLock), list(nullptr), resetAt(0), ptrToClean(nullptr)
|
||||
{ }
|
||||
|
||||
CacheElement(MemoryPool& p) :
|
||||
Permanent(p), list(nullptr), resetAt(0)
|
||||
Permanent(p), list(nullptr), resetAt(0), ptrToClean(nullptr)
|
||||
{ }
|
||||
|
||||
static void cleanup(thread_db* tdbb, CacheElement* element)
|
||||
@ -818,6 +831,9 @@ public:
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
if (element->ptrToClean)
|
||||
*element->ptrToClean = nullptr;
|
||||
|
||||
if (!Permanent::destroy(tdbb, element))
|
||||
{
|
||||
// destroy() returns true if it completed removal of permamnet part (delete by pool)
|
||||
@ -826,6 +842,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup(thread_db* tdbb) override
|
||||
{
|
||||
cleanup(tdbb, this);
|
||||
}
|
||||
|
||||
void setCleanup(AtomicElementPointer* clearPtr)
|
||||
{
|
||||
ptrToClean = clearPtr;
|
||||
}
|
||||
|
||||
void reload(thread_db* tdbb)
|
||||
{
|
||||
HazardPtr<ListEntry<Versioned>> listEntry(list);
|
||||
@ -907,7 +933,7 @@ public:
|
||||
TraNumber oldest = TransactionNumber::oldestActive(tdbb);
|
||||
TraNumber oldResetAt = resetAt.load(atomics::memory_order_acquire);
|
||||
if (oldResetAt && oldResetAt < oldest)
|
||||
setNewResetAt(oldResetAt, ListEntry<Versioned>::gc(tdbb, list, oldest));
|
||||
setNewResetAt(oldResetAt, ListEntry<Versioned>::gc(tdbb, &list, oldest));
|
||||
|
||||
TraNumber cur = TransactionNumber::current(tdbb);
|
||||
ListEntry<Versioned>* newEntry = FB_NEW_POOL(*getDefaultMemoryPool()) ListEntry<Versioned>(obj, cur, fl);
|
||||
@ -938,20 +964,23 @@ public:
|
||||
{
|
||||
HazardPtr<ListEntry<Versioned>> current(list);
|
||||
if (current)
|
||||
current->commit(tdbb, TransactionNumber::current(tdbb), TransactionNumber::next(tdbb));
|
||||
{
|
||||
if (current->commit(tdbb, TransactionNumber::current(tdbb), TransactionNumber::next(tdbb)))
|
||||
commitErase(tdbb);
|
||||
}
|
||||
}
|
||||
|
||||
void rollback(thread_db* tdbb)
|
||||
{
|
||||
ListEntry<Versioned>::rollback(tdbb, list, TransactionNumber::current(tdbb));
|
||||
}
|
||||
|
||||
/*
|
||||
void gc()
|
||||
{
|
||||
list.load()->assertCommitted();
|
||||
ListEntry<Versioned>::gc(list, MAX_TRA_NUMBER);
|
||||
ListEntry<Versioned>::gc(&list, MAX_TRA_NUMBER);
|
||||
}
|
||||
|
||||
*/
|
||||
void resetDependentObject(thread_db* tdbb, ResetType rt) override
|
||||
{
|
||||
switch (rt)
|
||||
@ -983,18 +1012,20 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void eraseObject(thread_db* tdbb) override
|
||||
bool erase(thread_db* tdbb)
|
||||
{
|
||||
HazardPtr<ListEntry<Versioned>> l(list);
|
||||
fb_assert(l);
|
||||
if (!l)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (!storeObject(tdbb, nullptr, CacheFlag::ERASED))
|
||||
if (!storeObject(tdbb, nullptr, CacheFlag::ERASED | CacheFlag::NOCOMMIT))
|
||||
{
|
||||
Versioned* oldObj = getObject(tdbb, 0);
|
||||
busyError(tdbb, this->getId(), this->c_name(), V::objectFamily(this));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Checking it does not protect from something to be added in this element at next cycle!!!
|
||||
@ -1033,6 +1064,7 @@ private:
|
||||
private:
|
||||
atomics::atomic<ListEntry<Versioned>*> list;
|
||||
atomics::atomic<TraNumber> resetAt;
|
||||
AtomicElementPointer* ptrToClean;
|
||||
};
|
||||
|
||||
|
||||
@ -1045,7 +1077,7 @@ public:
|
||||
|
||||
typedef typename StoredElement::Versioned Versioned;
|
||||
typedef typename StoredElement::Permanent Permanent;
|
||||
typedef atomics::atomic<StoredElement*> SubArrayData;
|
||||
typedef typename StoredElement::AtomicElementPointer SubArrayData;
|
||||
typedef atomics::atomic<SubArrayData*> ArrayData;
|
||||
typedef SharedReadVector<ArrayData, 4> Storage;
|
||||
|
||||
@ -1149,6 +1181,19 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
bool erase(thread_db* tdbb, MetaId id)
|
||||
{
|
||||
auto ptr = getDataPointer(id);
|
||||
if (ptr)
|
||||
{
|
||||
StoredElement* data = ptr->load(atomics::memory_order_acquire);
|
||||
if (data)
|
||||
return data->erase(tdbb);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Versioned* makeObject(thread_db* tdbb, MetaId id, ObjectBase::Flag fl)
|
||||
{
|
||||
if (id >= getCount())
|
||||
@ -1165,7 +1210,8 @@ public:
|
||||
if (ptr->compare_exchange_strong(data, newData,
|
||||
atomics::memory_order_release, atomics::memory_order_acquire))
|
||||
{
|
||||
data = newData;;
|
||||
newData->setCleanup(ptr);
|
||||
data = newData;
|
||||
}
|
||||
else
|
||||
StoredElement::cleanup(tdbb, newData);
|
||||
@ -1227,7 +1273,7 @@ public:
|
||||
continue;
|
||||
|
||||
StoredElement::cleanup(tdbb, elem);
|
||||
end->store(nullptr, atomics::memory_order_relaxed);
|
||||
fb_assert(!end->load(atomics::memory_order_relaxed));
|
||||
}
|
||||
|
||||
delete[] sub; // no need using retire() here in CacheVector's cleanup
|
||||
|
@ -489,7 +489,8 @@ Request* Statement::getRequest(thread_db* tdbb, const Requests::ReadAccessor& g,
|
||||
// Create the request.
|
||||
AutoMemoryPool reqPool(MemoryPool::createPool(ALLOC_ARGS1 pool));
|
||||
#ifdef DEBUG_LOST_POOLS
|
||||
fprintf(stderr, "%p %s\n", reqPool->mp(), sqlText ? sqlText->c_str() : "<nullptr>");
|
||||
fprintf(stderr, "%p %s %s\n", reqPool->mp(), sqlText ? sqlText->c_str() : "<nullptr>",
|
||||
procedure ? procedure->c_name() : "<not_prc>");
|
||||
#endif
|
||||
auto request = FB_NEW_POOL(*reqPool) Request(reqPool, dbb, this);
|
||||
loadResources(tdbb, request);
|
||||
|
@ -898,11 +898,12 @@ namespace
|
||||
{
|
||||
case 0:
|
||||
routine = lookupById(tdbb, work->dfw_id, CacheFlag::NOSCAN);
|
||||
if (!routine)
|
||||
return false;
|
||||
|
||||
if (routine->existenceLock)
|
||||
LCK_release(tdbb, routine->existenceLock);
|
||||
if (routine)
|
||||
{
|
||||
if (routine->existenceLock)
|
||||
LCK_release(tdbb, routine->existenceLock);
|
||||
routine->rollback(tdbb);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -941,8 +942,20 @@ namespace
|
||||
//if (routine->existenceLock)
|
||||
// routine->existenceLock->releaseLock(tdbb, ExistenceLock::ReleaseMethod::DropObject);
|
||||
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 5:
|
||||
case 6:
|
||||
return true;
|
||||
|
||||
case 7:
|
||||
routine = lookupById(tdbb, work->dfw_id, CacheFlag::RET_ERASED | CacheFlag::NOSCAN);
|
||||
fb_assert(routine);
|
||||
if (routine)
|
||||
routine->commit(tdbb);
|
||||
|
||||
return false;
|
||||
} // switch
|
||||
|
||||
return false;
|
||||
|
@ -5317,30 +5317,45 @@ Cached::CharSet* MetadataCache::getCharSet(thread_db* tdbb, CSetId id, ObjectBas
|
||||
|
||||
namespace {
|
||||
template <typename C>
|
||||
void changeVers(thread_db* tdbb, bool loadOld, CacheVector<C>& vector, MetaId id)
|
||||
void changeVers(thread_db* tdbb, MetadataCache::Changer cmd, CacheVector<C>& vector, MetaId id)
|
||||
{
|
||||
auto* ver = loadOld ? vector.getObject(tdbb, id, CacheFlag::AUTOCREATE) :
|
||||
vector.makeObject(tdbb, id, CacheFlag::NOCOMMIT);
|
||||
fb_assert(ver);
|
||||
bool processedChange = false;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case MetadataCache::Changer::CMD_OLD:
|
||||
processedChange = vector.getObject(tdbb, id, CacheFlag::AUTOCREATE);
|
||||
break;
|
||||
|
||||
case MetadataCache::Changer::CMD_NEW:
|
||||
processedChange = vector.makeObject(tdbb, id, CacheFlag::NOCOMMIT);
|
||||
break;
|
||||
|
||||
case MetadataCache::Changer::CMD_ERASE:
|
||||
processedChange = vector.erase(tdbb, id);
|
||||
break;
|
||||
}
|
||||
|
||||
fb_assert(processedChange);
|
||||
}
|
||||
}
|
||||
|
||||
void MetadataCache::changeVersion(thread_db* tdbb, bool loadOld, ObjectType objType, MetaId id)
|
||||
void MetadataCache::changeVersion(thread_db* tdbb, Changer cmd, ObjectType objType, MetaId id)
|
||||
{
|
||||
auto* mdc = tdbb->getDatabase()->dbb_mdc;
|
||||
switch(objType)
|
||||
{
|
||||
case obj_procedure:
|
||||
changeVers(tdbb, loadOld, mdc->mdc_procedures, id);
|
||||
changeVers(tdbb, cmd, mdc->mdc_procedures, id);
|
||||
break;
|
||||
|
||||
case obj_charset:
|
||||
changeVers(tdbb, loadOld, mdc->mdc_charsets, id);
|
||||
changeVers(tdbb, cmd, mdc->mdc_charsets, id);
|
||||
break;
|
||||
|
||||
/*
|
||||
case :
|
||||
changeVers(tdbb, loadOld, mdc->, id);
|
||||
changeVers(tdbb, cmd, mdc->, id);
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
@ -5353,3 +5368,56 @@ int jrd_prc::objectType()
|
||||
{
|
||||
return obj_trigger;
|
||||
}
|
||||
|
||||
MetadataCache::CleanupQueue::CleanupQueue(MemoryPool& p)
|
||||
: cq_data(p)
|
||||
{ }
|
||||
|
||||
void MetadataCache::CleanupQueue::enqueue(TraNumber traNum, ElementBase* toClean)
|
||||
{
|
||||
MutexLockGuard g(cq_mutex, FB_FUNCTION);
|
||||
|
||||
if (cq_data.getCount() == 0)
|
||||
{
|
||||
cq_traNum = traNum;
|
||||
fb_assert(cq_pos == 0);
|
||||
}
|
||||
cq_data.push(Stored(traNum, toClean));
|
||||
}
|
||||
|
||||
void MetadataCache::CleanupQueue::dequeue(thread_db* tdbb, TraNumber oldest)
|
||||
{
|
||||
MutexEnsureUnlock g(cq_mutex, FB_FUNCTION);
|
||||
|
||||
if (!g.tryEnter())
|
||||
return;
|
||||
|
||||
while (cq_pos < cq_data.getCount() && oldest > cq_data[cq_pos].t)
|
||||
{
|
||||
cq_data[cq_pos++].c->cleanup(tdbb);
|
||||
}
|
||||
|
||||
if (cq_data.getCount() <= cq_pos)
|
||||
{
|
||||
fb_assert(cq_data.getCount() == cq_pos);
|
||||
|
||||
cq_data.clear();
|
||||
cq_pos = 0;
|
||||
cq_traNum = MAX_TRA_NUMBER;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cq_pos > cq_data.getCount() / 2)
|
||||
{
|
||||
cq_data.removeCount(0, cq_pos);
|
||||
cq_pos = 0;
|
||||
}
|
||||
cq_traNum = cq_data[cq_pos].t;
|
||||
}
|
||||
}
|
||||
|
||||
void MetadataCache::objectCleanup(TraNumber traNum, ElementBase* toClean)
|
||||
{
|
||||
mdc_cleanup_queue.enqueue(traNum, toClean);
|
||||
}
|
||||
|
||||
|
@ -233,23 +233,20 @@ public:
|
||||
mdc_functions(getPool()),
|
||||
mdc_charsets(getPool()),
|
||||
mdc_ddl_triggers(nullptr),
|
||||
mdc_version(0)
|
||||
mdc_version(0),
|
||||
mdc_cleanup_queue(pool)
|
||||
{
|
||||
memset(mdc_triggers, 0, sizeof(mdc_triggers));
|
||||
}
|
||||
|
||||
~MetadataCache();
|
||||
|
||||
/*
|
||||
// Objects are placed to this list after DROP OBJECT
|
||||
// and wait for current OAT >= NEXT when DDL committed
|
||||
atomics::atomic<Cache List<ElementBase>*> dropList;
|
||||
// Objects are placed here after DROP OBJECT and wait for current OAT >= NEXT when DDL committed
|
||||
void objectCleanup(TraNumber traNum, ElementBase* toClean);
|
||||
void checkCleanup(thread_db* tdbb, TraNumber oldest)
|
||||
{
|
||||
public:
|
||||
void drop(
|
||||
}; ?????????????????????
|
||||
*/
|
||||
|
||||
mdc_cleanup_queue.check(tdbb, oldest);
|
||||
}
|
||||
|
||||
void releaseRelations(thread_db* tdbb);
|
||||
void releaseLocks(thread_db* tdbb);
|
||||
@ -364,16 +361,23 @@ public:
|
||||
|
||||
static void oldVersion(thread_db* tdbb, ObjectType objType, MetaId id)
|
||||
{
|
||||
changeVersion(tdbb, true, objType, id);
|
||||
changeVersion(tdbb, Changer::CMD_OLD, objType, id);
|
||||
}
|
||||
|
||||
static void newVersion(thread_db* tdbb, ObjectType objType, MetaId id)
|
||||
{
|
||||
changeVersion(tdbb, false, objType, id);
|
||||
changeVersion(tdbb, Changer::CMD_NEW, objType, id);
|
||||
}
|
||||
|
||||
static void erase(thread_db* tdbb, ObjectType objType, MetaId id)
|
||||
{
|
||||
changeVersion(tdbb, Changer::CMD_ERASE, objType, id);
|
||||
}
|
||||
|
||||
enum class Changer {CMD_OLD, CMD_NEW, CMD_ERASE};
|
||||
|
||||
private:
|
||||
static void changeVersion(thread_db* tdbb, bool loadOld, ObjectType objType, MetaId id);
|
||||
static void changeVersion(thread_db* tdbb, Changer cmd, ObjectType objType, MetaId id);
|
||||
|
||||
class GeneratorFinder
|
||||
{
|
||||
@ -433,6 +437,46 @@ private:
|
||||
Firebird::Mutex m_tx;
|
||||
};
|
||||
|
||||
class CleanupQueue
|
||||
{
|
||||
public:
|
||||
CleanupQueue(MemoryPool& p);
|
||||
|
||||
void enqueue(TraNumber traNum, ElementBase* toClean);
|
||||
|
||||
void check(thread_db* tdbb, TraNumber oldest)
|
||||
{
|
||||
// We check transaction number w/o lock - that's OK here cause even in
|
||||
// hardly imaginable case when correctly alligned memory read is not de-facto atomic
|
||||
// the worst result we get is skipped check (will be corrected by next transaction)
|
||||
// or taken extra lock for precise check. Not tragical.
|
||||
|
||||
if (oldest > cq_traNum)
|
||||
dequeue(tdbb, oldest);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Stored
|
||||
{
|
||||
TraNumber t;
|
||||
ElementBase* c;
|
||||
|
||||
Stored(TraNumber traNum, ElementBase* toClean)
|
||||
: t(traNum), c(toClean)
|
||||
{ }
|
||||
|
||||
Stored() // let HalfStatic work
|
||||
{ }
|
||||
};
|
||||
|
||||
Firebird::Mutex cq_mutex;
|
||||
Firebird::HalfStaticArray<Stored, 32> cq_data;
|
||||
TraNumber cq_traNum = MAX_TRA_NUMBER;
|
||||
FB_SIZE_T cq_pos = 0;
|
||||
|
||||
void dequeue(thread_db* tdbb, TraNumber oldest);
|
||||
};
|
||||
|
||||
GeneratorFinder mdc_generators;
|
||||
CacheVector<Cached::Relation> mdc_relations;
|
||||
CacheVector<Cached::Procedure> mdc_procedures;
|
||||
@ -442,6 +486,7 @@ private:
|
||||
TriggersSet mdc_ddl_triggers;
|
||||
|
||||
std::atomic<MdcVersion> mdc_version; // Current version of metadata cache (should have 2 nums???????????????)
|
||||
CleanupQueue mdc_cleanup_queue;
|
||||
};
|
||||
|
||||
} // namespace Jrd
|
||||
|
@ -3649,6 +3649,10 @@ static void transaction_start(thread_db* tdbb, jrd_tra* trans)
|
||||
dbb->dbb_tip_cache->updateOldestTransaction(tdbb,
|
||||
dbb->dbb_oldest_transaction, dbb->dbb_oldest_snapshot);
|
||||
|
||||
// Plumb remove really old objects from metadata cache
|
||||
|
||||
dbb->dbb_mdc->checkCleanup(tdbb, oldest);
|
||||
|
||||
// If the transaction block is getting out of hand, force a sweep
|
||||
|
||||
if (dbb->dbb_sweep_interval &&
|
||||
|
Loading…
Reference in New Issue
Block a user