blob: 9c98fcd95bc18b026c997c4544cf12e360bd677b [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
**/
#ifndef QUICKSTEP_STORAGE_EVICTION_POLICY_HPP_
#define QUICKSTEP_STORAGE_EVICTION_POLICY_HPP_
#include <chrono>
#include <cstddef>
#include <memory>
#include <random>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "storage/StorageBlockInfo.hpp"
#include "threading/Mutex.hpp"
#include "utility/Macros.hpp"
namespace quickstep {
/**
* @brief Base class for classes with the responsibility of selecting blocks
* to evict from the buffer pool.
* @note In this class, and its subclasses, block refers to both blocks and
* blobs. From the eviction policy's perspective, they are the same.
*/
class EvictionPolicy {
public:
enum class Status {
kOk,
kBlockNotFound
};
EvictionPolicy() { }
virtual ~EvictionPolicy() { }
/**
* @brief Choose a block to evict. The policy is responsible for only choosing
* a block that is not referenced. The policy will assume that the
* block this function returns is eventually evicted.
*
* @param block Where the ID of the block should be stored.
* @return Status::kOk if there was success; Status::kNoKnownBlocks if the
* EvictionPolicy knows of no unreferenced blocks.
**/
virtual Status chooseBlockToEvict(block_id* block) = 0;
/**
* @brief Notify the EvictionPolicy that a block has been initially created
* in the buffer pool, but currently has a reference count of zero.
*
* @note We do not simply rely on blockReferenced() to inform the
* EvictionPolicy of a newly-created block on first reference, because
* it may be some time between the call to
* StorageManager::createBlock() or StorageManager::createBlob() and
* the first call to StorageManager::getBlock() (or similar) for the
* block. During this period, the block is memory-resident and taking
* up buffer pool space but it would be effectively invisible to the
* EvictionPolicy and ineligible for eviction. Thus, this method is
* called by StorageManager::createBlock() and
* StorageManager::createBlob() to inform the EvictionPolicy of a new
* block's existence before it is ever referenced.
*
* @param block The block that has been newly created.
**/
virtual void blockCreated(const block_id block) { }
/**
* @brief Notify the EvictionPolicy that a block has been evicted even though
* it wasn't returned by chooseBlockToEvict. By default, this is a
* no-op.
*
* @param block The block that has been evicted.
**/
virtual void blockEvicted(const block_id block) { }
/**
* @brief Notify the EvictionPolicy that a block has been referenced. By
* default, this is a no-op. It is called each time the block's
* reference count is incremented.
*
* @param block The block that has been referenced.
**/
virtual void blockReferenced(const block_id block) { }
/**
* @brief Notify the EvictionPolicy that a block has been unreferenced. By
* default, this is a no-op. It is called each time the block's
* reference count is decremented.
* @param block The block that has been unreferenced.
*/
virtual void blockUnreferenced(const block_id block) { }
/**
* @brief Notify the EvictionPolicy that a block has been deleted. By default,
* this is a no-op. This is useful because some eviction policies may
* keep information about a block even after it has been evicted. If a
* block is evicted and then deleted, you should still call
* blockEvicted before calling blockDeleted.
* @param block The block that has been deleted.
*/
virtual void blockDeleted(const block_id block) { }
/**
* @brief Get the reference count of block.
*
* @param block The block whose reference count to get.
* @return The block's reference count.'
**/
virtual unsigned int getRefCount(const block_id block) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(EvictionPolicy);
};
/**
* @brief Simply evicts the first block it finds that is unreferenced.
**/
class EvictAnyBlockEvictionPolicy : public EvictionPolicy {
public:
EvictAnyBlockEvictionPolicy() { }
~EvictAnyBlockEvictionPolicy() override { }
Status chooseBlockToEvict(block_id* block) override;
void blockCreated(const block_id block) override;
void blockEvicted(const block_id block) override;
void blockReferenced(const block_id block) override;
void blockUnreferenced(const block_id block) override;
unsigned int getRefCount(const block_id block) override;
private:
// Maintain a reference count for each block
std::unordered_map<block_id, unsigned int> ref_counts_;
// For performance, keep track of all known non-referenced blocks
std::unordered_set<block_id> nonreferenced_blocks_;
// We just use a single mutex for simplicity. This class isn't really meant to
// be used except in testing, so performance is not a goal.
Mutex mutex_;
DISALLOW_COPY_AND_ASSIGN(EvictAnyBlockEvictionPolicy);
};
/**
* @brief Evicts non-referenced blocks with uniform probability.
**/
class UniformRandomEvictionPolicy : public EvictionPolicy {
public:
UniformRandomEvictionPolicy() : gen_(rd_()) { }
~UniformRandomEvictionPolicy() override { }
Status chooseBlockToEvict(block_id* block) override;
void blockCreated(const block_id block) override;
void blockEvicted(const block_id block) override;
void blockReferenced(const block_id block) override;
void blockUnreferenced(const block_id block) override;
unsigned int getRefCount(const block_id block) override;
private:
// Maintain a reference count for each block
std::unordered_map<block_id, unsigned int> ref_counts_;
// Maintain a list of non-referenced blocks to simplify victim selection
std::vector<block_id> nonreferenced_blocks_;
// Used to randomize victim selection
std::random_device rd_;
std::default_random_engine gen_;
// We just use a single mutex for simplicity. This class isn't really meant to
// be used except in testing, so performance is not a goal.
Mutex mutex_;
DISALLOW_COPY_AND_ASSIGN(UniformRandomEvictionPolicy);
};
/**
* @brief LRU-k eviction policy. Only supports k = 1, 2, or 3. If k is not one
* of these three options, it will be set to 2.
**/
class LRUKEvictionPolicyFactory {
public:
/**
* @brief Construct an LRU-k policy with the specified parameters.
* @param k The policy will keep track of the last k references for each known
* block.
* @param correlated_reference_period How far apart should two references be
* before they are considered to
* uncorrelated.
*/
static EvictionPolicy* ConstructLRUKEvictionPolicy(
const std::size_t k,
const std::chrono::microseconds correlated_reference_period);
private:
DISALLOW_COPY_AND_ASSIGN(LRUKEvictionPolicyFactory);
};
} // namespace quickstep
#endif // QUICKSTEP_STORAGE_EVICTION_POLICY_HPP_