1 | #include "duckdb/storage/buffer/block_handle.hpp" |
2 | #include "duckdb/storage/block.hpp" |
3 | #include "duckdb/storage/block_manager.hpp" |
4 | #include "duckdb/storage/buffer/buffer_handle.hpp" |
5 | #include "duckdb/storage/buffer_manager.hpp" |
6 | #include "duckdb/storage/buffer/buffer_pool.hpp" |
7 | #include "duckdb/common/file_buffer.hpp" |
8 | |
9 | namespace duckdb { |
10 | |
11 | BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p) |
12 | : block_manager(block_manager), readers(0), block_id(block_id_p), buffer(nullptr), eviction_timestamp(0), |
13 | can_destroy(false), memory_charge(block_manager.buffer_manager.GetBufferPool()), unswizzled(nullptr) { |
14 | eviction_timestamp = 0; |
15 | state = BlockState::BLOCK_UNLOADED; |
16 | memory_usage = Storage::BLOCK_ALLOC_SIZE; |
17 | } |
18 | |
19 | BlockHandle::BlockHandle(BlockManager &block_manager, block_id_t block_id_p, unique_ptr<FileBuffer> buffer_p, |
20 | bool can_destroy_p, idx_t block_size, BufferPoolReservation &&reservation) |
21 | : block_manager(block_manager), readers(0), block_id(block_id_p), eviction_timestamp(0), can_destroy(can_destroy_p), |
22 | memory_charge(block_manager.buffer_manager.GetBufferPool()), unswizzled(nullptr) { |
23 | buffer = std::move(buffer_p); |
24 | state = BlockState::BLOCK_LOADED; |
25 | memory_usage = block_size; |
26 | memory_charge = std::move(reservation); |
27 | } |
28 | |
29 | BlockHandle::~BlockHandle() { // NOLINT: allow internal exceptions |
30 | // being destroyed, so any unswizzled pointers are just binary junk now. |
31 | unswizzled = nullptr; |
32 | auto &buffer_manager = block_manager.buffer_manager; |
33 | // no references remain to this block: erase |
34 | if (buffer && state == BlockState::BLOCK_LOADED) { |
35 | D_ASSERT(memory_charge.size > 0); |
36 | // the block is still loaded in memory: erase it |
37 | buffer.reset(); |
38 | memory_charge.Resize(new_size: 0); |
39 | } else { |
40 | D_ASSERT(memory_charge.size == 0); |
41 | } |
42 | buffer_manager.GetBufferPool().PurgeQueue(); |
43 | block_manager.UnregisterBlock(block_id, can_destroy); |
44 | } |
45 | |
46 | unique_ptr<Block> AllocateBlock(BlockManager &block_manager, unique_ptr<FileBuffer> reusable_buffer, |
47 | block_id_t block_id) { |
48 | if (reusable_buffer) { |
49 | // re-usable buffer: re-use it |
50 | if (reusable_buffer->type == FileBufferType::BLOCK) { |
51 | // we can reuse the buffer entirely |
52 | auto &block = reinterpret_cast<Block &>(*reusable_buffer); |
53 | block.id = block_id; |
54 | return unique_ptr_cast<FileBuffer, Block>(src: std::move(reusable_buffer)); |
55 | } |
56 | auto block = block_manager.CreateBlock(block_id, source_buffer: reusable_buffer.get()); |
57 | reusable_buffer.reset(); |
58 | return block; |
59 | } else { |
60 | // no re-usable buffer: allocate a new block |
61 | return block_manager.CreateBlock(block_id, source_buffer: nullptr); |
62 | } |
63 | } |
64 | |
65 | BufferHandle BlockHandle::Load(shared_ptr<BlockHandle> &handle, unique_ptr<FileBuffer> reusable_buffer) { |
66 | if (handle->state == BlockState::BLOCK_LOADED) { |
67 | // already loaded |
68 | D_ASSERT(handle->buffer); |
69 | return BufferHandle(handle, handle->buffer.get()); |
70 | } |
71 | |
72 | auto &block_manager = handle->block_manager; |
73 | if (handle->block_id < MAXIMUM_BLOCK) { |
74 | auto block = AllocateBlock(block_manager, reusable_buffer: std::move(reusable_buffer), block_id: handle->block_id); |
75 | block_manager.Read(block&: *block); |
76 | handle->buffer = std::move(block); |
77 | } else { |
78 | if (handle->can_destroy) { |
79 | return BufferHandle(); |
80 | } else { |
81 | handle->buffer = |
82 | block_manager.buffer_manager.ReadTemporaryBuffer(id: handle->block_id, buffer: std::move(reusable_buffer)); |
83 | } |
84 | } |
85 | handle->state = BlockState::BLOCK_LOADED; |
86 | return BufferHandle(handle, handle->buffer.get()); |
87 | } |
88 | |
89 | unique_ptr<FileBuffer> BlockHandle::UnloadAndTakeBlock() { |
90 | if (state == BlockState::BLOCK_UNLOADED) { |
91 | // already unloaded: nothing to do |
92 | return nullptr; |
93 | } |
94 | D_ASSERT(!unswizzled); |
95 | D_ASSERT(CanUnload()); |
96 | |
97 | if (block_id >= MAXIMUM_BLOCK && !can_destroy) { |
98 | // temporary block that cannot be destroyed: write to temporary file |
99 | block_manager.buffer_manager.WriteTemporaryBuffer(block_id, buffer&: *buffer); |
100 | } |
101 | memory_charge.Resize(new_size: 0); |
102 | state = BlockState::BLOCK_UNLOADED; |
103 | return std::move(buffer); |
104 | } |
105 | |
106 | void BlockHandle::Unload() { |
107 | auto block = UnloadAndTakeBlock(); |
108 | block.reset(); |
109 | } |
110 | |
111 | bool BlockHandle::CanUnload() { |
112 | if (state == BlockState::BLOCK_UNLOADED) { |
113 | // already unloaded |
114 | return false; |
115 | } |
116 | if (readers > 0) { |
117 | // there are active readers |
118 | return false; |
119 | } |
120 | if (block_id >= MAXIMUM_BLOCK && !can_destroy && !block_manager.buffer_manager.HasTemporaryDirectory()) { |
121 | // in order to unload this block we need to write it to a temporary buffer |
122 | // however, no temporary directory is specified! |
123 | // hence we cannot unload the block |
124 | return false; |
125 | } |
126 | return true; |
127 | } |
128 | |
129 | } // namespace duckdb |
130 | |