Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ set_property(SOURCE dfly_main.cc APPEND PROPERTY COMPILE_DEFINITIONS
SOURCE_PATH_FROM_BUILD_ENV=${CMAKE_SOURCE_DIR})

if (WITH_TIERING)
SET(TX_LINUX_SRCS tiering/disk_storage.cc tiering/op_manager.cc tiering/small_bins.cc
tiering/external_alloc.cc tiering/decoders.cc)
SET(TX_LINUX_SRCS tiering/disk_storage.cc tiering/op_manager.cc
tiering/small_bins.cc tiering/external_alloc.cc tiering/serialized_map.cc tiering/decoders.cc)

add_executable(dfly_bench dfly_bench.cc)
cxx_link(dfly_bench dfly_parser_lib fibers2 absl::random_random redis_lib)
cxx_test(tiering/disk_storage_test dfly_test_lib LABELS DFLY)
cxx_test(tiering/op_manager_test dfly_test_lib LABELS DFLY)
cxx_test(tiering/small_bins_test dfly_test_lib LABELS DFLY)
cxx_test(tiering/external_alloc_test dfly_test_lib LABELS DFLY)
cxx_test(tiering/serialized_map_test dfly_test_lib LABELS DFLY)
endif()

# Optionally include command families as needed
Expand Down
80 changes: 80 additions & 0 deletions src/server/tiering/serialized_map.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "server/tiering/serialized_map.h"

#include <absl/base/internal/endian.h>

#include "base/logging.h"

namespace dfly::tiering {

SerializedMap::Iterator& SerializedMap::Iterator::operator++() {
slice_.remove_prefix(8 + key_.size() + value_.size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add constexpr for 8

Read();
return *this;
}

SerializedMap::Iterator::Iterator(std::string_view buffer) : slice_{buffer} {
Read();
}

void SerializedMap::Iterator::Read() {
if (slice_.empty())
return;

uint32_t key_len = absl::little_endian::Load32(slice_.data());
uint32_t value_len = absl::little_endian::Load32(slice_.data() + 4);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please give name to 4

key_ = {slice_.data() + 8, key_len};
value_ = {slice_.data() + 8 + key_len, value_len};
}

SerializedMap::SerializedMap(std::string_view slice) {
size_ = absl::little_endian::Load32(slice.data());
DCHECK_GT(size_, 0u);
slice_ = slice;
}

SerializedMap::Iterator SerializedMap::Find(std::string_view key) const {
return std::find_if(begin(), end(), [key](auto p) { return p.first == key; });
}

SerializedMap::Iterator SerializedMap::begin() const {
return Iterator{slice_.substr(4)};
}

SerializedMap::Iterator SerializedMap::end() const {
return Iterator{slice_.substr(slice_.size(), 0)};
}

size_t SerializedMap::size() const {
return size_;
}

constexpr size_t kLenBytes = 4;

size_t SerializedMap::SerializeSize(Input input) {
size_t out = kLenBytes; // number of entries
Copy link
Contributor

@kostasrim kostasrim Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

four * two or even better four times two sounds like math poetry 🤣

for (const auto& [key, value] : input)
out += kLenBytes * 2 /* string lengts */ + key.size() + value.size();
return out;
}

size_t SerializedMap::Serialize(Input input, absl::Span<char> buffer) {
DCHECK_GE(buffer.size(), SerializeSize(input));
char* ptr = buffer.data();
absl::little_endian::Store32(ptr, input.size());
ptr += kLenBytes;

for (const auto& [key, value] : input) {
absl::little_endian::Store32(ptr, key.length());
ptr += kLenBytes;
absl::little_endian::Store32(ptr, value.length());
ptr += kLenBytes;
memcpy(ptr, key.data(), key.length());
ptr += key.length();
memcpy(ptr, value.data(), value.length());
ptr += value.length();
}

return ptr - buffer.data();
}

} // namespace dfly::tiering
64 changes: 64 additions & 0 deletions src/server/tiering/serialized_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <absl/types/span.h>

#include <string_view>

namespace dfly::tiering {

// Map built over single continuous byte slice to allow easy read operations.
struct SerializedMap {
struct Iterator {
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::pair<std::string_view, std::string_view>;
using reference = value_type;
using pointer = value_type*;

Iterator& operator++();

bool operator==(const Iterator& other) const {
return slice_.data() == other.slice_.data() && slice_.size() == other.slice_.size();
}

bool operator!=(const Iterator& other) const {
return !operator==(other);
}

std::pair<std::string_view, std::string_view> operator*() const {
return {key_, value_};
}

private:
friend struct SerializedMap;

explicit Iterator(std::string_view buffer);
void Read();

std::string_view slice_; // the part left
std::string_view key_, value_;
};

explicit SerializedMap(std::string_view slice);

Iterator Find(std::string_view key) const; // Linear search
Iterator begin() const;
Iterator end() const;
size_t size() const;

// Input for serialization
using Input = const absl::Span<const std::pair<std::string_view, std::string_view>>;

// Buffer size required for serialization
static size_t SerializeSize(Input);

// Write a slice that can be used to a SerializedMap on top of it.
// Returns number of bytes written
static size_t Serialize(Input, absl::Span<char> buffer);

private:
size_t size_;
std::string_view slice_;
};

} // namespace dfly::tiering
38 changes: 38 additions & 0 deletions src/server/tiering/serialized_map_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "server/tiering/serialized_map.h"

#include <map>

#include "base/logging.h"
#include "gmock/gmock.h"

namespace dfly::tiering {

using namespace std;

class SerializedMapTest : public ::testing::Test {};

TEST_F(SerializedMapTest, TestBasic) {
const vector<std::pair<string_view, string_view>> kBase = {{"first key", "first value"},
{"second key", "second value"},
{"third key", "third value"},
{"fourth key", "fourth value"},
{"fifth key", "fifth value"}};

// Serialize kBase to buffer
std::string buffer;
buffer.resize(SerializedMap::SerializeSize(kBase));
size_t written = SerializedMap::Serialize(kBase, absl::MakeSpan(buffer));
EXPECT_GT(written, 0u);

// Build map over buffer and check size
SerializedMap map{buffer};
EXPECT_EQ(map.size(), kBase.size());

// Check entries
size_t idx = 0;
for (auto it = map.begin(); it != map.end(); ++it, ++idx) {
EXPECT_EQ(*it, kBase[idx]);
}
}

} // namespace dfly::tiering
Loading