| #include <c10/util/irange.h> | |
| #include "StoreTestCommon.hpp" | |
| #ifndef _WIN32 | |
| #include <unistd.h> | |
| #endif | |
| #include <iostream> | |
| #include <thread> | |
| #include <gtest/gtest.h> | |
| #include <c10d/FileStore.hpp> | |
| #include <c10d/PrefixStore.hpp> | |
| #ifdef _WIN32 | |
| std::string tmppath() { | |
| return c10d::test::autoGenerateTmpFilePath(); | |
| } | |
| #else | |
| std::string tmppath() { | |
| const char* tmpdir = getenv("TMPDIR"); | |
| if (tmpdir == nullptr) { | |
| tmpdir = "/tmp"; | |
| } | |
| // Create template | |
| std::vector<char> tmp(256); | |
| auto len = snprintf(tmp.data(), tmp.size(), "%s/testXXXXXX", tmpdir); | |
| tmp.resize(len); | |
| // Create temporary file | |
| auto fd = mkstemp(&tmp[0]); | |
| if (fd == -1) { | |
| throw std::system_error(errno, std::system_category()); | |
| } | |
| close(fd); | |
| return std::string(tmp.data(), tmp.size()); | |
| } | |
| #endif | |
| void testGetSet(std::string path, std::string prefix = "") { | |
| // Basic Set/Get on File Store | |
| { | |
| auto fileStore = c10::make_intrusive<c10d::FileStore>(path, 2); | |
| c10d::PrefixStore store(prefix, fileStore); | |
| c10d::test::set(store, "key0", "value0"); | |
| c10d::test::set(store, "key1", "value1"); | |
| c10d::test::set(store, "key2", "value2"); | |
| c10d::test::check(store, "key0", "value0"); | |
| c10d::test::check(store, "key1", "value1"); | |
| c10d::test::check(store, "key2", "value2"); | |
| auto numKeys = fileStore->getNumKeys(); | |
| EXPECT_EQ(numKeys, 3); | |
| // Check compareSet, does not check return value | |
| c10d::test::compareSet(store, "key0", "wrongExpectedValue", "newValue"); | |
| c10d::test::check(store, "key0", "value0"); | |
| c10d::test::compareSet(store, "key0", "value0", "newValue"); | |
| c10d::test::check(store, "key0", "newValue"); | |
| // Check deleteKey | |
| c10d::test::deleteKey(store, "key1"); | |
| numKeys = fileStore->getNumKeys(); | |
| EXPECT_EQ(numKeys, 2); | |
| c10d::test::check(store, "key0", "newValue"); | |
| c10d::test::check(store, "key2", "value2"); | |
| c10d::test::set(store, "-key0", "value-"); | |
| c10d::test::check(store, "key0", "newValue"); | |
| c10d::test::check(store, "-key0", "value-"); | |
| numKeys = fileStore->getNumKeys(); | |
| EXPECT_EQ(numKeys, 3); | |
| c10d::test::deleteKey(store, "-key0"); | |
| numKeys = fileStore->getNumKeys(); | |
| EXPECT_EQ(numKeys, 2); | |
| c10d::test::check(store, "key0", "newValue"); | |
| c10d::test::check(store, "key2", "value2"); | |
| } | |
| // Perform get on new instance | |
| { | |
| auto fileStore = c10::make_intrusive<c10d::FileStore>(path, 2); | |
| c10d::PrefixStore store(prefix, fileStore); | |
| c10d::test::check(store, "key0", "newValue"); | |
| auto numKeys = fileStore->getNumKeys(); | |
| // There will be 3 keys since we still use the same underlying file as the | |
| // other store above. | |
| EXPECT_EQ(numKeys, 3); | |
| } | |
| } | |
| void stressTestStore(std::string path, std::string prefix = "") { | |
| // Hammer on FileStore::add | |
| const auto numThreads = 4; | |
| const auto numIterations = 100; | |
| std::vector<std::thread> threads; | |
| c10d::test::Semaphore sem1, sem2; | |
| for (C10_UNUSED const auto i : c10::irange(numThreads)) { | |
| threads.emplace_back(std::thread([&] { | |
| auto fileStore = | |
| c10::make_intrusive<c10d::FileStore>(path, numThreads + 1); | |
| c10d::PrefixStore store(prefix, fileStore); | |
| sem1.post(); | |
| sem2.wait(); | |
| for (C10_UNUSED const auto j : c10::irange(numIterations)) { | |
| store.add("counter", 1); | |
| } | |
| })); | |
| } | |
| sem1.wait(numThreads); | |
| sem2.post(numThreads); | |
| for (auto& thread : threads) { | |
| thread.join(); | |
| } | |
| // Check that the counter has the expected value | |
| { | |
| auto fileStore = c10::make_intrusive<c10d::FileStore>(path, numThreads + 1); | |
| c10d::PrefixStore store(prefix, fileStore); | |
| std::string expected = std::to_string(numThreads * numIterations); | |
| c10d::test::check(store, "counter", expected); | |
| } | |
| } | |
| class FileStoreTest : public ::testing::Test { | |
| protected: | |
| void SetUp() override { | |
| path_ = tmppath(); | |
| } | |
| void TearDown() override { | |
| unlink(path_.c_str()); | |
| } | |
| std::string path_; | |
| }; | |
| TEST_F(FileStoreTest, testGetAndSet) { | |
| testGetSet(path_); | |
| } | |
| TEST_F(FileStoreTest, testGetAndSetWithPrefix) { | |
| testGetSet(path_, "testPrefix"); | |
| } | |
| TEST_F(FileStoreTest, testStressStore) { | |
| stressTestStore(path_); | |
| } | |
| TEST_F(FileStoreTest, testStressStoreWithPrefix) { | |
| stressTestStore(path_, "testPrefix"); | |
| } |