From 6b160e90c3753c9e48ffd7447ff033a30998a926 Mon Sep 17 00:00:00 2001 From: Abhijat Malviya Date: Fri, 21 Nov 2025 12:16:39 +0530 Subject: [PATCH 1/5] core: Introduce stateless allocator for JSON A stateless allocator is added for JSON parsing. This allocator forwards all requests to a thread local pointer to a memory resource, and assumes that the resource has been initialized when the allocator is constructed. Signed-off-by: Abhijat Malviya --- src/core/json/json_object.cc | 4 ++++ src/core/json/json_object.h | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/core/json/json_object.cc b/src/core/json/json_object.cc index d0f12d7b9431..1bc1603c9f80 100644 --- a/src/core/json/json_object.cc +++ b/src/core/json/json_object.cc @@ -46,6 +46,10 @@ std::optional ParseWithDecoder(std::string_view input, json_decoder&& deco namespace dfly { +void InitTLJsonHeap(PMR_NS::memory_resource* mr) { + detail::tl_mr = mr; +} + std::optional JsonFromString(std::string_view input) { return ParseWithDecoder(input, json_decoder{}); } diff --git a/src/core/json/json_object.h b/src/core/json/json_object.h index b9e72eaf8b0d..41972463f25d 100644 --- a/src/core/json/json_object.h +++ b/src/core/json/json_object.h @@ -21,6 +21,52 @@ namespace dfly { +namespace detail { + +inline thread_local PMR_NS::memory_resource* tl_mr = nullptr; + +template class StatelessJsonAllocator { + public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using is_always_equal = std::true_type; + + template StatelessJsonAllocator(const StatelessJsonAllocator&) noexcept { + } + + StatelessJsonAllocator() noexcept { + DCHECK_NE(tl_mr, nullptr) << "json allocator created without backing memory resource"; + }; + + static value_type* allocate(size_type n) { + void* ptr = tl_mr->allocate(n * sizeof(value_type), alignof(value_type)); + return static_cast(ptr); + } + + static void deallocate(value_type* ptr, size_type n) noexcept { + tl_mr->deallocate(ptr, n * sizeof(value_type), alignof(value_type)); + } + + static PMR_NS::memory_resource* resource() { + return tl_mr; + } +}; + +template +bool operator==(const StatelessJsonAllocator&, const StatelessJsonAllocator&) noexcept { + return true; +} + +template +bool operator!=(const StatelessJsonAllocator&, const StatelessJsonAllocator&) noexcept { + return false; +} + +} // namespace detail + +void InitTLJsonHeap(PMR_NS::memory_resource* mr); + using ShortLivedJSON = jsoncons::json; using JsonType = jsoncons::pmr::json; From ad5097fb016c37614ee82aaaa147af65c0ecdfde Mon Sep 17 00:00:00 2001 From: Abhijat Malviya Date: Fri, 21 Nov 2025 12:23:15 +0530 Subject: [PATCH 2/5] core,server: Use new allocator for json type The new stateless allocator is used for JSON type. The backing memory resource is initialized in engine shard. The function which parses JSON is refactored to no longer accept a memory resource, as the allocator is bound to its thread local memory resource. Signed-off-by: Abhijat Malviya --- src/core/compact_object.cc | 2 +- src/core/compact_object_test.cc | 8 ++++---- src/core/json/json_object.cc | 8 ++++---- src/core/json/json_object.h | 7 ++++--- src/core/json/jsonpath_test.cc | 2 +- src/core/page_usage_stats_test.cc | 2 +- src/server/engine_shard.cc | 1 + src/server/json_family.cc | 21 ++++++++++++++------- src/server/json_family_memory_test.cc | 5 ++--- src/server/rdb_load.cc | 3 +-- 10 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/core/compact_object.cc b/src/core/compact_object.cc index 6859991cc68d..e755c894ca3b 100644 --- a/src/core/compact_object.cc +++ b/src/core/compact_object.cc @@ -1624,7 +1624,7 @@ MemoryResource* CompactObj::memory_resource() { bool CompactObj::JsonConsT::DefragIfNeeded(PageUsage* page_usage) { if (JsonType* old = json_ptr; ShouldDefragment(page_usage)) { - json_ptr = AllocateMR(DeepCopyJSON(old, memory_resource())); + json_ptr = AllocateMR(DeepCopyJSON(old)); DeleteMR(old); return true; } diff --git a/src/core/compact_object_test.cc b/src/core/compact_object_test.cc index 75a15d7b7c37..91f4c682b90c 100644 --- a/src/core/compact_object_test.cc +++ b/src/core/compact_object_test.cc @@ -455,14 +455,14 @@ TEST_F(CompactObjectTest, JsonTypeTest) { "children":[],"spouse":null} )"; std::optional json_option2 = - JsonFromString(R"({"a":{}, "b":{"a":1}, "c":{"a":1, "b":2}})", CompactObj::memory_resource()); + ParseJsonUsingShardHeap(R"({"a":{}, "b":{"a":1}, "c":{"a":1, "b":2}})"); cobj_.SetString(json_str, false); ASSERT_TRUE(cobj_.ObjType() == OBJ_STRING); // we set this as a string JsonType* failed_json = cobj_.GetJson(); ASSERT_TRUE(failed_json == nullptr); ASSERT_TRUE(cobj_.ObjType() == OBJ_STRING); - std::optional json_option = JsonFromString(json_str, CompactObj::memory_resource()); + std::optional json_option = ParseJsonUsingShardHeap(json_str); ASSERT_TRUE(json_option.has_value()); cobj_.SetJson(std::move(json_option.value())); ASSERT_TRUE(cobj_.ObjType() == OBJ_JSON); // and now this is a JSON type @@ -477,7 +477,7 @@ TEST_F(CompactObjectTest, JsonTypeTest) { ASSERT_TRUE(json != nullptr); ASSERT_TRUE(json->contains("b")); ASSERT_FALSE(json->contains("firstName")); - std::optional set_array = JsonFromString("", CompactObj::memory_resource()); + std::optional set_array = ParseJsonUsingShardHeap(""); // now set it to string again cobj_.SetString(R"({"a":{}, "b":{"a":1}, "c":{"a":1, "b":2}})", false); ASSERT_TRUE(cobj_.ObjType() == OBJ_STRING); // we set this as a string @@ -504,7 +504,7 @@ TEST_F(CompactObjectTest, JsonTypeWithPathTest) { "title" : "The Night Watch", "author" : "Phillips, David Atlee" }]})"; - std::optional json_array = JsonFromString(books_json, CompactObj::memory_resource()); + std::optional json_array = ParseJsonUsingShardHeap(books_json); ASSERT_TRUE(json_array.has_value()); cobj_.SetJson(std::move(json_array.value())); ASSERT_TRUE(cobj_.ObjType() == OBJ_JSON); // and now this is a JSON type diff --git a/src/core/json/json_object.cc b/src/core/json/json_object.cc index 1bc1603c9f80..94a01de47e17 100644 --- a/src/core/json/json_object.cc +++ b/src/core/json/json_object.cc @@ -54,14 +54,14 @@ std::optional JsonFromString(std::string_view input) { return ParseWithDecoder(input, json_decoder{}); } -optional JsonFromString(string_view input, PMR_NS::memory_resource* mr) { - return ParseWithDecoder(input, json_decoder{PMR_NS::polymorphic_allocator{mr}}); +optional ParseJsonUsingShardHeap(string_view input) { + return ParseWithDecoder(input, json_decoder{detail::StatelessJsonAllocator{}}); } -JsonType DeepCopyJSON(const JsonType* j, PMR_NS::memory_resource* mr) { +JsonType DeepCopyJSON(const JsonType* j) { std::string serialized; j->dump(serialized); - auto deserialized = JsonFromString(serialized, mr); + auto deserialized = ParseJsonUsingShardHeap(serialized); DCHECK(deserialized.has_value()); return std::move(deserialized.value()); } diff --git a/src/core/json/json_object.h b/src/core/json/json_object.h index 41972463f25d..fc816971a78e 100644 --- a/src/core/json/json_object.h +++ b/src/core/json/json_object.h @@ -68,7 +68,8 @@ bool operator!=(const StatelessJsonAllocator&, const StatelessJsonAllocator>; // A helper type to use in template functions which are expected to work with both ShortLivedJSON // and JsonType @@ -82,12 +83,12 @@ std::optional JsonFromString(std::string_view input); // Parses string into JSON, using mimalloc heap for allocations. This method should only be used on // shards where mimalloc heap is initialized. -std::optional JsonFromString(std::string_view input, PMR_NS::memory_resource* mr); +std::optional ParseJsonUsingShardHeap(std::string_view input); // Deep copy a JSON object, by first serializing it to a string and then deserializing the string. // The operation is intended to help during defragmentation, by copying into a page reserved for // malloc. -JsonType DeepCopyJSON(const JsonType* j, PMR_NS::memory_resource* mr); +JsonType DeepCopyJSON(const JsonType* j); inline auto MakeJsonPathExpr(std::string_view path, std::error_code& ec) -> jsoncons::jsonpath::jsonpath_expression { diff --git a/src/core/json/jsonpath_test.cc b/src/core/json/jsonpath_test.cc index ecce8e78bc66..38bc38a5fdc3 100644 --- a/src/core/json/jsonpath_test.cc +++ b/src/core/json/jsonpath_test.cc @@ -39,7 +39,7 @@ class TestDriver : public Driver { template JSON ValidJson(string_view str); template <> JsonType ValidJson(string_view str) { - auto res = ::dfly::JsonFromString(str, pmr::get_default_resource()); + auto res = ParseJsonUsingShardHeap(str); CHECK(res) << "Failed to parse json: " << str; return *res; } diff --git a/src/core/page_usage_stats_test.cc b/src/core/page_usage_stats_test.cc index c110c45ce477..61ae608907f2 100644 --- a/src/core/page_usage_stats_test.cc +++ b/src/core/page_usage_stats_test.cc @@ -191,7 +191,7 @@ TEST_F(PageUsageStatsTest, JSONCons) { // encoding. std::string_view data{R"#({"data": "some", "count": 1, "checked": false})#"}; - auto parsed = JsonFromString(data, &m_); + auto parsed = ParseJsonUsingShardHeap(data); EXPECT_TRUE(parsed.has_value()); c_obj_.SetJson(std::move(parsed.value())); diff --git a/src/server/engine_shard.cc b/src/server/engine_shard.cc index c6e32fe1b005..abb03c33d72b 100644 --- a/src/server/engine_shard.cc +++ b/src/server/engine_shard.cc @@ -422,6 +422,7 @@ void EngineShard::InitThreadLocal(ProactorBase* pb) { CompactObj::InitThreadLocal(shard_->memory_resource()); SmallString::InitThreadLocal(data_heap); + InitTLJsonHeap(shard_->memory_resource()); shard_->shard_search_indices_ = std::make_unique(); } diff --git a/src/server/json_family.cc b/src/server/json_family.cc index 4f7e16d86bf1..4a023cdc22c7 100644 --- a/src/server/json_family.cc +++ b/src/server/json_family.cc @@ -475,7 +475,7 @@ std::optional ConvertJsonPathToJsonPointer(string_view json_path) { result before invoking SetJsonSize. Note that even after calling std::move on an optional, it may still hold the JSON value, which can lead to incorrect memory tracking. */ std::optional ShardJsonFromString(std::string_view input) { - return JsonFromString(input, CompactObj::memory_resource()); + return ParseJsonUsingShardHeap(input); } OpStatus SetFullJson(const OpArgs& op_args, string_view key, string_view json_str) { @@ -556,8 +556,7 @@ OpResult SetPartialJson(const OpArgs& op_args, string_view key, path_exists = true; if (!is_nx_condition) { value_was_set = true; - *val = JsonType(parsed_json.value(), - std::pmr::polymorphic_allocator{CompactObj::memory_resource()}); + *val = JsonType(parsed_json.value(), detail::StatelessJsonAllocator{}); } return {}; }; @@ -1526,10 +1525,18 @@ auto OpMemory(const OpArgs& op_args, string_view key, const WrappedJsonPath& jso ReadOnlyOperationOptions{false, CallbackResultOptions::DefaultReadOnlyOptions()}); } -// Returns json vector that represents the result of the json query. -auto OpResp(const OpArgs& op_args, string_view key, const WrappedJsonPath& json_path) { - auto cb = [](const string_view&, const JsonType& val) { return val; }; - return JsonReadOnlyOperation(op_args, key, json_path, std::move(cb)); +// Returns json vector that represents the result of the json query. A shard local +// heap allocated JSON cannot be copied and then destroyed on another shard because we use stateless +// allocators which forward all requests to thread local memory resource. So the value is first +// copied to the std allocator-backed type ShortLivedJSON. +OpResult> OpResp(const OpArgs& op_args, string_view key, + const WrappedJsonPath& json_path) { + auto cb = [](const string_view&, const JsonType& val) { + string s; + val.dump(s); + return JsonFromString(s); + }; + return JsonReadOnlyOperation(op_args, key, json_path, std::move(cb)); } // Returns boolean that represents the result of the operation. diff --git a/src/server/json_family_memory_test.cc b/src/server/json_family_memory_test.cc index 6cd6243d6438..01fa39c73ff6 100644 --- a/src/server/json_family_memory_test.cc +++ b/src/server/json_family_memory_test.cc @@ -111,9 +111,8 @@ TEST_F(JsonFamilyMemoryTest, JsonConsDelTest) { size_t start = GetMemoryUsage(); - auto json = dfly::JsonFromString(start_json, JsonFamilyMemoryTest::GetMemoryResource()); - void* ptr = - JsonFamilyMemoryTest::GetMemoryResource()->allocate(sizeof(JsonType), alignof(JsonType)); + auto json = ParseJsonUsingShardHeap(start_json); + void* ptr = GetMemoryResource()->allocate(sizeof(JsonType), alignof(JsonType)); JsonType* json_on_heap = new (ptr) JsonType(std::move(json).value()); size_t memory_usage_before_erase = GetMemoryUsage() - start; diff --git a/src/server/rdb_load.cc b/src/server/rdb_load.cc index 883e4e3e6fc8..e7b7903a2f1c 100644 --- a/src/server/rdb_load.cc +++ b/src/server/rdb_load.cc @@ -984,8 +984,7 @@ void RdbLoaderBase::OpaqueObjLoader::HandleBlob(string_view blob) { } else if (rdb_type_ == RDB_TYPE_JSON) { size_t start_size = static_cast(CompactObj::memory_resource())->used(); { - auto json = JsonFromString(blob, CompactObj::memory_resource()); - if (json) { + if (auto json = ParseJsonUsingShardHeap(blob)) { pv_->SetJson(std::move(*json)); } else { LOG(INFO) << "Invalid JSON string during rdb load of JSON object: " << blob; From 03a0fdc8e97d855233ad0ef092111f7b9d69970d Mon Sep 17 00:00:00 2001 From: Abhijat Malviya Date: Fri, 21 Nov 2025 12:26:02 +0530 Subject: [PATCH 3/5] core,server: Fix tests Some unit tests do custom memory resource set up and parse JSON, these are fixed so that the thread local resource used for parsing is available before test starts. Signed-off-by: Abhijat Malviya --- src/core/compact_object_test.cc | 1 + src/core/json/jsonpath_test.cc | 5 +++++ src/core/page_usage_stats_test.cc | 1 + src/server/json_family_memory_test.cc | 19 +++++++++++++------ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/core/compact_object_test.cc b/src/core/compact_object_test.cc index 91f4c682b90c..3f9d2cfd4125 100644 --- a/src/core/compact_object_test.cc +++ b/src/core/compact_object_test.cc @@ -88,6 +88,7 @@ static void InitThreadStructs() { SmallString::InitThreadLocal(tlh); thread_local MiMemoryResource mi_resource(tlh); CompactObj::InitThreadLocal(&mi_resource); + InitTLJsonHeap(&mi_resource); }; static void CheckEverythingDeallocated() { diff --git a/src/core/json/jsonpath_test.cc b/src/core/json/jsonpath_test.cc index 38bc38a5fdc3..f2fa1ce22324 100644 --- a/src/core/json/jsonpath_test.cc +++ b/src/core/json/jsonpath_test.cc @@ -89,6 +89,11 @@ bool is_array(FlatJson ref) { class ScannerTest : public ::testing::Test { protected: + void SetUp() override { + Test::SetUp(); + InitTLJsonHeap(PMR_NS::get_default_resource()); + } + ScannerTest() { driver_.lexer()->set_debug(1); } diff --git a/src/core/page_usage_stats_test.cc b/src/core/page_usage_stats_test.cc index 61ae608907f2..07c5d6a14d65 100644 --- a/src/core/page_usage_stats_test.cc +++ b/src/core/page_usage_stats_test.cc @@ -45,6 +45,7 @@ class PageUsageStatsTest : public ::testing::Test { } PageUsageStatsTest() : m_(mi_heap_get_backing()) { + InitTLJsonHeap(&m_); } void SetUp() override { diff --git a/src/server/json_family_memory_test.cc b/src/server/json_family_memory_test.cc index 01fa39c73ff6..fc68d7e26447 100644 --- a/src/server/json_family_memory_test.cc +++ b/src/server/json_family_memory_test.cc @@ -6,7 +6,6 @@ #include "base/logging.h" #include "facade/facade_test.h" #include "server/command_registry.h" -#include "server/json_family.h" #include "server/test_utils.h" using namespace testing; @@ -19,25 +18,33 @@ namespace dfly { class JsonFamilyMemoryTest : public BaseFamilyTest { public: - static dfly::MiMemoryResource* GetMemoryResource() { - static thread_local mi_heap_t* heap = mi_heap_new(); - static thread_local dfly::MiMemoryResource memory_resource{heap}; + static MiMemoryResource* GetMemoryResource() { + thread_local mi_heap_t* heap = mi_heap_new(); + thread_local MiMemoryResource memory_resource{heap}; return &memory_resource; } protected: + void SetUp() override { + BaseFamilyTest::SetUp(); + // Make the core running the thread use the same resource as the rest of the test. Although + // BaseFamilyTest initializes the heap on shards serving transactions, the core running the test + // needs this initialized explicitly. + InitTLJsonHeap(GetMemoryResource()); + } + auto GetJsonMemoryUsageFromDb(std::string_view key) { return Run({"MEMORY", "USAGE", key, "WITHOUTKEY"}); } }; size_t GetMemoryUsage() { - return static_cast(JsonFamilyMemoryTest::GetMemoryResource())->used(); + return JsonFamilyMemoryTest::GetMemoryResource()->used(); } size_t GetJsonMemoryUsageFromString(std::string_view json_str) { size_t start = GetMemoryUsage(); - auto json = dfly::JsonFromString(json_str, JsonFamilyMemoryTest::GetMemoryResource()); + auto json = ParseJsonUsingShardHeap(json_str); if (!json) { return 0; } From ed58a32b8bfd902fa4886500b1b212a04c464055 Mon Sep 17 00:00:00 2001 From: Abhijat Malviya Date: Fri, 21 Nov 2025 12:27:34 +0530 Subject: [PATCH 4/5] core,server: Make json expression templatized The json expression maker function is templatized so it can be used with both short lived and storable JSON values. The short lived variant is used within search family validation. Signed-off-by: Abhijat Malviya --- src/core/json/json_object.h | 13 +++++++++---- src/server/search/search_family.cc | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/core/json/json_object.h b/src/core/json/json_object.h index fc816971a78e..db842215af1f 100644 --- a/src/core/json/json_object.h +++ b/src/core/json/json_object.h @@ -4,6 +4,8 @@ #pragma once +#include + #include // for __cpp_lib_to_chars macro. // std::from_chars is available in C++17 if __cpp_lib_to_chars is defined. @@ -90,10 +92,13 @@ std::optional ParseJsonUsingShardHeap(std::string_view input); // malloc. JsonType DeepCopyJSON(const JsonType* j); -inline auto MakeJsonPathExpr(std::string_view path, std::error_code& ec) - -> jsoncons::jsonpath::jsonpath_expression { - return jsoncons::jsonpath::make_expression>( - jsoncons::allocator_set>(), path, ec); +template +auto MakeJsonPathExpr(std::string_view path, std::error_code& ec) + -> jsoncons::jsonpath::jsonpath_expression { + using result_allocator_t = typename Json::allocator_type; + using temp_allocator_t = std::allocator; + using allocator_set_t = jsoncons::allocator_set; + return jsoncons::jsonpath::make_expression(allocator_set_t(), path, ec); } } // namespace dfly diff --git a/src/server/search/search_family.cc b/src/server/search/search_family.cc index 3e12dbfd4698..1d4f2dd29a73 100644 --- a/src/server/search/search_family.cc +++ b/src/server/search/search_family.cc @@ -81,7 +81,7 @@ bool SendErrorIfOccurred(const ParseResult& result, CmdArgParser* parser, bool IsValidJsonPath(string_view path) { error_code ec; - MakeJsonPathExpr(path, ec); + MakeJsonPathExpr(path, ec); return !ec; } From 46a72819a6a3d698bd286b4ed7b23c54a1708680 Mon Sep 17 00:00:00 2001 From: Abhijat Malviya Date: Fri, 21 Nov 2025 12:33:53 +0530 Subject: [PATCH 5/5] core,server: Refactor ShortLivedJSON type name to TmpJson Signed-off-by: Abhijat Malviya --- src/core/json/json_object.cc | 4 ++-- src/core/json/json_object.h | 14 +++++++------- src/server/cluster/cluster_config.cc | 12 ++++++------ src/server/json_family.cc | 19 ++++++++++--------- src/server/search/search_family.cc | 2 +- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/core/json/json_object.cc b/src/core/json/json_object.cc index 94a01de47e17..b6e885b4991b 100644 --- a/src/core/json/json_object.cc +++ b/src/core/json/json_object.cc @@ -50,8 +50,8 @@ void InitTLJsonHeap(PMR_NS::memory_resource* mr) { detail::tl_mr = mr; } -std::optional JsonFromString(std::string_view input) { - return ParseWithDecoder(input, json_decoder{}); +std::optional JsonFromString(std::string_view input) { + return ParseWithDecoder(input, json_decoder{}); } optional ParseJsonUsingShardHeap(string_view input) { diff --git a/src/core/json/json_object.h b/src/core/json/json_object.h index db842215af1f..3b175e915678 100644 --- a/src/core/json/json_object.h +++ b/src/core/json/json_object.h @@ -69,11 +69,11 @@ bool operator!=(const StatelessJsonAllocator&, const StatelessJsonAllocator>; -// A helper type to use in template functions which are expected to work with both ShortLivedJSON +// A helper type to use in template functions which are expected to work with both TmpJson // and JsonType template using JsonWithAllocator = jsoncons::basic_json; @@ -81,7 +81,7 @@ using JsonWithAllocator = jsoncons::basic_json JsonFromString(std::string_view input); +std::optional JsonFromString(std::string_view input); // Parses string into JSON, using mimalloc heap for allocations. This method should only be used on // shards where mimalloc heap is initialized. @@ -95,10 +95,10 @@ JsonType DeepCopyJSON(const JsonType* j); template auto MakeJsonPathExpr(std::string_view path, std::error_code& ec) -> jsoncons::jsonpath::jsonpath_expression { - using result_allocator_t = typename Json::allocator_type; - using temp_allocator_t = std::allocator; - using allocator_set_t = jsoncons::allocator_set; - return jsoncons::jsonpath::make_expression(allocator_set_t(), path, ec); + using ResultAllocT = typename Json::allocator_type; + using TmpAllocT = std::allocator; + using AllocSetT = jsoncons::allocator_set; + return jsoncons::jsonpath::make_expression(AllocSetT(), path, ec); } } // namespace dfly diff --git a/src/server/cluster/cluster_config.cc b/src/server/cluster/cluster_config.cc index 3082cabb6879..58550dd58add 100644 --- a/src/server/cluster/cluster_config.cc +++ b/src/server/cluster/cluster_config.cc @@ -127,7 +127,7 @@ shared_ptr ClusterConfig::CreateFromConfig(string_view my_id, namespace { constexpr string_view kInvalidConfigPrefix = "Invalid JSON cluster config: "sv; -template optional ReadNumeric(const ShortLivedJSON& obj) { +template optional ReadNumeric(const TmpJson& obj) { if (!obj.is_number()) { LOG(ERROR) << kInvalidConfigPrefix << "object is not a number " << obj; return nullopt; @@ -136,7 +136,7 @@ template optional ReadNumeric(const ShortLivedJSON& obj) { return obj.as(); } -optional GetClusterSlotRanges(const ShortLivedJSON& slots) { +optional GetClusterSlotRanges(const TmpJson& slots) { if (!slots.is_array()) { LOG(ERROR) << kInvalidConfigPrefix << "slot_ranges is not an array " << slots; return nullopt; @@ -162,7 +162,7 @@ optional GetClusterSlotRanges(const ShortLivedJSON& slots) { return SlotRanges(ranges); } -optional ParseClusterNode(const ShortLivedJSON& json) { +optional ParseClusterNode(const TmpJson& json) { if (!json.is_object()) { LOG(ERROR) << kInvalidConfigPrefix << "node config is not an object " << json; return nullopt; @@ -221,7 +221,7 @@ optional ParseClusterNode(const ShortLivedJSON& json) { return node; } -optional> ParseMigrations(const ShortLivedJSON& json) { +optional> ParseMigrations(const TmpJson& json) { std::vector res; if (json.is_null()) { return res; @@ -251,7 +251,7 @@ optional> ParseMigrations(const ShortLivedJSON& json) return res; } -optional BuildClusterConfigFromJson(const ShortLivedJSON& json) { +optional BuildClusterConfigFromJson(const TmpJson& json) { std::vector config; if (!json.is_array()) { @@ -309,7 +309,7 @@ optional BuildClusterConfigFromJson(const ShortLivedJSON& jso /* static */ shared_ptr ClusterConfig::CreateFromConfig(string_view my_id, std::string_view json_str) { - optional json_config = JsonFromString(json_str); + optional json_config = JsonFromString(json_str); if (!json_config.has_value()) { LOG(ERROR) << "Can't parse JSON for ClusterConfig " << json_str; return nullptr; diff --git a/src/server/json_family.cc b/src/server/json_family.cc index 4a023cdc22c7..f3e1a0b546ef 100644 --- a/src/server/json_family.cc +++ b/src/server/json_family.cc @@ -342,7 +342,7 @@ void SendJsonString(const OpResult& result, RedisReplyBuilder* rb) { if (result) { const string& json_str = result.value(); if (rb->IsResp3()) { - if (const std::optional parsed_json = JsonFromString(json_str)) { + if (const std::optional parsed_json = JsonFromString(json_str)) { Send(parsed_json.value(), rb); return; } @@ -1525,18 +1525,19 @@ auto OpMemory(const OpArgs& op_args, string_view key, const WrappedJsonPath& jso ReadOnlyOperationOptions{false, CallbackResultOptions::DefaultReadOnlyOptions()}); } -// Returns json vector that represents the result of the json query. A shard local -// heap allocated JSON cannot be copied and then destroyed on another shard because we use stateless -// allocators which forward all requests to thread local memory resource. So the value is first -// copied to the std allocator-backed type ShortLivedJSON. -OpResult> OpResp(const OpArgs& op_args, string_view key, - const WrappedJsonPath& json_path) { +// Returns json vector that represents the result of the json query. A shard local allocated JSON +// cannot be copied and then destroyed on another shard. We use stateless allocators which forward +// all requests to thread local memory resource which uses mimalloc, and the coordinator thread may +// not have a mimalloc backed heap set up. So the value is first copied to the std allocator-backed +// type TmpJson. +OpResult> OpResp(const OpArgs& op_args, string_view key, + const WrappedJsonPath& json_path) { auto cb = [](const string_view&, const JsonType& val) { string s; val.dump(s); return JsonFromString(s); }; - return JsonReadOnlyOperation(op_args, key, json_path, std::move(cb)); + return JsonReadOnlyOperation(op_args, key, json_path, std::move(cb)); } // Returns boolean that represents the result of the operation. @@ -2011,7 +2012,7 @@ void JsonFamily::StrAppend(CmdArgList args, const CommandContext& cmd_cntx) { WrappedJsonPath json_path = GET_OR_SEND_UNEXPECTED(ParseJsonPath(path)); // We try parsing the value into json string object first. - optional parsed_json = JsonFromString(value); + optional parsed_json = JsonFromString(value); if (!parsed_json || !parsed_json->is_string()) { return builder->SendError("expected string value", kSyntaxErrType); }; diff --git a/src/server/search/search_family.cc b/src/server/search/search_family.cc index 1d4f2dd29a73..232ade9afa47 100644 --- a/src/server/search/search_family.cc +++ b/src/server/search/search_family.cc @@ -81,7 +81,7 @@ bool SendErrorIfOccurred(const ParseResult& result, CmdArgParser* parser, bool IsValidJsonPath(string_view path) { error_code ec; - MakeJsonPathExpr(path, ec); + MakeJsonPathExpr(path, ec); return !ec; }