1//===----------------------------------------------------------------------===//
2// DuckDB
3//
4// duckdb/storage/standard_buffer_manager.hpp
5//
6//
7//===----------------------------------------------------------------------===//
8
9#pragma once
10
11#include "duckdb/common/allocator.hpp"
12#include "duckdb/common/atomic.hpp"
13#include "duckdb/common/file_system.hpp"
14#include "duckdb/common/mutex.hpp"
15#include "duckdb/storage/block_manager.hpp"
16
17#include "duckdb/storage/buffer/block_handle.hpp"
18#include "duckdb/storage/buffer_manager.hpp"
19#include "duckdb/storage/buffer/buffer_pool.hpp"
20
21namespace duckdb {
22class BlockManager;
23class DatabaseInstance;
24class TemporaryDirectoryHandle;
25struct EvictionQueue;
26
27//! The BufferManager is in charge of handling memory management for a single database. It cooperatively shares a
28//! BufferPool with other BufferManagers, belonging to different databases. It hands out memory buffers that can
29//! be used by the database internally, and offers configuration options specific to a database, which need not be
30//! shared by the BufferPool, including whether to support swapping temp buffers to disk, and where to swap them to.
31class StandardBufferManager : public BufferManager {
32 friend class BufferHandle;
33 friend class BlockHandle;
34 friend class BlockManager;
35
36public:
37 StandardBufferManager(DatabaseInstance &db, string temp_directory);
38 virtual ~StandardBufferManager();
39
40public:
41 static unique_ptr<StandardBufferManager> CreateBufferManager(DatabaseInstance &db, string temp_directory);
42 //! Registers an in-memory buffer that cannot be unloaded until it is destroyed
43 //! This buffer can be small (smaller than BLOCK_SIZE)
44 //! Unpin and pin are nops on this block of memory
45 shared_ptr<BlockHandle> RegisterSmallMemory(idx_t block_size) final override;
46
47 idx_t GetUsedMemory() const final override;
48 idx_t GetMaxMemory() const final override;
49
50 //! Allocate an in-memory buffer with a single pin.
51 //! The allocated memory is released when the buffer handle is destroyed.
52 DUCKDB_API BufferHandle Allocate(idx_t block_size, bool can_destroy = true,
53 shared_ptr<BlockHandle> *block = nullptr) final override;
54
55 //! Reallocate an in-memory buffer that is pinned.
56 void ReAllocate(shared_ptr<BlockHandle> &handle, idx_t block_size) final override;
57
58 BufferHandle Pin(shared_ptr<BlockHandle> &handle) final override;
59 void Unpin(shared_ptr<BlockHandle> &handle) final override;
60
61 //! Set a new memory limit to the buffer manager, throws an exception if the new limit is too low and not enough
62 //! blocks can be evicted
63 void SetLimit(idx_t limit = (idx_t)-1) final override;
64
65 //! Returns a list of all temporary files
66 vector<TemporaryFileInformation> GetTemporaryFiles() final override;
67
68 const string &GetTemporaryDirectory() final override {
69 return temp_directory;
70 }
71
72 void SetTemporaryDirectory(const string &new_dir) final override;
73
74 DUCKDB_API Allocator &GetBufferAllocator() final override;
75
76 DatabaseInstance &GetDatabase() final override {
77 return db;
78 }
79
80 //! Construct a managed buffer.
81 unique_ptr<FileBuffer> ConstructManagedBuffer(idx_t size, unique_ptr<FileBuffer> &&source,
82 FileBufferType type = FileBufferType::MANAGED_BUFFER) override;
83
84 DUCKDB_API void ReserveMemory(idx_t size) final override;
85 DUCKDB_API void FreeReservedMemory(idx_t size) final override;
86 bool HasTemporaryDirectory() const final override;
87
88protected:
89 //! Helper
90 template <typename... ARGS>
91 TempBufferPoolReservation EvictBlocksOrThrow(idx_t memory_delta, unique_ptr<FileBuffer> *buffer, ARGS...);
92
93 //! Register an in-memory buffer of arbitrary size, as long as it is >= BLOCK_SIZE. can_destroy signifies whether or
94 //! not the buffer can be destroyed when unpinned, or whether or not it needs to be written to a temporary file so
95 //! it can be reloaded. The resulting buffer will already be allocated, but needs to be pinned in order to be used.
96 //! This needs to be private to prevent creating blocks without ever pinning them:
97 //! blocks that are never pinned are never added to the eviction queue
98 shared_ptr<BlockHandle> RegisterMemory(idx_t block_size, bool can_destroy);
99
100 //! Evict blocks until the currently used memory + extra_memory fit, returns false if this was not possible
101 //! (i.e. not enough blocks could be evicted)
102 //! If the "buffer" argument is specified AND the system can find a buffer to re-use for the given allocation size
103 //! "buffer" will be made to point to the re-usable memory. Note that this is not guaranteed.
104 //! Returns a pair. result.first indicates if eviction was successful. result.second contains the
105 //! reservation handle, which can be moved to the BlockHandle that will own the reservation.
106 BufferPool::EvictionResult EvictBlocks(idx_t extra_memory, idx_t memory_limit,
107 unique_ptr<FileBuffer> *buffer = nullptr);
108
109 //! Garbage collect eviction queue
110 void PurgeQueue() final override;
111
112 BufferPool &GetBufferPool() final override;
113
114 //! Write a temporary buffer to disk
115 void WriteTemporaryBuffer(block_id_t block_id, FileBuffer &buffer) final override;
116 //! Read a temporary buffer from disk
117 unique_ptr<FileBuffer> ReadTemporaryBuffer(block_id_t id, unique_ptr<FileBuffer> buffer = nullptr) final override;
118 //! Get the path of the temporary buffer
119 string GetTemporaryPath(block_id_t id);
120
121 void DeleteTemporaryFile(block_id_t id) final override;
122
123 void RequireTemporaryDirectory();
124
125 void AddToEvictionQueue(shared_ptr<BlockHandle> &handle) final override;
126
127 const char *InMemoryWarning();
128
129 static data_ptr_t BufferAllocatorAllocate(PrivateAllocatorData *private_data, idx_t size);
130 static void BufferAllocatorFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size);
131 static data_ptr_t BufferAllocatorRealloc(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size,
132 idx_t size);
133
134 //! When the BlockHandle reaches 0 readers, this creates a new FileBuffer for this BlockHandle and
135 //! overwrites the data within with garbage. Any readers that do not hold the pin will notice
136 void VerifyZeroReaders(shared_ptr<BlockHandle> &handle);
137
138protected:
139 //! The database instance
140 DatabaseInstance &db;
141 //! The buffer pool
142 BufferPool &buffer_pool;
143 //! The directory name where temporary files are stored
144 string temp_directory;
145 //! Lock for creating the temp handle
146 mutex temp_handle_lock;
147 //! Handle for the temporary directory
148 unique_ptr<TemporaryDirectoryHandle> temp_directory_handle;
149 //! The temporary id used for managed buffers
150 atomic<block_id_t> temporary_id;
151 //! Allocator associated with the buffer manager, that passes all allocations through this buffer manager
152 Allocator buffer_allocator;
153 //! Block manager for temp data
154 unique_ptr<BlockManager> temp_block_manager;
155};
156
157} // namespace duckdb
158