8
0
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:
Vlad Khorsun 2023-09-05 00:48:58 +03:00 committed by GitHub
commit 11ffa71b4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 267 additions and 1 deletions

View File

@ -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" />

View File

@ -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>

View File

@ -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
View 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

View File

@ -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;