cachelib/cachebench/runner/FastShutdown.cpp (97 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cachelib/cachebench/runner/FastShutdown.h" #include <folly/logging/xlog.h> #include <iostream> namespace facebook { namespace cachelib { namespace cachebench { FastShutdownStressor::FastShutdownStressor(const CacheConfig& cacheConfig, uint64_t numOps) : numOps_(numOps), cacheDir_{folly::sformat("/tmp/cache_bench_fss_{}", getpid())}, cache_(std::make_unique<Cache<LruAllocator>>( cacheConfig, nullptr, cacheDir_)) {} void FastShutdownStressor::start() { startTime_ = std::chrono::system_clock::now(); constexpr uint32_t kSlabSize = 4 * 1024 * 1024; uint32_t nslabs = cache_->getCacheSize() / kSlabSize; uint32_t numSmallAllocs = kSlabSize / 64; using CacheType = Cache<LruAllocator>; uint64_t expectedAbortCount = 0; // Test with wait time 6 seconds first time and wait time 30 seconds second // time to interrupt slab release at different places. uint32_t waitTime = 6; for (; waitTime <= numOps_ * 3; waitTime += 3) { std::vector<CacheType::ItemHandle> v; std::cout << "allocating....\n"; for (uint32_t i = 0; i < nslabs; i++) { for (uint32_t j = 0; j < numSmallAllocs; j++) { auto it = cache_->allocate( static_cast<uint8_t>(0), folly::sformat("key_{}", i * numSmallAllocs + j), 5); if (it) { cache_->insertOrReplace(it); v.push_back(std::move(it)); } } ops_.fetch_add(numSmallAllocs, std::memory_order_relaxed); } std::cout << "removing most of the allocated items....\n"; // Free up items one per slab so that items from same slab are not together // in the free allocs list. Skip some while doing this. for (uint32_t j = 0; j < numSmallAllocs; j++) { // skip some items if (j % 10000 == 0) { continue; } for (uint32_t i = 0; i < nslabs; i++) { // Adding the check to make the linter happy if (!v.empty()) { v[i * numSmallAllocs + j].reset(); cache_->remove(folly::sformat("key_{}", i * numSmallAllocs + j)); } } } // create a thread that tries to allocate from a different slab class // resulting in pool rebalancer to release one slab from class id 0 to // class id 1. std::cout << "creating thread...\n"; testThread_ = std::thread([this] { int count = 0; // This should trigger rebalancer to release a slab from class id 0. while (count < 20) { auto it1 = cache_->allocate(static_cast<uint8_t>(0), "nkey1", 50); if (it1) { cache_->insertOrReplace(it1); break; } /* sleep override */ std::this_thread::sleep_for(std::chrono::milliseconds{50}); count++; } }); std::cout << "sleeping for " << waitTime << " seconds\n"; // It could take up to 3 seconds for the rebalancer to kick in. /* sleep override */ std::this_thread::sleep_for(std::chrono::seconds{waitTime}); std::cout << "releasing the last few items...\n"; // release the items that were not removed, so that the items can be // moved. for (uint32_t j = 0; j < numSmallAllocs; j += 10000) { for (uint32_t i = 0; i < nslabs; i++) { // Adding the check to make the linter happy if (!v.empty()) { v[i * numSmallAllocs + j].reset(); } } } auto shutDownStartTime = std::chrono::system_clock::now(); std::cout << "Shutting Down...\n"; cache_->shutDown(); testThread_.join(); endTime_ = std::chrono::system_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::seconds>( endTime_ - shutDownStartTime); std::cout << "Shut down durtaion " << duration.count() << "\n"; if (duration.count() > 10) { throw std::runtime_error( folly::sformat("Failed. Took {} seconds for shutdown to complete", duration.count())); } // reattach the cache, so that stats can be collected or the test can be // repeated. std::cout << "Reattaching to cache...\n"; cache_->reAttach(); expectedAbortCount++; const auto stats = cache_->getStats(); if (stats.numAbortedSlabReleases != expectedAbortCount) { throw std::runtime_error( folly::sformat("Failed. Expected abort count did not match {} {}", stats.numAbortedSlabReleases, expectedAbortCount)); } } } } // namespace cachebench } // namespace cachelib } // namespace facebook