Skip to content

Commit cd1f9d3

Browse files
authored
chore: Introduce CoolQueue (#3365)
Also, add the according API to compact object. Now external objects can be in two states: Cool and Offloaded. Signed-off-by: Roman Gershman <[email protected]>
1 parent bcdfccc commit cd1f9d3

File tree

6 files changed

+199
-14
lines changed

6 files changed

+199
-14
lines changed

src/core/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ add_subdirectory(json)
33

44
set(SEARCH_LIB query_parser)
55

6-
add_library(dfly_core bloom.cc compact_object.cc dragonfly_core.cc extent_tree.cc
6+
add_library(dfly_core bloom.cc compact_object.cc cool_queue.cc dragonfly_core.cc extent_tree.cc
77
interpreter.cc mi_memory_resource.cc sds_utils.cc
88
segment_allocator.cc score_map.cc small_string.cc sorted_map.cc
99
tx_queue.cc dense_set.cc allocation_tracker.cc

src/core/compact_object.cc

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ size_t CompactObj::Size() const {
569569
break;
570570
}
571571
case EXTERNAL_TAG:
572-
raw_size = u_.ext_ptr.size;
572+
raw_size = u_.ext_ptr.serialized_size;
573573
break;
574574
case ROBJ_TAG:
575575
raw_size = u_.r_obj.Size();
@@ -941,12 +941,31 @@ void CompactObj::GetString(char* dest) const {
941941
LOG(FATAL) << "Bad tag " << int(taglen_);
942942
}
943943

944-
void CompactObj::SetExternal(size_t offset, size_t sz) {
944+
void CompactObj::SetExternal(size_t offset, uint32_t sz) {
945945
SetMeta(EXTERNAL_TAG, mask_);
946946

947-
u_.ext_ptr.page_index = offset / 4096;
947+
u_.ext_ptr.is_cool = 0;
948948
u_.ext_ptr.page_offset = offset % 4096;
949-
u_.ext_ptr.size = sz;
949+
u_.ext_ptr.serialized_size = sz;
950+
u_.ext_ptr.offload.page_index = offset / 4096;
951+
}
952+
953+
void CompactObj::SetCold(size_t offset, uint32_t sz, detail::TieredColdRecord* record) {
954+
SetMeta(EXTERNAL_TAG, mask_);
955+
u_.ext_ptr.is_cool = 1;
956+
u_.ext_ptr.page_offset = offset % 4096;
957+
u_.ext_ptr.serialized_size = sz;
958+
u_.ext_ptr.cold_record = record;
959+
}
960+
961+
auto CompactObj::GetCold() const -> ColdItem {
962+
DCHECK(IsExternal() && u_.ext_ptr.is_cool);
963+
964+
ColdItem res;
965+
res.page_offset = u_.ext_ptr.page_offset;
966+
res.serialized_size = u_.ext_ptr.serialized_size;
967+
res.record = u_.ext_ptr.cold_record;
968+
return res;
950969
}
951970

952971
void CompactObj::ImportExternal(const CompactObj& src) {
@@ -957,8 +976,10 @@ void CompactObj::ImportExternal(const CompactObj& src) {
957976

958977
std::pair<size_t, size_t> CompactObj::GetExternalSlice() const {
959978
DCHECK_EQ(EXTERNAL_TAG, taglen_);
960-
size_t offset = size_t(u_.ext_ptr.page_index) * 4096 + u_.ext_ptr.page_offset;
961-
return pair<size_t, size_t>(offset, size_t(u_.ext_ptr.size));
979+
DCHECK_EQ(u_.ext_ptr.is_cool, 0);
980+
981+
size_t offset = size_t(u_.ext_ptr.offload.page_index) * 4096 + u_.ext_ptr.page_offset;
982+
return pair<size_t, size_t>(offset, size_t(u_.ext_ptr.serialized_size));
962983
}
963984

964985
void CompactObj::Materialize(std::string_view blob, bool is_raw) {

src/core/compact_object.h

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2022, DragonflyDB authors. All rights reserved.
1+
// Copyright 2024, DragonflyDB authors. All rights reserved.
22
// See LICENSE for licensing terms.
33
//
44

@@ -92,6 +92,8 @@ class RobjWrapper {
9292

9393
} __attribute__((packed));
9494

95+
struct TieredColdRecord;
96+
9597
} // namespace detail
9698

9799
class CompactObj {
@@ -323,7 +325,21 @@ class CompactObj {
323325
return taglen_ == EXTERNAL_TAG;
324326
}
325327

326-
void SetExternal(size_t offset, size_t sz);
328+
bool IsCool() const {
329+
assert(IsExternal());
330+
return u_.ext_ptr.is_cool;
331+
}
332+
333+
void SetExternal(size_t offset, uint32_t sz);
334+
void SetCold(size_t offset, uint32_t serialized_size, detail::TieredColdRecord* record);
335+
336+
struct ColdItem {
337+
uint16_t page_offset;
338+
size_t serialized_size;
339+
detail::TieredColdRecord* record;
340+
};
341+
ColdItem GetCold() const;
342+
327343
void ImportExternal(const CompactObj& src);
328344

329345
std::pair<size_t, size_t> GetExternalSlice() const;
@@ -410,13 +426,22 @@ class CompactObj {
410426
mask_ = mask;
411427
}
412428

429+
// Must be 16 bytes.
413430
struct ExternalPtr {
414-
uint32_t type : 8;
415-
uint32_t reserved : 24;
416-
uint32_t page_index;
431+
uint32_t serialized_size;
417432
uint16_t page_offset; // 0 for multi-page blobs. != 0 for small blobs.
418-
uint16_t reserved2;
419-
uint32_t size;
433+
uint16_t is_cool : 1;
434+
uint16_t is_reserved : 15;
435+
436+
struct Offload {
437+
uint32_t page_index;
438+
uint32_t reserved;
439+
};
440+
441+
union {
442+
Offload offload;
443+
detail::TieredColdRecord* cold_record;
444+
};
420445
} __attribute__((packed));
421446

422447
struct JsonWrapper {
@@ -508,6 +533,20 @@ class CompactObjectView {
508533
CompactObj obj_;
509534
};
510535

536+
namespace detail {
537+
538+
struct TieredColdRecord {
539+
TieredColdRecord* next = nullptr;
540+
TieredColdRecord* prev = nullptr;
541+
uint64_t key_hash; // Allows searching the entry in the dbslice.
542+
CompactObj value;
543+
uint16_t db_index;
544+
uint32_t page_index;
545+
};
546+
static_assert(sizeof(TieredColdRecord) == 48);
547+
548+
}; // namespace detail
549+
511550
} // namespace dfly
512551

513552
namespace std {

src/core/cool_queue.cc

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2024, DragonflyDB authors. All rights reserved.
2+
// See LICENSE for licensing terms.
3+
//
4+
5+
#include "src/core/cool_queue.h"
6+
7+
#include "base/logging.h"
8+
9+
namespace dfly {
10+
11+
CoolQueue::~CoolQueue() {
12+
while (!Empty()) {
13+
auto* record = PopBack();
14+
CompactObj::DeleteMR<detail::TieredColdRecord>(record);
15+
}
16+
}
17+
18+
detail::TieredColdRecord* CoolQueue::PushFront(uint16_t db_index, uint64_t key_hash,
19+
uint32_t page_index, CompactObj obj) {
20+
detail::TieredColdRecord* record = CompactObj::AllocateMR<detail::TieredColdRecord>();
21+
record->key_hash = key_hash;
22+
record->db_index = db_index;
23+
record->page_index = page_index;
24+
record->value = std::move(obj);
25+
26+
record->next = head_;
27+
if (head_) {
28+
head_->prev = record;
29+
} else {
30+
DCHECK(tail_ == nullptr);
31+
tail_ = record;
32+
}
33+
head_ = record;
34+
used_memory_ += (sizeof(detail::TieredColdRecord) + record->value.MallocUsed());
35+
return record;
36+
}
37+
38+
detail::TieredColdRecord* CoolQueue::PopBack() {
39+
auto* res = tail_;
40+
if (tail_) {
41+
auto* prev = tail_->prev;
42+
tail_->prev = nullptr;
43+
if (prev) {
44+
prev->next = nullptr;
45+
} else {
46+
DCHECK(tail_ == head_);
47+
head_ = nullptr;
48+
}
49+
tail_ = prev;
50+
used_memory_ -= (sizeof(detail::TieredColdRecord) + res->value.MallocUsed());
51+
}
52+
return res;
53+
}
54+
55+
CompactObj CoolQueue::Erase(detail::TieredColdRecord* record) {
56+
DCHECK(record);
57+
58+
if (record == tail_) {
59+
PopBack();
60+
} else {
61+
used_memory_ -= (sizeof(detail::TieredColdRecord) + record->value.MallocUsed());
62+
record->next->prev = record->prev;
63+
if (record->prev) {
64+
record->prev->next = record->next;
65+
} else {
66+
DCHECK(record == head_);
67+
head_ = record->next;
68+
}
69+
}
70+
71+
CompactObj res = std::move(record->value);
72+
CompactObj::DeleteMR<detail::TieredColdRecord>(record);
73+
return res;
74+
}
75+
} // namespace dfly

src/core/cool_queue.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2024, DragonflyDB authors. All rights reserved.
2+
// See LICENSE for licensing terms.
3+
//
4+
5+
#pragma once
6+
7+
#include "core/compact_object.h"
8+
9+
namespace dfly {
10+
11+
// Used to store "cold" items before erasing them from RAM.
12+
//
13+
class CoolQueue {
14+
public:
15+
~CoolQueue();
16+
17+
bool Empty() const {
18+
return head_ == nullptr;
19+
}
20+
21+
detail::TieredColdRecord* PushFront(uint16_t db_index, uint64_t key_hash, uint32_t page_index,
22+
CompactObj obj);
23+
24+
// The ownership is passed to the caller. The record must be deleted with
25+
// CompactObj::DeleteMR<detail::TieredColdRecord>.
26+
detail::TieredColdRecord* PopBack();
27+
28+
CompactObj Erase(detail::TieredColdRecord* record);
29+
30+
size_t UsedMemory() const;
31+
32+
private:
33+
detail::TieredColdRecord* head_ = nullptr;
34+
detail::TieredColdRecord* tail_ = nullptr;
35+
size_t used_memory_ = 0;
36+
};
37+
38+
} // namespace dfly

src/core/dfly_core_test.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//
44

55
#include "base/gtest.h"
6+
#include "core/cool_queue.h"
67
#include "core/intent_lock.h"
78
#include "core/tx_queue.h"
89

@@ -65,4 +66,15 @@ TEST_F(IntentLockTest, Basic) {
6566
ASSERT_TRUE(lk_.Check(IntentLock::EXCLUSIVE));
6667
}
6768

69+
TEST_F(TxQueueTest, CoolQueue) {
70+
CoolQueue queue;
71+
72+
ASSERT_TRUE(queue.Empty());
73+
auto* record = queue.PushFront(0, 1, 2, CompactObj{});
74+
EXPECT_EQ(record->key_hash, 1);
75+
ASSERT_FALSE(queue.Empty());
76+
queue.PopBack();
77+
ASSERT_TRUE(queue.Empty());
78+
}
79+
6880
} // namespace dfly

0 commit comments

Comments
 (0)