Skip to content

Commit 94a16e4

Browse files
committed
MB-32589: AddStatsStream: Correctly account allocated mem to NonBucket (take 2)
The previous fix for this issue (in the alice branch) failed to correctly account for the memory freed by the std::string created from 'buf': auto* e = ObjectRegistry::onSwitchThread(nullptr, true); > auto value = buf.str(); // value is of type std::string callback(key.data(), key.size(), value.data(), value.size(), cookie); ObjectRegistry::onSwitchThread(e); The manual onTheadSwitch() calls incorrectly assigned the deallocation of std::string to the engine as the std::string dtor runs after onThreadSwitch(e). Fix (hopefully correctly this time!) by using a scoped guard instead. Also includes a unit test. Change-Id: Ic3eb7a1aa8a00012c99910bbaf4277a5d1548c80 Reviewed-on: http://review.couchbase.org/103843 Well-Formed: Build Bot <[email protected]> Reviewed-by: Chris Farman <[email protected]> Tested-by: Build Bot <[email protected]>
1 parent b5fe9f5 commit 94a16e4

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

engines/ep/src/ep_engine.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3347,10 +3347,9 @@ class AddStatsStream : public std::ostream {
33473347
// stat data) which will be de-allocated inside the server (i.e.
33483348
// after the engine call has returned). As such we do not want to
33493349
// account such memory against this bucket.
3350-
auto* e = ObjectRegistry::onSwitchThread(nullptr, true);
3350+
SystemAllocationGuard guard;
33513351
auto value = buf.str();
33523352
callback(key.data(), key.size(), value.data(), value.size(), cookie);
3353-
ObjectRegistry::onSwitchThread(e);
33543353
}
33553354

33563355
private:

engines/ep/tests/module_tests/stats_test.cc

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "dcp/producer.h"
2525
#include "dcp/stream.h"
2626
#include "evp_store_single_threaded_test.h"
27+
#include "memory_tracker.h"
2728
#include "tasks.h"
2829
#include "test_helpers.h"
2930
#include "thread_gate.h"
@@ -144,6 +145,59 @@ TEST_F(StatTest, vbucket_takeover_stats_stream_not_active) {
144145
producer->closeStream(/*opaque*/ 0, vbid);
145146
}
146147

148+
// MB-32589: Check that _hash-dump stats correctly accounts temporary memory.
149+
TEST_F(StatTest, HashStatsMemUsed) {
150+
// Enable memory tracking hooks
151+
MemoryTracker::getInstance(*get_mock_server_api()->alloc_hooks);
152+
engine->getEpStats().memoryTrackerEnabled.store(true);
153+
154+
ASSERT_TRUE(engine->getEpStats().memoryTrackerEnabled);
155+
156+
// Add some items to VBucket 0 so the stats call has some data to
157+
// dump.
158+
store_item(0, makeStoredDocKey("key1"), std::string(100, 'x'));
159+
store_item(0, makeStoredDocKey("key2"), std::string(100, 'y'));
160+
161+
auto baselineMemory = engine->getEpStats().getPreciseTotalMemoryUsed();
162+
163+
// Perform the stats call. Should be associated with an engine at this
164+
// point (as setup by EvpGetStats in the 'real' environment).
165+
auto oldEngine = ObjectRegistry::getCurrentEngine();
166+
ASSERT_TRUE(oldEngine);
167+
168+
cb::const_char_buffer key{"_hash-dump 0"};
169+
struct Cookie : public cb::tracing::Traceable {
170+
int addStats_calls = 0;
171+
} state;
172+
173+
auto callback = [](const char* key,
174+
const uint16_t klen,
175+
const char* val,
176+
const uint32_t vlen,
177+
gsl::not_null<const void*> cookie) {
178+
Cookie& state =
179+
*reinterpret_cast<Cookie*>(const_cast<void*>(cookie.get()));
180+
state.addStats_calls++;
181+
182+
// This callback should run in the memcache-context so no engine should
183+
// be assigned to the current thread.
184+
EXPECT_FALSE(ObjectRegistry::getCurrentEngine());
185+
};
186+
187+
ASSERT_EQ(ENGINE_SUCCESS,
188+
engine->getStats(&state, key.data(), key.size(), callback));
189+
190+
// Sanity check - should have had at least 1 call to ADD_STATS (otherwise
191+
// the test isn't valid).
192+
ASSERT_GT(state.addStats_calls, 0);
193+
194+
// Should still be associated with the same engine.
195+
EXPECT_EQ(oldEngine, ObjectRegistry::getCurrentEngine());
196+
197+
// Any temporary memory should have been freed by now, and accounted
198+
// correctly.
199+
EXPECT_EQ(baselineMemory, engine->getEpStats().getPreciseTotalMemoryUsed());
200+
}
147201

148202
TEST_P(DatatypeStatTest, datatypesInitiallyZero) {
149203
// Check that the datatype stats initialise to 0

0 commit comments

Comments
 (0)