Skip to content

Commit f7e7867

Browse files
pbhandar2meta-codesync[bot]
authored andcommitted
Add unit tests for AccessTimeMap shm persistence
Summary: Add 5 unit tests exercising the persist/recover methods added in the previous diff: - PersistAndRecover: round-trip 4 entries through shm and verify values. - RecoverVersionMismatch: tamper with stored version, verify map starts empty. - RecoverConfigMismatch: persist with 8 shards, recover with 4 shards, verify map starts empty. - PersistEmptyMap: persist an empty map, verify recovery succeeds and map remains empty. - RecoverNoShm: recover when no segments exist, verify map starts empty. Also adds the `//cachelib/shm:shm` dep to the test BUCK target. Reviewed By: byahn0996 Differential Revision: D95409646 fbshipit-source-id: be2ffe4be37d74338dab92926322185bb0610b2d
1 parent 86e4903 commit f7e7867

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

cachelib/allocator/nvmcache/tests/AccessTimeMapTest.cpp

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "cachelib/allocator/nvmcache/AccessTimeMap.h"
2323
#include "cachelib/common/Utils.h"
24+
#include "cachelib/shm/ShmManager.h"
2425

2526
namespace facebook {
2627
namespace cachelib {
@@ -271,6 +272,156 @@ TEST(AccessTimeMapTest, CounterTypes) {
271272
util::CounterVisitor::CounterType::RATE);
272273
}
273274

275+
TEST(AccessTimeMapTest, PersistAndRecover) {
276+
const auto shmDir = "/tmp/atm_persist_test_" + std::to_string(::getpid());
277+
constexpr size_t kNumShards = 4;
278+
constexpr size_t kMaxSize = 1000;
279+
280+
// Populate and persist
281+
{
282+
ShmManager shm(shmDir, true);
283+
AccessTimeMap m(kNumShards, kMaxSize);
284+
285+
m.set(10, 100);
286+
m.set(20, 200);
287+
m.set(30, 300);
288+
m.set(40, 400);
289+
ASSERT_EQ(m.size(), 4);
290+
291+
m.persist(shm);
292+
shm.shutDown();
293+
}
294+
295+
// Recover into a new map and verify
296+
{
297+
ShmManager shm(shmDir, true);
298+
AccessTimeMap m(kNumShards, kMaxSize);
299+
ASSERT_EQ(m.size(), 0);
300+
301+
m.recover(shm);
302+
ASSERT_EQ(m.size(), 4);
303+
EXPECT_EQ(m.get(10), 100);
304+
EXPECT_EQ(m.get(20), 200);
305+
EXPECT_EQ(m.get(30), 300);
306+
EXPECT_EQ(m.get(40), 400);
307+
308+
shm.shutDown();
309+
}
310+
311+
ShmManager::cleanup(shmDir, true);
312+
}
313+
314+
TEST(AccessTimeMapTest, RecoverVersionMismatch) {
315+
const auto shmDir = "/tmp/atm_version_test_" + std::to_string(::getpid());
316+
constexpr size_t kNumShards = 4;
317+
318+
// Persist with current version
319+
{
320+
ShmManager shm(shmDir, true);
321+
AccessTimeMap m(kNumShards);
322+
323+
m.set(1, 10);
324+
m.persist(shm);
325+
326+
// Overwrite the info segment with a bad version
327+
shm.removeShm(std::string(AccessTimeMap::kShmInfoName));
328+
navy::serialization::AccessTimeMapConfig cfg;
329+
*cfg.version() = 999;
330+
*cfg.numShards() = static_cast<int64_t>(kNumShards);
331+
*cfg.maxSize() = 0;
332+
*cfg.entryCount() = 1;
333+
334+
auto ioBuf = Serializer::serializeToIOBuf(cfg);
335+
auto infoAddr = shm.createShm(std::string(AccessTimeMap::kShmInfoName),
336+
ioBuf->length());
337+
Serializer serializer(
338+
reinterpret_cast<uint8_t*>(infoAddr.addr),
339+
reinterpret_cast<uint8_t*>(infoAddr.addr) + ioBuf->length());
340+
serializer.writeToBuffer(std::move(ioBuf));
341+
342+
shm.shutDown();
343+
}
344+
345+
// Recovery should detect version mismatch and start empty
346+
{
347+
ShmManager shm(shmDir, true);
348+
AccessTimeMap m(kNumShards);
349+
m.recover(shm);
350+
EXPECT_EQ(m.size(), 0);
351+
shm.shutDown();
352+
}
353+
354+
ShmManager::cleanup(shmDir, true);
355+
}
356+
357+
TEST(AccessTimeMapTest, RecoverDifferentShardCount) {
358+
const auto shmDir = "/tmp/atm_config_test_" + std::to_string(::getpid());
359+
constexpr size_t kNumShards = 8;
360+
361+
// Persist with 8 shards
362+
{
363+
ShmManager shm(shmDir, true);
364+
AccessTimeMap m(kNumShards);
365+
366+
m.set(1, 10);
367+
m.set(2, 20);
368+
m.persist(shm);
369+
shm.shutDown();
370+
}
371+
372+
// Recover with 4 shards — entries should still be recovered
373+
{
374+
ShmManager shm(shmDir, true);
375+
AccessTimeMap m(4);
376+
m.recover(shm);
377+
EXPECT_EQ(m.size(), 2);
378+
EXPECT_EQ(m.get(1), 10);
379+
EXPECT_EQ(m.get(2), 20);
380+
shm.shutDown();
381+
}
382+
383+
ShmManager::cleanup(shmDir, true);
384+
}
385+
386+
TEST(AccessTimeMapTest, PersistEmptyMap) {
387+
const auto shmDir = "/tmp/atm_empty_test_" + std::to_string(::getpid());
388+
constexpr size_t kNumShards = 4;
389+
390+
// Persist an empty map
391+
{
392+
ShmManager shm(shmDir, true);
393+
AccessTimeMap m(kNumShards);
394+
ASSERT_EQ(m.size(), 0);
395+
m.persist(shm);
396+
shm.shutDown();
397+
}
398+
399+
// Recover should succeed and remain empty
400+
{
401+
ShmManager shm(shmDir, true);
402+
AccessTimeMap m(kNumShards);
403+
m.recover(shm);
404+
EXPECT_EQ(m.size(), 0);
405+
shm.shutDown();
406+
}
407+
408+
ShmManager::cleanup(shmDir, true);
409+
}
410+
411+
TEST(AccessTimeMapTest, RecoverNoShm) {
412+
const auto shmDir = "/tmp/atm_noshm_test_" + std::to_string(::getpid());
413+
constexpr size_t kNumShards = 4;
414+
415+
// Recover when no shm segments exist - should start empty
416+
ShmManager shm(shmDir, true);
417+
AccessTimeMap m(kNumShards);
418+
m.recover(shm);
419+
EXPECT_EQ(m.size(), 0);
420+
shm.shutDown();
421+
422+
ShmManager::cleanup(shmDir, true);
423+
}
424+
274425
} // namespace tests
275426
} // namespace cachelib
276427
} // namespace facebook

0 commit comments

Comments
 (0)