Skip to content

Commit 6221169

Browse files
authored
chore(rdb): Insert big values only after full construction (#5796)
Insert big values only after construction
1 parent 61ed4e8 commit 6221169

File tree

2 files changed

+26
-37
lines changed

2 files changed

+26
-37
lines changed

src/server/rdb_load.cc

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,38 +2565,27 @@ void RdbLoader::CreateObjectOnShard(const DbContext& db_cntx, const Item* item,
25652565
item->val.rdb_type);
25662566
};
25672567

2568-
// The scope is important here, as we need to ensure that the object memory is properly
2569-
// accounted for.
2570-
DbSlice::ItAndUpdater append_res;
2571-
2572-
LoadConfig tmp_load_config = item->load_config;
2573-
2574-
// If we're appending the item to an existing key, first load the
2575-
// object.
2576-
if (tmp_load_config.append) {
2577-
append_res = db_slice->FindMutable(db_cntx, item->key);
2578-
if (IsValid(append_res.it)) {
2579-
pv_ptr = &append_res.it->second;
2568+
// Streamed big values are stored in a separate map. unique_ptr for pointer stability
2569+
thread_local std::unordered_map<std::string, std::unique_ptr<PrimeValue>> now_streamed;
2570+
LoadConfig config_copy = item->load_config;
2571+
if (item->load_config.streamed && item->load_config.append) {
2572+
if (auto it = now_streamed.find(item->key); it != now_streamed.end()) {
2573+
pv_ptr = &*now_streamed[item->key];
25802574
} else {
2581-
// If the item has expired we may not find the key. Note if the key
2582-
// is found, but expired since we started loading, we still append to
2583-
// avoid an inconsistent state where only part of the key is loaded.
2584-
// We allow expiration for values inside sset/hmap,
2585-
// so the object can unexist if all keys in it were expired
2575+
// Sets and hashes are deleted when all their entries are expired.
2576+
// If it's the case, set reset append flag and start from scratch.
25862577
bool key_is_not_expired = item->expire_ms == 0 || db_cntx.time_now_ms < item->expire_ms;
25872578
bool is_set_expiry_type = item->val.rdb_type == RDB_TYPE_HASH_WITH_EXPIRY ||
25882579
item->val.rdb_type == RDB_TYPE_SET_WITH_EXPIRY;
25892580
if (!is_set_expiry_type && key_is_not_expired) {
25902581
LOG(ERROR) << "Count not to find append key '" << item->key << "' in DB " << db_ind;
25912582
return;
25922583
}
2593-
// Because the previous batches of items for this key were fully expired,
2594-
// we need to create a new key
2595-
tmp_load_config.append = false;
2584+
config_copy.append = false;
25962585
}
25972586
}
25982587

2599-
if (auto ec = FromOpaque(item->val, tmp_load_config, pv_ptr); ec) {
2588+
if (auto ec = FromOpaque(item->val, config_copy, pv_ptr); ec) {
26002589
if (ec.value() == errc::value_expired) {
26012590
// hmap and sset values can expire and we ok with it,
26022591
// so we don't set ec_ in this case
@@ -2614,11 +2603,18 @@ void RdbLoader::CreateObjectOnShard(const DbContext& db_cntx, const Item* item,
26142603
}
26152604
LOG(ERROR) << "Could not load value for key '" << item->key << "' in DB " << db_ind;
26162605
stop_early_ = true;
2606+
now_streamed.clear();
26172607
return;
26182608
}
26192609

2620-
if (tmp_load_config.append) {
2621-
return;
2610+
if (item->load_config.streamed) {
2611+
if (now_streamed.find(item->key) == now_streamed.end())
2612+
now_streamed.emplace(item->key, make_unique<PrimeValue>(std::move(pv)));
2613+
2614+
if (!item->load_config.finalize)
2615+
return;
2616+
2617+
pv = std::move(*now_streamed.extract(item->key).mapped());
26222618
}
26232619

26242620
// We need this extra check because we don't return empty_key
@@ -2722,7 +2718,8 @@ error_code RdbLoader::LoadKeyValPair(int type, ObjSettings* settings) {
27222718
continue;
27232719
}
27242720

2725-
if (pending_read_.remaining > 0) {
2721+
item->load_config.finalize = pending_read_.remaining == 0;
2722+
if (!item->load_config.finalize) {
27262723
item->key = key;
27272724
streamed = true;
27282725
} else {
@@ -2760,7 +2757,7 @@ error_code RdbLoader::LoadKeyValPair(int type, ObjSettings* settings) {
27602757
FlushShardAsync(sid);
27612758
}
27622759
}
2763-
} while (pending_read_.remaining > 0);
2760+
} while (pending_read_.remaining > 0 && !stop_early_.load(memory_order_relaxed));
27642761

27652762
int delta_ms = (absl::GetCurrentTimeNanos() - start) / 1000'000;
27662763
LOG_IF(INFO, delta_ms > 1000) << "Took " << delta_ms << " ms to load rdb_type " << type;

src/server/rdb_load.h

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,10 @@ class RdbLoaderBase {
129129
};
130130

131131
struct LoadConfig {
132-
// Whether the loaded item is being streamed incrementally in partial
133-
// reads.
134-
bool streamed = false;
135-
136-
// Number of elements in the object to reserve.
137-
//
138-
// Used to reserve the elements in a huge object up front, then append
139-
// in next loads.
140-
size_t reserve = 0;
141-
142-
// Whether to append to the existing object or initialize a new object.
143-
bool append = false;
132+
bool streamed = false; // Big value streamed incrementally
133+
size_t reserve = 0; // Number of elements to reserve to optimize big value load
134+
bool append = false; // Append stream to existing object
135+
bool finalize = false; // Last portion of stream, finalize object
144136
};
145137

146138
class OpaqueObjLoader;

0 commit comments

Comments
 (0)