Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cache layer #440

Merged
merged 19 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pikiwidb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

- name: Check Format
working-directory: ${{ github.workspace }}/build
run: make check-format
run: clang-format --version

build_on_macos:
runs-on: macos-latest
Expand Down
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ INCLUDE(cmake/gtest.cmake)
INCLUDE(cmake/brpc.cmake)
INCLUDE(cmake/rocksdb.cmake)
INCLUDE(cmake/braft.cmake)
INCLUDE(cmake/rediscache.cmake)

SET(PROTO_OUTPUT_DIR "${CMAKE_BINARY_DIR}/generated_pb")
FILE(MAKE_DIRECTORY "${PROTO_OUTPUT_DIR}")
Expand All @@ -190,7 +191,7 @@ ADD_SUBDIRECTORY(src/pstd)
ADD_SUBDIRECTORY(src/net)
ADD_SUBDIRECTORY(src/praft)
ADD_SUBDIRECTORY(src/storage)

ADD_SUBDIRECTORY(src/cache)
ADD_SUBDIRECTORY(src)

#############################################################################
Expand Down
34 changes: 34 additions & 0 deletions cmake/rediscache.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) 2024-present, Qihoo, Inc. All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

SET(REDISCACHE_SOURCES_DIR "${LIB_INSTALL_PREFIX}" CACHE PATH "rediscache source directory." FORCE)
SET(REDISCACHE_INSTALL_DIR "${LIB_INSTALL_PREFIX}" CACHE PATH "rediscache install directory." FORCE)
SET(REDISCACHE_INCLUDE_DIR "${LIB_INCLUDE_DIR}" CACHE PATH "rediscache include directory." FORCE)
SET(REDISCACHE_LIBRARIES "${LIB_INSTALL_DIR}/librediscache.a" CACHE FILEPATH "rediscache library." FORCE)

ExternalProject_Add(
extern_rediscache
${EXTERNAL_PROJECT_LOG_ARGS}
#URL https://github.com/pikiwidb/rediscache/archive/refs/tags/v1.0.7.tar.gz
#URL_HASH MD5=02c8aadc018dd8d4d3803cc420d1d75b
#temp used
GIT_REPOSITORY https://github.com/hahahashen/rediscache.git
GIT_TAG feat/removeUseTcMallocMacroDefinition
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_INSTALL_PREFIX=${LIB_INSTALL_PREFIX}
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DPROJECT_BINARY_DIR=${LIB_INSTALL_PREFIX}
-DCMAKE_FIND_LIBRARY_SUFFIXES=${LIB_INSTALL_PREFIX}
-DSNAPPY_BUILD_TESTS=OFF
-DCMAKE_INSTALL_INCLUDEDIR=${REDISCACHE_INCLUDE_DIR}
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
BUILD_COMMAND make -j${CPU_CORE}
)

ADD_LIBRARY(rediscache STATIC IMPORTED GLOBAL)
SET_PROPERTY(TARGET rediscache PROPERTY IMPORTED_LOCATION ${REDISCACHE_LIBRARIES})
ADD_DEPENDENCIES(rediscache extern_rediscache)

44 changes: 44 additions & 0 deletions etc/conf/pikiwidb.conf
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,47 @@ rocksdb-periodic-second 259200;
use-raft no
# Braft relies on brpc to communicate via the default port number plus the port offset
raft-port-offset 10

################### Cache Settings ###################
# the number of caches for every db
cache-num 16

# cache-mode 0:cache_none 1:cache_read
cache-mode 1
# cache-type: string, set, zset, list, hash, bit
cache-type string, set, zset, list, hash, bit

# Maximum number of keys in the zset redis cache
# On the disk DB, a zset field may have many fields. In the memory cache, we limit the maximum
# number of keys that can exist in a zset, which is zset-zset-cache-field-num-per-key, with a
# default value of 512.
zset-cache-field-num-per-key 512

# If the number of elements in a zset in the DB exceeds zset-cache-field-num-per-key,
# we determine whether to cache the first 512[zset-cache-field-num-per-key] elements
# or the last 512[zset-cache-field-num-per-key] elements in the zset based on zset-cache-start-direction.
#
# If zset-cache-start-direction is 0, cache the first 512[zset-cache-field-num-per-key] elements from the header
# If zset-cache-start-direction is -1, cache the last 512[zset-cache-field-num-per-key] elements
zset-cache-start-direction 0


# the cache maxmemory of every db, configuration 10G
cache-maxmemory 10737418240

# cache-maxmemory-policy
# 0: volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# 1: allkeys-lru -> Evict any key using approximated LRU.
# 2: volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# 3: allkeys-lfu -> Evict any key using approximated LFU.
# 4: volatile-random -> Remove a random key among the ones with an expire set.
# 5: allkeys-random -> Remove a random key, any key.
# 6: volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# 7: noeviction -> Don't evict anything, just return an error on write operations.
cache-maxmemory-policy 1

# cache-maxmemory-samples
cache-maxmemory-samples 5

# cache-lfu-decay-time
cache-lfu-decay-time 1
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ TARGET_INCLUDE_DIRECTORIES(pikiwidb
PRIVATE ${PROJECT_SOURCE_DIR}/src/pstd
PRIVATE ${PROJECT_SOURCE_DIR}/src/net
PRIVATE ${PROJECT_SOURCE_DIR}/src/storage/include
PRIVATE ${PROJECT_SOURCE_DIR}/src/cache
PRIVATE ${rocksdb_SOURCE_DIR}/
PRIVATE ${rocksdb_SOURCE_DIR}/include
PRIVATE ${BRAFT_INCLUDE_DIR}
Expand All @@ -45,6 +46,7 @@ ADD_DEPENDENCIES(pikiwidb
braft
brpc
storage
pcache
)

TARGET_LINK_LIBRARIES(pikiwidb
Expand All @@ -56,6 +58,7 @@ TARGET_LINK_LIBRARIES(pikiwidb
lz4
zstd
storage
pcache
gflags
spdlog
pstd
Expand Down
60 changes: 59 additions & 1 deletion src/base_cmd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,65 @@ void BaseCmd::Execute(PClient* client) {
if (!DoInitial(client)) {
return;
}
DoCmd(client);

if (IsNeedCacheDo(client) && PCACHE_NONE != g_config.cache_mode.load() &&
PSTORE.GetBackend(dbIndex)->GetCache()->CacheStatus() == PCACHE_STATUS_OK) {
if (IsNeedReadCache()) {
ReadCache(client);
}
if (HasFlag(kCmdFlagsReadonly) && client->CacheMiss()) {
DoThroughDB(client);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里没有命中缓存为什么还需要在写一次DB

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是,对于读命令来说,如果读命令没有命中cache就执行DB操作,读命令对应DB操作是读DB。这样处理的原因是,一个key在cache中不存在不代表在DB中不存在

if (IsNeedUpdateCache()) {
DoUpdateCache(client);
}
} else if (HasFlag(kCmdFlagsWrite)) {
DoThroughDB(client);
if (IsNeedUpdateCache()) {
DoUpdateCache(client);
}
}
} else {
DoCmd(client);
}
}

bool BaseCmd::IsNeedReadCache() const { return HasFlag(kCmdFlagsReadCache); }
bool BaseCmd::IsNeedUpdateCache() const { return HasFlag(kCmdFlagsUpdateCache); }

bool BaseCmd::IsNeedCacheDo(PClient* client) const {
if (g_config.tmp_cache_disable_flag.load()) {
return false;
}

if (HasFlag(kCmdFlagsKv)) {
if (!g_config.cache_string.load()) {
return false;
}
} else if (HasFlag(kCmdFlagsSet)) {
if (!g_config.cache_set.load()) {
return false;
}
} else if (HasFlag(kCmdFlagsZset)) {
int32_t db_len = 0;
PSTORE.GetBackend(client->GetCurrentDB())->GetStorage()->ZCard(client->Key(), &db_len);
auto zset_cache_field_num_per_key = g_config.zset_cache_field_num_per_key.load();
if (!g_config.cache_zset.load() || db_len > zset_cache_field_num_per_key) {
return false;
}
} else if (HasFlag(kCmdFlagsHash)) {
if (!g_config.cache_hash.load()) {
return false;
}
} else if (HasFlag(kCmdFlagsList)) {
if (!g_config.cache_list.load()) {
return false;
}
} else if (HasFlag(kCmdFlagsBit)) {
if (!g_config.cache_bit.load()) {
return false;
}
}
return (HasFlag(kCmdFlagsDoThroughDB));
}

std::string BaseCmd::ToBinlog(uint32_t exec_time, uint32_t term_id, uint64_t logic_id, uint32_t filenum,
Expand Down
16 changes: 16 additions & 0 deletions src/base_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ enum CmdFlags {
kCmdFlagsNoMulti = (1 << 14), // Cannot be pipelined
kCmdFlagsExclusive = (1 << 15), // May change Storage pointer, like pika's kCmdFlagsSuspend
kCmdFlagsRaft = (1 << 16), // raft
kCmdFlagsKv = (1 << 17),
kCmdFlagsHash = (1 << 18),
kCmdFlagsList = (1 << 19),
kCmdFlagsSet = (1 << 20),
kCmdFlagsZset = (1 << 21),
kCmdFlagsBit = (1 << 22),
kCmdFlagsReadCache = (1 << 23),
kCmdFlagsUpdateCache = (1 << 24),
kCmdFlagsDoThroughDB = (1 << 25),
};

enum AclCategory {
Expand Down Expand Up @@ -319,9 +328,16 @@ class BaseCmd : public std::enable_shared_from_this<BaseCmd> {

uint32_t GetCmdID() const;

bool IsNeedUpdateCache() const;
bool IsNeedReadCache() const;
bool IsNeedCacheDo(PClient* client) const;

protected:
// Execute a specific command
virtual void DoCmd(PClient* client) = 0;
virtual void DoThroughDB(PClient* client) {}
virtual void DoUpdateCache(PClient* client) {}
virtual void ReadCache(PClient* client) {}

std::string name_;
int16_t arity_ = 0;
Expand Down
26 changes: 26 additions & 0 deletions src/cache/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (c) 2024-present, Qihoo, Inc. All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.

FILE(GLOB PCACHE_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/*.cc"
)
SET(LIBRARY_OUTPUT_PATH ${PLIB_INSTALL_DIR})

ADD_LIBRARY(pcache ${PCACHE_SRC})

TARGET_INCLUDE_DIRECTORIES(pcache
PRIVATE ${PROJECT_SOURCE_DIR}/src
PRIVATE ${rocksdb_SOURCE_DIR}/include
PRIVATE ${REDISCACHE_INCLUDE_DIR}
PRIVATE ${PROJECT_SOURCE_DIR}/src/pstd
PRIVATE ${PROJECT_SOURCE_DIR}/src/storage/include
)

ADD_DEPENDENCIES(pcache rediscache storage )

TARGET_LINK_LIBRARIES(pcache pstd rediscache gflags rocksdb storage)

SET_TARGET_PROPERTIES(pcache PROPERTIES LINKER_LANGUAGE CXX)
66 changes: 66 additions & 0 deletions src/cache/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2024-present, Qihoo, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.

#pragma once

#include <cstdint>

#include "rediscache/commondef.h"

namespace cache {

/* Redis maxmemory strategies */
enum RedisMaxmemoryPolicy {
CACHE_VOLATILE_LRU = 0,
CACHE_ALLKEYS_LRU = 1,
CACHE_VOLATILE_LFU = 2,
CACHE_ALLKEYS_LFU = 3,
CACHE_VOLATILE_RANDOM = 4,
CACHE_ALLKEYS_RANDOM = 5,
CACHE_VOLATILE_TTL = 6,
CACHE_NO_EVICTION = 7
};

#define CACHE_DEFAULT_MAXMEMORY CONFIG_DEFAULT_MAXMEMORY // 10G
#define CACHE_DEFAULT_MAXMEMORY_SAMPLES CONFIG_DEFAULT_MAXMEMORY_SAMPLES
#define CACHE_DEFAULT_LFU_DECAY_TIME CONFIG_DEFAULT_LFU_DECAY_TIME

/*
* cache start pos
*/
constexpr int CACHE_START_FROM_BEGIN = 0;
constexpr int CACHE_START_FROM_END = -1;
/*
* cache items per key
*/
#define DEFAULT_CACHE_ITEMS_PER_KEY 512

struct CacheConfig {
uint64_t maxmemory; /* Can used max memory */
int32_t maxmemory_policy; /* Policy for key eviction */
int32_t maxmemory_samples; /* Precision of random sampling */
int32_t lfu_decay_time; /* LFU counter decay factor. */
int32_t zset_cache_start_direction;
int32_t zset_cache_field_num_per_key;

CacheConfig()
: maxmemory(CACHE_DEFAULT_MAXMEMORY),
maxmemory_policy(CACHE_NO_EVICTION),
maxmemory_samples(CACHE_DEFAULT_MAXMEMORY_SAMPLES),
lfu_decay_time(CACHE_DEFAULT_LFU_DECAY_TIME),
zset_cache_start_direction(CACHE_START_FROM_BEGIN),
zset_cache_field_num_per_key(DEFAULT_CACHE_ITEMS_PER_KEY) {}

CacheConfig& operator=(const CacheConfig& obj) {
maxmemory = obj.maxmemory;
maxmemory_policy = obj.maxmemory_policy;
maxmemory_samples = obj.maxmemory_samples;
lfu_decay_time = obj.lfu_decay_time;
zset_cache_start_direction = obj.zset_cache_start_direction;
zset_cache_field_num_per_key = obj.zset_cache_field_num_per_key;
return *this;
}
};
} // namespace cache
Loading
Loading