@@ -75,6 +75,7 @@ struct HMapWrap {
7575 }
7676
7777 public:
78+ // Create from non-external prime value
7879 HMapWrap (const PrimeValue& pv, DbContext db_cntx) {
7980 DCHECK (!pv.IsExternal () || pv.IsCool ());
8081 if (pv.Encoding () == kEncodingListPack )
@@ -83,6 +84,9 @@ struct HMapWrap {
8384 impl_ = GetStringMap (pv, db_cntx);
8485 }
8586
87+ explicit HMapWrap (detail::ListpackWrap lw) : impl_{std::move (lw)} {
88+ }
89+
8690 explicit HMapWrap (tiering::SerializedMap* sm) : impl_{sm} {
8791 }
8892
@@ -193,7 +197,12 @@ OpResult<T> ExecuteRO(Transaction* tx, F&& f) {
193197 using D = tiering::SerializedMapDecoder;
194198 util::fb2::Future<OpResult<T>> fut;
195199 auto read_cb = [fut, f = std::move (f)](io::Result<D*> res) mutable {
196- HMapWrap hw{res.value ()->Get ()};
200+ // Create wrapper from different types
201+ Overloaded ov{
202+ [](tiering::SerializedMap* sm) { return HMapWrap{sm}; },
203+ [](detail::ListpackWrap* lw) { return HMapWrap{*lw}; },
204+ };
205+ auto hw = visit (ov, res.value ()->Read ());
197206 fut.Resolve (f (hw));
198207 };
199208
@@ -216,15 +225,34 @@ OpResult<T> ExecuteRO(Transaction* tx, F&& f) {
216225}
217226
218227// Wrap write handler
219- template <typename F> auto WrapW (F&& f) {
220- using RT = std::invoke_result_t <F, HMapWrap&>;
221- return [f = std::forward<F>(f)](Transaction* t, EngineShard* es) -> RT {
228+ template <typename F> auto ExecuteW (Transaction* tx, F&& f) {
229+ using T = typename std::invoke_result_t <F, HMapWrap&>::Type;
230+ auto shard_cb = [f = std::forward<F>(f)](Transaction* t,
231+ EngineShard* es) -> OpResult<CbVariant<T>> {
232+ // Fetch value of hash type
222233 auto [key, op_args] = KeyAndArgs (t, es);
223234
224235 auto it_res = op_args.GetDbSlice ().FindMutable (op_args.db_cntx , key, OBJ_HASH);
225236 RETURN_ON_BAD_STATUS (it_res);
226237 auto & pv = it_res->it ->second ;
227238
239+ // Enqueue read for future values
240+ if (pv.IsExternal () && !pv.IsCool ()) {
241+ using D = tiering::SerializedMapDecoder;
242+ util::fb2::Future<OpResult<T>> fut;
243+ auto read_cb = [fut, f = std::move (f)](io::Result<D*> res) mutable {
244+ // Create wrapper from different types
245+ HMapWrap hw{*res.value ()->Write ()};
246+ fut.Resolve (f (hw));
247+
248+ // soak listpack wrapper back to get updated value
249+ *res.value ()->Write () = *hw.Get <detail::ListpackWrap>();
250+ };
251+
252+ es->tiered_storage ()->Read (op_args.db_cntx .db_index , key, pv, D{}, std::move (read_cb));
253+ return CbVariant<T>{std::move (fut)};
254+ }
255+
228256 // Remove document before modification
229257 op_args.shard ->search_indices ()->RemoveDoc (key, op_args.db_cntx , pv);
230258
@@ -240,8 +268,11 @@ template <typename F> auto WrapW(F&& f) {
240268 else
241269 op_args.shard ->search_indices ()->AddDoc (key, op_args.db_cntx , pv);
242270
243- return res;
271+ RETURN_ON_BAD_STATUS (res);
272+ return CbVariant<T>{std::move (res).value ()};
244273 };
274+
275+ return Unwrap (tx->ScheduleSingleHopT (std::move (shard_cb)));
245276}
246277
247278size_t EstimateListpackMinBytes (CmdArgList members) {
@@ -391,28 +422,27 @@ OpResult<vector<OptStr>> OpHMGet(const HMapWrap& hw, CmdArgList fields) {
391422 DCHECK (!fields.empty ());
392423
393424 std::vector<OptStr> result (fields.size ());
394- if (auto lw = hw.Get <detail::ListpackWrap>(); lw) {
425+ if (auto sm = hw.Get <StringMap*>(); sm) {
426+ for (size_t i = 0 ; i < fields.size (); ++i) {
427+ if (auto it = (*sm)->Find (fields[i]); it != (*sm)->end ()) {
428+ result[i].emplace (it->second , sdslen (it->second ));
429+ }
430+ }
431+ } else {
395432 absl::flat_hash_map<string_view, absl::InlinedVector<size_t , 3 >> reverse;
396433 reverse.reserve (fields.size () + 1 );
397434 for (size_t i = 0 ; i < fields.size (); ++i) {
398435 reverse[ArgS (fields, i)].push_back (i); // map fields to their index.
399436 }
400437
401- for (const auto [key, value] : *lw ) {
438+ for (const auto [key, value] : hw. Range () ) {
402439 if (auto it = reverse.find (key); it != reverse.end ()) {
403440 for (size_t index : it->second ) {
404441 DCHECK_LT (index, result.size ());
405442 result[index].emplace (value);
406443 }
407444 }
408445 }
409- } else {
410- StringMap* sm = *hw.Get <StringMap*>();
411- for (size_t i = 0 ; i < fields.size (); ++i) {
412- if (auto it = sm->Find (fields[i]); it != sm->end ()) {
413- result[i].emplace (it->second , sdslen (it->second ));
414- }
415- }
416446 }
417447
418448 return result;
@@ -424,8 +454,8 @@ struct OpSetParams {
424454 bool keepttl = false ;
425455};
426456
427- OpResult<uint32_t > OpSet (const OpArgs& op_args, string_view key, CmdArgList values,
428- const OpSetParams& op_sp = OpSetParams{}) {
457+ OpResult<CbVariant< uint32_t > > OpSet (const OpArgs& op_args, string_view key, CmdArgList values,
458+ const OpSetParams& op_sp = OpSetParams{}) {
429459 DCHECK (!values.empty () && 0 == values.size () % 2 );
430460 VLOG (2 ) << " OpSet(" << key << " )" ;
431461
@@ -438,6 +468,26 @@ OpResult<uint32_t> OpSet(const OpArgs& op_args, string_view key, CmdArgList valu
438468 auto & it = add_res.it ;
439469 PrimeValue& pv = it->second ;
440470
471+ // If the value is external, enqueue read and modify it there
472+ if (pv.IsExternal () && !pv.IsCool ()) {
473+ CHECK (op_sp.ttl == UINT32_MAX); // TODO: remove
474+ using D = tiering::SerializedMapDecoder;
475+ util::fb2::Future<OpResult<uint32_t >> fut;
476+ auto read_cb = [fut, values, &op_sp](io::Result<D*> res) mutable {
477+ // Create wrapper from different types
478+ auto & lw = *res.value ()->Write ();
479+ uint32_t created = 0 ;
480+ for (size_t i = 0 ; i < values.size (); i += 2 ) {
481+ created += lw.Insert (values[i], values[i + 1 ], op_sp.skip_if_exists );
482+ }
483+ fut.Resolve (created);
484+ };
485+
486+ op_args.shard ->tiered_storage ()->Read (op_args.db_cntx .db_index , key, pv, D{},
487+ std::move (read_cb));
488+ return CbVariant<uint32_t >{std::move (fut)};
489+ }
490+
441491 if (add_res.is_new ) {
442492 if (op_sp.ttl == UINT32_MAX) {
443493 lp = lpNew (0 );
@@ -495,7 +545,7 @@ OpResult<uint32_t> OpSet(const OpArgs& op_args, string_view key, CmdArgList valu
495545 if (auto * ts = op_args.shard ->tiered_storage (); ts)
496546 ts->TryStash (op_args.db_cntx .db_index , key, &pv);
497547
498- return created;
548+ return CbVariant< uint32_t >{ created} ;
499549}
500550
501551void HGetGeneric (CmdArgList args, uint8_t getall_mask, Transaction* tx, SinkReplyBuilder* builder) {
@@ -587,7 +637,8 @@ void HSetEx(CmdArgList args, const CommandContext& cmd_cntx) {
587637 return OpSet (t->GetOpArgs (shard), key, fields, op_sp);
588638 };
589639
590- OpResult<uint32_t > result = cmd_cntx.tx ->ScheduleSingleHopT (std::move (cb));
640+ auto delayed_result = cmd_cntx.tx ->ScheduleSingleHopT (std::move (cb));
641+ OpResult<uint32_t > result = Unwrap (std::move (delayed_result));
591642 if (result) {
592643 cmd_cntx.rb ->SendLong (*result);
593644 } else {
@@ -618,7 +669,7 @@ void HSetFamily::HDel(CmdArgList args, const CommandContext& cmd_cntx) {
618669 deleted += hw.Erase (s);
619670 return deleted;
620671 };
621- HSetReplies{cmd_cntx.rb }.Send (cmd_cntx.tx -> ScheduleSingleHopT ( WrapW (cb)));
672+ HSetReplies{cmd_cntx.rb }.Send (ExecuteW ( cmd_cntx.tx , std::move (cb)));
622673}
623674
624675void HSetFamily::HExpire (CmdArgList args, const CommandContext& cmd_cntx) {
@@ -861,7 +912,8 @@ void HSetFamily::HSet(CmdArgList args, const CommandContext& cmd_cntx) {
861912 return OpSet (t->GetOpArgs (shard), key, args);
862913 };
863914
864- OpResult<uint32_t > result = cmd_cntx.tx ->ScheduleSingleHopT (std::move (cb));
915+ auto delayed_result = cmd_cntx.tx ->ScheduleSingleHopT (std::move (cb));
916+ OpResult<uint32_t > result = Unwrap (std::move (delayed_result));
865917
866918 if (result && cmd == " HSET" ) {
867919 cmd_cntx.rb ->SendLong (*result);
@@ -876,7 +928,7 @@ void HSetFamily::HSetNx(CmdArgList args, const CommandContext& cmd_cntx) {
876928 auto cb = [&](Transaction* t, EngineShard* shard) {
877929 return OpSet (t->GetOpArgs (shard), key, args.subspan (1 ), OpSetParams{.skip_if_exists = true });
878930 };
879- HSetReplies{cmd_cntx.rb }.Send (cmd_cntx.tx ->ScheduleSingleHopT (cb));
931+ HSetReplies{cmd_cntx.rb }.Send (Unwrap ( cmd_cntx.tx ->ScheduleSingleHopT (cb) ));
880932}
881933
882934void StrVecEmplaceBack (StringVec& str_vec, const listpackEntry& lp) {
0 commit comments