mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 18:43:02 +01:00
Merge pull request #7735 from FirebirdSQL/work/AttCacheBdb
Per-attachment cache of pointers to the often used page buffers.
This commit is contained in:
commit
11ffa71b4f
@ -310,6 +310,7 @@
|
|||||||
<ClInclude Include="..\..\..\src\jrd\ods_proto.h" />
|
<ClInclude Include="..\..\..\src\jrd\ods_proto.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\optimizer\Optimizer.h" />
|
<ClInclude Include="..\..\..\src\jrd\optimizer\Optimizer.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\pag.h" />
|
<ClInclude Include="..\..\..\src\jrd\pag.h" />
|
||||||
|
<ClInclude Include="..\..\..\src\jrd\PageToBufferMap.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\pag_proto.h" />
|
<ClInclude Include="..\..\..\src\jrd\pag_proto.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\par_proto.h" />
|
<ClInclude Include="..\..\..\src\jrd\par_proto.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\PreparedStatement.h" />
|
<ClInclude Include="..\..\..\src\jrd\PreparedStatement.h" />
|
||||||
|
@ -1085,6 +1085,7 @@
|
|||||||
<ClInclude Include="..\..\..\src\jrd\WorkerAttachment.h">
|
<ClInclude Include="..\..\..\src\jrd\WorkerAttachment.h">
|
||||||
<Filter>Header files</Filter>
|
<Filter>Header files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\src\jrd\PageToBufferMap.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\..\src\dsql\DdlNodes.epp">
|
<None Include="..\..\..\src\dsql\DdlNodes.epp">
|
||||||
@ -1130,4 +1131,4 @@
|
|||||||
<Filter>DSQL</Filter>
|
<Filter>DSQL</Filter>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -73,6 +73,7 @@ namespace Jrd
|
|||||||
class jrd_file;
|
class jrd_file;
|
||||||
class Format;
|
class Format;
|
||||||
class BufferControl;
|
class BufferControl;
|
||||||
|
class PageToBufferMap;
|
||||||
class SparseBitmap;
|
class SparseBitmap;
|
||||||
class jrd_rel;
|
class jrd_rel;
|
||||||
class ExternalFile;
|
class ExternalFile;
|
||||||
@ -639,6 +640,8 @@ public:
|
|||||||
USHORT att_current_timezone;
|
USHORT att_current_timezone;
|
||||||
int att_parallel_workers;
|
int att_parallel_workers;
|
||||||
|
|
||||||
|
PageToBufferMap* att_bdb_cache; // managed in CCH, created in att_pool, freed with it
|
||||||
|
|
||||||
Firebird::RefPtr<Firebird::IReplicatedSession> att_replicator;
|
Firebird::RefPtr<Firebird::IReplicatedSession> att_replicator;
|
||||||
Firebird::AutoPtr<Replication::TableMatcher> att_repl_matcher;
|
Firebird::AutoPtr<Replication::TableMatcher> att_repl_matcher;
|
||||||
Firebird::Array<Applier*> att_repl_appliers;
|
Firebird::Array<Applier*> att_repl_appliers;
|
||||||
|
217
src/jrd/PageToBufferMap.h
Normal file
217
src/jrd/PageToBufferMap.h
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* PROGRAM: JRD Access Method
|
||||||
|
* MODULE: PageToBufferMap.h
|
||||||
|
* DESCRIPTION: Support of disk cache manager
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Interbase Public
|
||||||
|
* License Version 1.0 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy
|
||||||
|
* of the License at http://www.Inprise.com/IPL.html
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an
|
||||||
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing
|
||||||
|
* rights and limitations under the License.
|
||||||
|
*
|
||||||
|
* The Original Code was created by Vladyslav Khorsun for the
|
||||||
|
* Firebird Open Source RDBMS project.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Vladyslav Khorsun <hvlad@users.sourceforge.net>
|
||||||
|
* and all contributors signed below.
|
||||||
|
*
|
||||||
|
* All Rights Reserved.
|
||||||
|
* Contributor(s): ______________________________________.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JRD_PAGE_TO_BUFFER_MAP
|
||||||
|
#define JRD_PAGE_TO_BUFFER_MAP
|
||||||
|
|
||||||
|
#include "../common/classes/Hash.h"
|
||||||
|
#include "../jrd/cch.h"
|
||||||
|
|
||||||
|
namespace Jrd {
|
||||||
|
|
||||||
|
// PageToBufferMap used to cache pointers to the often used page buffers.
|
||||||
|
// Its purpose is to avoid more costly usage of shared hash table.
|
||||||
|
|
||||||
|
class PageToBufferMap
|
||||||
|
{
|
||||||
|
static const size_t MAP_SIZE = 64;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageToBufferMap(MemoryPool& pool) :
|
||||||
|
m_map(pool)
|
||||||
|
{
|
||||||
|
Item* items = FB_NEW_POOL(pool) Item[MAP_SIZE];
|
||||||
|
for (FB_SIZE_T i = 1; i < MAP_SIZE; i++)
|
||||||
|
items[i - 1].m_next = &items[i];
|
||||||
|
|
||||||
|
m_free = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferDesc* get(PageNumber page)
|
||||||
|
{
|
||||||
|
Item* item = m_map.lookup(page);
|
||||||
|
|
||||||
|
if (!item)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (m_list != item)
|
||||||
|
{
|
||||||
|
listRemove(item);
|
||||||
|
listInsert(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item->m_bdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void put(BufferDesc* bdb)
|
||||||
|
{
|
||||||
|
Item* item = m_map.lookup(bdb->bdb_page);
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
if (m_list != item)
|
||||||
|
listRemove(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = getFreeItem();
|
||||||
|
item->m_page = bdb->bdb_page;
|
||||||
|
m_map.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
item->m_bdb = bdb;
|
||||||
|
if (m_list != item)
|
||||||
|
listInsert(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(PageNumber page)
|
||||||
|
{
|
||||||
|
Item* item = m_map.remove(page);
|
||||||
|
|
||||||
|
fb_assert(item);
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
listRemove(item);
|
||||||
|
|
||||||
|
item->m_bdb = nullptr;
|
||||||
|
item->m_next = m_free;
|
||||||
|
m_free = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Item;
|
||||||
|
using HashTableType = Firebird::HashTable<Item, MAP_SIZE, PageNumber, Item, Item>;
|
||||||
|
|
||||||
|
struct Item : public HashTableType::Entry
|
||||||
|
{
|
||||||
|
PageNumber m_page;
|
||||||
|
BufferDesc* m_bdb = nullptr;
|
||||||
|
Item* m_next = nullptr; // LRU list support
|
||||||
|
Item* m_prev = nullptr;
|
||||||
|
|
||||||
|
// KeyOfValue
|
||||||
|
static PageNumber generate(Item& item)
|
||||||
|
{
|
||||||
|
return item.m_page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash function
|
||||||
|
static FB_SIZE_T hash(const PageNumber& value, FB_SIZE_T hashSize)
|
||||||
|
{
|
||||||
|
return value.getPageNum() % hashSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashTable::Entry
|
||||||
|
bool isEqual(const PageNumber& page) const override
|
||||||
|
{
|
||||||
|
return this->m_page == page;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item* get() override
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Item* getFreeItem()
|
||||||
|
{
|
||||||
|
Item* item = m_free;
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
m_free = m_free->m_next;
|
||||||
|
item->m_next = nullptr;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get least recently used item from list
|
||||||
|
fb_assert(m_list != nullptr);
|
||||||
|
|
||||||
|
item = m_list->m_prev;
|
||||||
|
listRemove(item);
|
||||||
|
|
||||||
|
if (m_map.remove(item->m_page) != item)
|
||||||
|
fb_assert(false);
|
||||||
|
|
||||||
|
item->m_bdb = nullptr;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void listRemove(Item* item)
|
||||||
|
{
|
||||||
|
// remove item from list
|
||||||
|
fb_assert(m_list != nullptr);
|
||||||
|
fb_assert(item->m_next);
|
||||||
|
fb_assert(item->m_prev);
|
||||||
|
|
||||||
|
if (item->m_next == item)
|
||||||
|
{
|
||||||
|
fb_assert(item->m_prev == item);
|
||||||
|
fb_assert(m_list == item);
|
||||||
|
m_list = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_list == item)
|
||||||
|
m_list = item->m_next;
|
||||||
|
|
||||||
|
item->m_next->m_prev = item->m_prev;
|
||||||
|
item->m_prev->m_next = item->m_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
item->m_next = item->m_prev = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void listInsert(Item* item)
|
||||||
|
{
|
||||||
|
// put item at the list head
|
||||||
|
if (m_list != nullptr)
|
||||||
|
{
|
||||||
|
fb_assert(m_list->m_next);
|
||||||
|
fb_assert(m_list->m_prev);
|
||||||
|
|
||||||
|
item->m_next = m_list;
|
||||||
|
item->m_prev = m_list->m_prev;
|
||||||
|
|
||||||
|
item->m_next->m_prev = item;
|
||||||
|
item->m_prev->m_next = item;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item->m_next = item;
|
||||||
|
item->m_prev = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_list = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTableType m_map;
|
||||||
|
Item* m_list = nullptr; // head of LRU list
|
||||||
|
Item* m_free = nullptr; // unused items
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Jrd
|
||||||
|
|
||||||
|
#endif // JRD_PAGE_TO_BUFFER_MAP
|
@ -62,6 +62,7 @@
|
|||||||
#include "../common/classes/MsgPrint.h"
|
#include "../common/classes/MsgPrint.h"
|
||||||
#include "../jrd/CryptoManager.h"
|
#include "../jrd/CryptoManager.h"
|
||||||
#include "../common/utils_proto.h"
|
#include "../common/utils_proto.h"
|
||||||
|
#include "../jrd/PageToBufferMap.h"
|
||||||
|
|
||||||
// Use lock-free lists in hash table implementation
|
// Use lock-free lists in hash table implementation
|
||||||
#define HASH_USE_CDS_LIST
|
#define HASH_USE_CDS_LIST
|
||||||
@ -131,6 +132,7 @@ static void prefetch_init(Prefetch*, thread_db*);
|
|||||||
static void prefetch_io(Prefetch*, FbStatusVector *);
|
static void prefetch_io(Prefetch*, FbStatusVector *);
|
||||||
static void prefetch_prologue(Prefetch*, SLONG *);
|
static void prefetch_prologue(Prefetch*, SLONG *);
|
||||||
#endif
|
#endif
|
||||||
|
static void cacheBuffer(Attachment* att, BufferDesc* bdb);
|
||||||
static void check_precedence(thread_db*, WIN*, PageNumber);
|
static void check_precedence(thread_db*, WIN*, PageNumber);
|
||||||
static void clear_precedence(thread_db*, BufferDesc*);
|
static void clear_precedence(thread_db*, BufferDesc*);
|
||||||
static void down_grade(thread_db*, BufferDesc*, int high = 0);
|
static void down_grade(thread_db*, BufferDesc*, int high = 0);
|
||||||
@ -3152,6 +3154,18 @@ void BufferControl::exceptionHandler(const Firebird::Exception& ex, BcbThreadSyn
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cacheBuffer(Attachment* att, BufferDesc* bdb)
|
||||||
|
{
|
||||||
|
if (att)
|
||||||
|
{
|
||||||
|
if (!att->att_bdb_cache)
|
||||||
|
att->att_bdb_cache = FB_NEW_POOL(*att->att_pool) PageToBufferMap(*att->att_pool);
|
||||||
|
|
||||||
|
att->att_bdb_cache->put(bdb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void check_precedence(thread_db* tdbb, WIN* window, PageNumber page)
|
static void check_precedence(thread_db* tdbb, WIN* window, PageNumber page)
|
||||||
{
|
{
|
||||||
/**************************************
|
/**************************************
|
||||||
@ -3782,6 +3796,32 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s
|
|||||||
SET_TDBB(tdbb);
|
SET_TDBB(tdbb);
|
||||||
Database* dbb = tdbb->getDatabase();
|
Database* dbb = tdbb->getDatabase();
|
||||||
BufferControl* bcb = dbb->dbb_bcb;
|
BufferControl* bcb = dbb->dbb_bcb;
|
||||||
|
Attachment* att = tdbb->getAttachment();
|
||||||
|
|
||||||
|
if (att && att->att_bdb_cache)
|
||||||
|
{
|
||||||
|
if (BufferDesc* bdb = att->att_bdb_cache->get(page))
|
||||||
|
{
|
||||||
|
if (bdb->addRef(tdbb, syncType, wait))
|
||||||
|
{
|
||||||
|
if (bdb->bdb_page == page)
|
||||||
|
{
|
||||||
|
recentlyUsed(bdb);
|
||||||
|
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
||||||
|
return bdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdb->release(tdbb, true);
|
||||||
|
att->att_bdb_cache->remove(page);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fb_assert(wait <= 0);
|
||||||
|
if (bdb->bdb_page == page)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -3811,6 +3851,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s
|
|||||||
{
|
{
|
||||||
recentlyUsed(bdb);
|
recentlyUsed(bdb);
|
||||||
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
||||||
|
cacheBuffer(att, bdb);
|
||||||
return bdb;
|
return bdb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3850,6 +3891,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s
|
|||||||
bdb->downgrade(syncType);
|
bdb->downgrade(syncType);
|
||||||
recentlyUsed(bdb);
|
recentlyUsed(bdb);
|
||||||
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
||||||
|
cacheBuffer(att, bdb);
|
||||||
return bdb;
|
return bdb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3893,6 +3935,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s
|
|||||||
recentlyUsed(bdb);
|
recentlyUsed(bdb);
|
||||||
}
|
}
|
||||||
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
||||||
|
cacheBuffer(att, bdb);
|
||||||
return bdb;
|
return bdb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3910,6 +3953,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s
|
|||||||
}
|
}
|
||||||
recentlyUsed(bdb2);
|
recentlyUsed(bdb2);
|
||||||
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES);
|
||||||
|
cacheBuffer(att, bdb2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
bdb2 = nullptr;
|
bdb2 = nullptr;
|
||||||
|
Loading…
Reference in New Issue
Block a user