1#include "duckdb/storage/block_manager.hpp"
2#include "duckdb/storage/buffer_manager.hpp"
3#include "duckdb/storage/buffer/block_handle.hpp"
4#include "duckdb/storage/buffer/buffer_pool.hpp"
5
6namespace duckdb {
7
8shared_ptr<BlockHandle> BlockManager::RegisterBlock(block_id_t block_id, bool is_meta_block) {
9 lock_guard<mutex> lock(blocks_lock);
10 // check if the block already exists
11 auto entry = blocks.find(x: block_id);
12 if (entry != blocks.end()) {
13 // already exists: check if it hasn't expired yet
14 auto existing_ptr = entry->second.lock();
15 if (existing_ptr) {
16 //! it hasn't! return it
17 return existing_ptr;
18 }
19 }
20 // create a new block pointer for this block
21 auto result = make_shared<BlockHandle>(args&: *this, args&: block_id);
22 // for meta block, cache the handle in meta_blocks
23 if (is_meta_block) {
24 meta_blocks[block_id] = result;
25 }
26 // register the block pointer in the set of blocks as a weak pointer
27 blocks[block_id] = weak_ptr<BlockHandle>(result);
28 return result;
29}
30
31void BlockManager::ClearMetaBlockHandles() {
32 meta_blocks.clear();
33}
34
35shared_ptr<BlockHandle> BlockManager::ConvertToPersistent(block_id_t block_id, shared_ptr<BlockHandle> old_block) {
36 // pin the old block to ensure we have it loaded in memory
37 auto old_handle = buffer_manager.Pin(handle&: old_block);
38 D_ASSERT(old_block->state == BlockState::BLOCK_LOADED);
39 D_ASSERT(old_block->buffer);
40
41 // Temp buffers can be larger than the storage block size. But persistent buffers
42 // cannot.
43 D_ASSERT(old_block->buffer->AllocSize() <= Storage::BLOCK_ALLOC_SIZE);
44
45 // register a block with the new block id
46 auto new_block = RegisterBlock(block_id);
47 D_ASSERT(new_block->state == BlockState::BLOCK_UNLOADED);
48 D_ASSERT(new_block->readers == 0);
49
50 // move the data from the old block into data for the new block
51 new_block->state = BlockState::BLOCK_LOADED;
52 new_block->buffer = ConvertBlock(block_id, source_buffer&: *old_block->buffer);
53 new_block->memory_usage = old_block->memory_usage;
54 new_block->memory_charge = std::move(old_block->memory_charge);
55
56 // clear the old buffer and unload it
57 old_block->buffer.reset();
58 old_block->state = BlockState::BLOCK_UNLOADED;
59 old_block->memory_usage = 0;
60 old_handle.Destroy();
61 old_block.reset();
62
63 // persist the new block to disk
64 Write(block&: *new_block->buffer, block_id);
65
66 buffer_manager.GetBufferPool().AddToEvictionQueue(handle&: new_block);
67
68 return new_block;
69}
70
71void BlockManager::UnregisterBlock(block_id_t block_id, bool can_destroy) {
72 if (block_id >= MAXIMUM_BLOCK) {
73 // in-memory buffer: buffer could have been offloaded to disk: remove the file
74 buffer_manager.DeleteTemporaryFile(id: block_id);
75 } else {
76 lock_guard<mutex> lock(blocks_lock);
77 // on-disk block: erase from list of blocks in manager
78 blocks.erase(x: block_id);
79 }
80}
81
82} // namespace duckdb
83