Skip to content

Commit 0a1b5eb

Browse files
authored
chore(server): rdb save can now save into tcp socket directly. (#317)
In more detail, RdbSaver uses AlignedBuffer that writes into io::Sink in chunks of 4KB. It's great for the direct file I/O, but bad for sockets that receive blocks of 4KB with garbage at the end. I improved the code around this and actually simplified the logic, so now AlignedBuffer is just another Sink that is passed into serializer when writing into files. When sending to sockets a socket sink is passed instead. Also many other unrelated changes grouped into this pretty big cr. 1. dashtable readability improvements. 2. Move methods from facade::ConnectionContext - into facade::Service, make ConnectionContext a dumb object. 3. Optionally allow journal to be memory only (not backed up by a disk) by using a ring buffer to store last k entries in each journal slice. Also renamed journal_shard into journal_slice because journal has presence in each DF thread and not only in its shards. 4. Introduce journal::Entry that will consolidate any store change that happens in the thread. 5. Introduce GetRandomHex utility function. 6. Introduce two hooks: ServerFamily::OnClose that is called when a connection is closed, and ServerFamily::BreakOnShutdown that is called when process exits and any background fibers neet to break early. 7. Pull some noisy info logs out of rdb_load class. 8. Snapshot class now has the ability to subscribe to journal changes, thus it can include concurrent changes into the snapshot. Currently only journal::Op::VAL is supported (it's part of RDB format anyway). Signed-off-by: Roman Gershman <roman@dragonflydb.io>
1 parent 1733af4 commit 0a1b5eb

33 files changed

+569
-314
lines changed

src/core/dash.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -716,12 +716,12 @@ template <typename U, typename V, typename EvictionPolicy>
716716
auto DashTable<_Key, _Value, Policy>::InsertInternal(U&& key, V&& value, EvictionPolicy& ev)
717717
-> std::pair<iterator, bool> {
718718
uint64_t key_hash = DoHash(key);
719-
uint32_t seg_id = SegmentId(key_hash);
719+
uint32_t target_seg_id = SegmentId(key_hash);
720720

721721
while (true) {
722722
// Keep last global_depth_ msb bits of the hash.
723-
assert(seg_id < segment_.size());
724-
SegmentType* target = segment_[seg_id];
723+
assert(target_seg_id < segment_.size());
724+
SegmentType* target = segment_[target_seg_id];
725725

726726
// Load heap allocated segment data - to avoid TLB miss when accessing the bucket.
727727
__builtin_prefetch(target, 0, 1);
@@ -731,12 +731,12 @@ auto DashTable<_Key, _Value, Policy>::InsertInternal(U&& key, V&& value, Evictio
731731

732732
if (res) { // success
733733
++size_;
734-
return std::make_pair(iterator{this, seg_id, it.index, it.slot}, true);
734+
return std::make_pair(iterator{this, target_seg_id, it.index, it.slot}, true);
735735
}
736736

737737
/*duplicate insert, insertion failure*/
738738
if (it.found()) {
739-
return std::make_pair(iterator{this, seg_id, it.index, it.slot}, false);
739+
return std::make_pair(iterator{this, target_seg_id, it.index, it.slot}, false);
740740
}
741741

742742
// At this point we must split the segment.
@@ -749,12 +749,12 @@ auto DashTable<_Key, _Value, Policy>::InsertInternal(U&& key, V&& value, Evictio
749749
hotspot.key_hash = key_hash;
750750

751751
for (unsigned j = 0; j < HotspotBuckets::kRegularBuckets; ++j) {
752-
hotspot.probes.by_type.regular_buckets[j] = bucket_iterator{this, seg_id, bid[j]};
752+
hotspot.probes.by_type.regular_buckets[j] = bucket_iterator{this, target_seg_id, bid[j]};
753753
}
754754

755755
for (unsigned i = 0; i < Policy::kStashBucketNum; ++i) {
756756
hotspot.probes.by_type.stash_buckets[i] =
757-
bucket_iterator{this, seg_id, uint8_t(kLogicalBucketNum + i), 0};
757+
bucket_iterator{this, target_seg_id, uint8_t(kLogicalBucketNum + i), 0};
758758
}
759759
hotspot.num_buckets = HotspotBuckets::kNumBuckets;
760760

@@ -770,7 +770,7 @@ auto DashTable<_Key, _Value, Policy>::InsertInternal(U&& key, V&& value, Evictio
770770
/*unsigned start = (bid[HotspotBuckets::kNumBuckets - 1] + 1) % kLogicalBucketNum;
771771
for (unsigned i = 0; i < HotspotBuckets::kNumBuckets; ++i) {
772772
uint8_t id = (start + i) % kLogicalBucketNum;
773-
buckets.probes.arr[i] = bucket_iterator{this, seg_id, id};
773+
buckets.probes.arr[i] = bucket_iterator{this, target_seg_id, id};
774774
}
775775
garbage_collected_ += ev.GarbageCollect(buckets, this);
776776
*/
@@ -804,12 +804,12 @@ auto DashTable<_Key, _Value, Policy>::InsertInternal(U&& key, V&& value, Evictio
804804
if (target->local_depth() == global_depth_) {
805805
IncreaseDepth(global_depth_ + 1);
806806

807-
seg_id = SegmentId(key_hash);
808-
assert(seg_id < segment_.size() && segment_[seg_id] == target);
807+
target_seg_id = SegmentId(key_hash);
808+
assert(target_seg_id < segment_.size() && segment_[target_seg_id] == target);
809809
}
810810

811811
ev.RecordSplit(target);
812-
Split(seg_id);
812+
Split(target_seg_id);
813813
}
814814

815815
return std::make_pair(iterator{}, false);

src/core/dash_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,8 @@ void Segment<Key, Value, Policy>::Split(HFunc&& hfn, Segment* dest_right) {
12201220
auto it = dest_right->InsertUniq(std::forward<Key_t>(Key(bid, slot)),
12211221
std::forward<Value_t>(Value(bid, slot)), hash);
12221222
(void)it;
1223+
assert(it.index != kNanBid);
1224+
12231225
if constexpr (USE_VERSION) {
12241226
// Update the version in the destination bucket.
12251227
uint64_t ver = stash.GetVersion();

src/facade/conn_context.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class ConnectionContext {
1717
public:
1818
ConnectionContext(::io::Sink* stream, Connection* owner);
1919

20-
// We won't have any virtual methods, probably. However, since we allocate derived class,
21-
// we need to declare a virtual d-tor so we could delete them inside Connection.
20+
// We won't have any virtual methods, probably. However, since we allocate a derived class,
21+
// we need to declare a virtual d-tor, so we could properly delete it from Connection code.
2222
virtual ~ConnectionContext() {}
2323

2424
Connection* owner() {
@@ -51,10 +51,6 @@ class ConnectionContext {
5151
bool authenticated: 1;
5252
bool force_dispatch: 1; // whether we should route all requests to the dispatch fiber.
5353

54-
virtual void OnClose() {}
55-
56-
virtual std::string GetContextInfo() const { return std::string{}; }
57-
5854
private:
5955
Connection* owner_;
6056
Protocol protocol_ = Protocol::REDIS;

src/facade/dragonfly_connection.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ string Connection::GetClientInfo() const {
300300
absl::StrAppend(&res, " age=", now - creation_time_, " idle=", now - last_interaction_);
301301
absl::StrAppend(&res, " phase=", phase_, " ");
302302
if (cc_) {
303-
absl::StrAppend(&res, cc_->GetContextInfo());
303+
absl::StrAppend(&res, service_->GetContextInfo(cc_.get()));
304304
}
305305

306306
return res;
@@ -374,7 +374,7 @@ void Connection::ConnectionFlow(FiberSocketBase* peer) {
374374
VLOG(1) << "Before dispatch_fb.join()";
375375
dispatch_fb.join();
376376
VLOG(1) << "After dispatch_fb.join()";
377-
cc_->OnClose();
377+
service_->OnClose(cc_.get());
378378

379379
stats->read_buf_capacity -= io_buf_.Capacity();
380380

src/facade/service_interface.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ class ServiceInterface {
3232

3333
virtual void ConfigureHttpHandlers(util::HttpListenerBase* base) {
3434
}
35+
36+
virtual void OnClose(ConnectionContext* cntx) {
37+
}
38+
39+
virtual std::string GetContextInfo(ConnectionContext* cntx) {
40+
return {};
41+
}
3542
};
3643

3744
} // namespace facade

src/redis/redis_aux.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#define MAXMEMORY_NO_EVICTION (7<<8)
2828

2929

30-
#define CONFIG_RUN_ID_SIZE 40
30+
#define CONFIG_RUN_ID_SIZE 40U
3131

3232
#define EVPOOL_CACHED_SDS_SIZE 255
3333
#define EVPOOL_SIZE 16

src/server/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ add_executable(dragonfly dfly_main.cc)
22
cxx_link(dragonfly base dragonfly_lib)
33

44
add_library(dfly_transaction db_slice.cc engine_shard_set.cc blocking_controller.cc common.cc
5-
io_mgr.cc journal/journal.cc journal/journal_shard.cc table.cc
5+
io_mgr.cc journal/journal.cc journal/journal_slice.cc table.cc
66
tiered_storage.cc transaction.cc)
77
cxx_link(dfly_transaction uring_fiber_lib dfly_core strings_lib)
88

src/server/common.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#pragma once
66

77
#include <absl/strings/ascii.h>
8+
#include <absl/strings/str_cat.h>
89
#include <absl/types/span.h>
910

1011
#include <string_view>
@@ -131,4 +132,28 @@ extern unsigned kernel_version;
131132

132133
const char* GlobalStateName(GlobalState gs);
133134

135+
template <typename RandGen> std::string GetRandomHex(RandGen& gen, size_t len) {
136+
static_assert(std::is_same<uint64_t, decltype(gen())>::value);
137+
std::string res(len, '\0');
138+
size_t indx = 0;
139+
140+
for (size_t i = 0; i < len / 16; ++i) { // 2 chars per byte
141+
absl::AlphaNum an(absl::Hex(gen(), absl::kZeroPad16));
142+
143+
for (unsigned j = 0; j < 16; ++j) {
144+
res[indx++] = an.Piece()[j];
145+
}
146+
}
147+
148+
if (indx < res.size()) {
149+
absl::AlphaNum an(absl::Hex(gen(), absl::kZeroPad16));
150+
151+
for (unsigned j = 0; indx < res.size(); indx++, j++) {
152+
res[indx] = an.Piece()[j];
153+
}
154+
}
155+
156+
return res;
157+
}
158+
134159
} // namespace dfly

src/server/conn_context.cc

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -211,45 +211,6 @@ void ConnectionContext::SendSubscriptionChangedResponse(string_view action,
211211
(*this)->SendLong(count);
212212
}
213213

214-
void ConnectionContext::OnClose() {
215-
if (!conn_state.exec_info.watched_keys.empty()) {
216-
shard_set->RunBriefInParallel([this](EngineShard* shard) {
217-
return shard->db_slice().UnregisterConnectionWatches(&conn_state.exec_info);
218-
});
219-
}
220-
221-
if (!conn_state.subscribe_info)
222-
return;
223-
224-
if (!conn_state.subscribe_info->channels.empty()) {
225-
auto token = conn_state.subscribe_info->borrow_token;
226-
UnsubscribeAll(false);
227-
// Check that all borrowers finished processing
228-
token.Wait();
229-
}
230-
231-
if (conn_state.subscribe_info) {
232-
DCHECK(!conn_state.subscribe_info->patterns.empty());
233-
auto token = conn_state.subscribe_info->borrow_token;
234-
PUnsubscribeAll(false);
235-
// Check that all borrowers finished processing
236-
token.Wait();
237-
DCHECK(!conn_state.subscribe_info);
238-
}
239-
}
240-
241-
string ConnectionContext::GetContextInfo() const {
242-
char buf[16] = {0};
243-
unsigned index = 0;
244-
if (async_dispatch)
245-
buf[index++] = 'a';
246-
247-
if (conn_closing)
248-
buf[index++] = 't';
249-
250-
return index ? absl::StrCat("flags:", buf) : string();
251-
}
252-
253214
void ConnectionState::ExecInfo::Clear() {
254215
state = EXEC_INACTIVE;
255216
body.clear();

src/server/conn_context.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,11 @@ struct ConnectionState {
8383
// For set op - it's the flag value we are storing along with the value.
8484
// For get op - we use it as a mask of MCGetMask values.
8585
uint32_t memcache_flag = 0;
86-
// If it's a replication client - then it holds positive sync session id.
87-
uint32_t sync_session_id = 0;
86+
87+
// If this server is master, and this connection is from a secondary replica,
88+
// then it holds positive sync session id.
89+
uint32_t repl_session_id = 0;
90+
uint32_t repl_threadid = kuint32max;
8891

8992
ExecInfo exec_info;
9093
std::optional<ScriptInfo> script_info;
@@ -97,8 +100,6 @@ class ConnectionContext : public facade::ConnectionContext {
97100
: facade::ConnectionContext(stream, owner) {
98101
}
99102

100-
void OnClose() override;
101-
102103
struct DebugInfo {
103104
uint32_t shards_count = 0;
104105
TxClock clock = 0;
@@ -123,8 +124,6 @@ class ConnectionContext : public facade::ConnectionContext {
123124

124125
bool is_replicating = false;
125126

126-
std::string GetContextInfo() const override;
127-
128127
private:
129128
void SendSubscriptionChangedResponse(std::string_view action,
130129
std::optional<std::string_view> topic,

0 commit comments

Comments
 (0)