Skip to content

Commit aecbeaa

Browse files
authored
[Store] Serialize/Deserialize Offset Allocator (#760)
Also add generic interfaces to serialize/deserialize other objects.
1 parent b63322c commit aecbeaa

File tree

6 files changed

+1196
-94
lines changed

6 files changed

+1196
-94
lines changed

mooncake-store/include/offset_allocator/offset_allocator.hpp

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <memory>
66
#include <optional>
7+
#include <glog/logging.h>
78

89
#include "mutex.h"
910

@@ -94,6 +95,8 @@ class OffsetAllocationHandle {
9495
// The real base and requested size of the allocated memory.
9596
uint64_t real_base;
9697
uint64_t requested_size;
98+
99+
friend class OffsetAllocatorTest; // for unit tests
97100
};
98101

99102
struct OffsetAllocatorMetrics {
@@ -155,6 +158,13 @@ class OffsetAllocator : public std::enable_shared_from_this<OffsetAllocator> {
155158
[[nodiscard]]
156159
OffsetAllocatorMetrics get_metrics() const;
157160

161+
// Serialize the allocator with serializer.
162+
template <typename T>
163+
void serialize_to(T& serializer) const;
164+
165+
template <typename T>
166+
static std::shared_ptr<OffsetAllocator> deserialize_from(T& serializer);
167+
158168
private:
159169
friend class OffsetAllocationHandle;
160170

@@ -166,11 +176,11 @@ class OffsetAllocator : public std::enable_shared_from_this<OffsetAllocator> {
166176
OffsetAllocatorMetrics get_metrics_internal() const;
167177

168178
std::unique_ptr<__Allocator> m_allocator GUARDED_BY(m_mutex);
169-
const uint64_t m_base;
179+
uint64_t m_base;
170180
// The real offset and size of the allocated memory need to be multiplied by
171181
// m_multiplier
172-
const uint64_t m_multiplier_bits;
173-
const uint64_t m_capacity;
182+
uint64_t m_multiplier_bits;
183+
uint64_t m_capacity;
174184
mutable Mutex m_mutex;
175185

176186
// Lightweight metrics maintained during allocation/deallocation
@@ -179,12 +189,20 @@ class OffsetAllocator : public std::enable_shared_from_this<OffsetAllocator> {
179189

180190
// Private constructor - use create() factory method instead
181191
OffsetAllocator(uint64_t base, size_t size, uint32 init_capacity,
182-
uint32 max_capacity);
192+
uint32 max_capacity);
193+
194+
// Private constructor - initialize from serialized data
195+
template <typename T>
196+
OffsetAllocator(T& serializer);
197+
198+
friend class OffsetAllocatorTest; // for unit tests
183199
};
184200

185201
class __Allocator {
186202
public:
187203
__Allocator(uint32 size, uint32 init_capacity, uint32 max_capacity);
204+
template <typename T>
205+
__Allocator(T& serializer) noexcept(false);
188206
__Allocator(__Allocator&& other);
189207
~__Allocator();
190208
void reset();
@@ -196,6 +214,10 @@ class __Allocator {
196214
OffsetAllocStorageReport storageReport() const;
197215
OffsetAllocStorageReportFull storageReportFull() const;
198216

217+
// Serialize the allocator with serializer.
218+
template <typename T>
219+
void serialize_to(T& serializer) const;
220+
199221
private:
200222
uint32 insertNodeIntoBin(uint32 size, uint32 dataOffset);
201223
void removeNodeFromBin(uint32 nodeIndex);
@@ -224,6 +246,103 @@ class __Allocator {
224246
Node* m_nodes;
225247
NodeIndex* m_freeNodes;
226248
uint32 m_freeOffset;
249+
250+
friend class OffsetAllocatorTest; // for unit tests
227251
};
228252

253+
// Template method implementations
254+
template <typename T>
255+
void OffsetAllocator::serialize_to(T& serializer) const {
256+
MutexLocker guard(&m_mutex);
257+
258+
if (!m_allocator) {
259+
serializer.set_error("Allocator is not initialized");
260+
return;
261+
}
262+
263+
// Basic member variables
264+
serializer.write(&m_base, sizeof(m_base));
265+
serializer.write(&m_multiplier_bits, sizeof(m_multiplier_bits));
266+
serializer.write(&m_capacity, sizeof(m_capacity));
267+
serializer.write(&m_allocated_size, sizeof(m_allocated_size));
268+
serializer.write(&m_allocated_num, sizeof(m_allocated_num));
269+
// Serialize the allocator
270+
m_allocator->serialize_to(serializer);
271+
}
272+
273+
template <typename T>
274+
std::shared_ptr<OffsetAllocator> OffsetAllocator::deserialize_from(
275+
T& serializer) {
276+
return std::shared_ptr<OffsetAllocator>(new OffsetAllocator(serializer));
277+
}
278+
279+
template <typename T>
280+
OffsetAllocator::OffsetAllocator(T& serializer) {
281+
// serializer.read() will throw an exception if the buffer is corrupted.
282+
try {
283+
serializer.read(&m_base, sizeof(m_base));
284+
serializer.read(&m_multiplier_bits, sizeof(m_multiplier_bits));
285+
serializer.read(&m_capacity, sizeof(m_capacity));
286+
serializer.read(&m_allocated_size, sizeof(m_allocated_size));
287+
serializer.read(&m_allocated_num, sizeof(m_allocated_num));
288+
m_allocator = std::make_unique<__Allocator>(serializer);
289+
} catch (const std::exception& e) {
290+
LOG(ERROR) << "Deserializing OffsetAllocator failed, error="
291+
<< e.what();
292+
throw std::runtime_error("Deserializing OffsetAllocator failed");
293+
}
294+
}
295+
296+
template <typename T>
297+
void __Allocator::serialize_to(T& serializer) const {
298+
if (!m_nodes || !m_freeNodes) {
299+
serializer.set_error("Allocator is not initialized");
300+
return;
301+
}
302+
303+
serializer.write(&m_size, sizeof(m_size));
304+
serializer.write(&m_current_capacity, sizeof(m_current_capacity));
305+
serializer.write(&m_max_capacity, sizeof(m_max_capacity));
306+
serializer.write(&m_freeStorage, sizeof(m_freeStorage));
307+
serializer.write(&m_usedBinsTop, sizeof(m_usedBinsTop));
308+
serializer.write(&m_usedBins, sizeof(m_usedBins));
309+
serializer.write(&m_binIndices, sizeof(m_binIndices));
310+
serializer.write(&m_freeOffset, sizeof(m_freeOffset));
311+
serializer.write(m_nodes, m_current_capacity * sizeof(Node));
312+
serializer.write(m_freeNodes, m_current_capacity * sizeof(NodeIndex));
313+
}
314+
315+
template <typename T>
316+
__Allocator::__Allocator(T& serializer) {
317+
m_nodes = nullptr;
318+
m_freeNodes = nullptr;
319+
320+
// serializer.read() will throw an exception if the buffer is corrupted.
321+
try {
322+
// Deserialize basic member variables
323+
serializer.read(&m_size, sizeof(m_size));
324+
serializer.read(&m_current_capacity, sizeof(m_current_capacity));
325+
serializer.read(&m_max_capacity, sizeof(m_max_capacity));
326+
serializer.read(&m_freeStorage, sizeof(m_freeStorage));
327+
serializer.read(&m_usedBinsTop, sizeof(m_usedBinsTop));
328+
serializer.read(&m_usedBins, sizeof(m_usedBins));
329+
serializer.read(&m_binIndices, sizeof(m_binIndices));
330+
serializer.read(&m_freeOffset, sizeof(m_freeOffset));
331+
332+
// Allocate memory for nodes and freeNodes
333+
m_nodes = new Node[m_max_capacity];
334+
m_freeNodes = new NodeIndex[m_max_capacity];
335+
336+
// Deserialize the arrays
337+
serializer.read(m_nodes, m_current_capacity * sizeof(Node));
338+
serializer.read(m_freeNodes, m_current_capacity * sizeof(NodeIndex));
339+
} catch (const std::exception& e) {
340+
// Free memory if deserialization fails
341+
LOG(ERROR) << "Deserializing __Allocator failed, error=" << e.what();
342+
if (m_nodes) delete[] m_nodes;
343+
if (m_freeNodes) delete[] m_freeNodes;
344+
throw std::runtime_error("Deserializing __Allocator failed");
345+
}
346+
}
347+
229348
} // namespace mooncake::offset_allocator

0 commit comments

Comments
 (0)