Skip to content

Commit 1e131ea

Browse files
author
wuxianrong
committed
The data backup and recovery functions have been added
1 parent 3e19283 commit 1e131ea

18 files changed

Lines changed: 982 additions & 61 deletions

File tree

.github/workflows/pika.yml

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ env:
1212
ARTIFACT_PIKA_NAME: artifact-pika
1313

1414
jobs:
15+
1516
build_on_ubuntu:
1617
# The CMake configure and build commands are platform-agnostic and should work equally well on Windows or Mac.
1718
# You can convert this to a matrix build if you need cross-platform coverage.
@@ -41,12 +42,49 @@ jobs:
4142
- name: Install Deps
4243
run: |
4344
sudo apt-get update
44-
sudo apt-get install -y autoconf libprotobuf-dev protobuf-compiler clang-tidy
45+
sudo apt-get install -y autoconf libprotobuf-dev protobuf-compiler clang-tidy gcc-11 g++-11 zlib1g-dev
46+
47+
- name: Patch glibc headers for GCC compatibility
48+
run: |
49+
# Workaround for __has_attribute compatibility issue between newer glibc and GCC
50+
# The issue is that __glibc_has_attribute uses parenthesized arguments like __glibc_has_attribute(__const__)
51+
# which is incompatible with GCC's __has_attribute implementation in GCC 11+
52+
#
53+
# Solution: Redefine __glibc_has_attribute to always return 0
54+
# This is done by replacing the macro definition itself
55+
56+
CDEFS_FILE=""
57+
if [ -f /usr/include/x86_64-linux-gnu/sys/cdefs.h ]; then
58+
CDEFS_FILE="/usr/include/x86_64-linux-gnu/sys/cdefs.h"
59+
elif [ -f /usr/include/sys/cdefs.h ]; then
60+
CDEFS_FILE="/usr/include/sys/cdefs.h"
61+
fi
62+
63+
if [ -n "$CDEFS_FILE" ]; then
64+
echo "Patching $CDEFS_FILE"
65+
echo "=== Before patching (lines with __glibc_has_attribute) ==="
66+
grep -n "__glibc_has_attribute" "$CDEFS_FILE" || echo "No matches found"
67+
echo "=========================================================="
68+
69+
sudo cp "$CDEFS_FILE" /tmp/cdefs.h.backup
70+
71+
# Replace the macro definition that uses __has_attribute with one that always returns 0
72+
# Original: # define __glibc_has_attribute(attr) __has_attribute (attr)
73+
# Changed: # define __glibc_has_attribute(attr) 0
74+
# Use extended regex (-E) for better pattern matching
75+
sudo sed -i -E 's/#[ \t]*define[ \t]+__glibc_has_attribute\(attr\)[ \t]+__has_attribute[ \t]*\(attr\)/#define __glibc_has_attribute(attr) 0/g' "$CDEFS_FILE"
76+
77+
echo "=== After patching (lines with __glibc_has_attribute) ==="
78+
grep -n "__glibc_has_attribute" "$CDEFS_FILE" || echo "No matches found"
79+
echo "========================================================="
80+
81+
echo "=== Verifying line 331 area ==="
82+
sed -n '325,340p' "$CDEFS_FILE"
83+
echo "==============================="
84+
fi
4585
4686
- name: Configure CMake
47-
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
48-
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
49-
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DUSE_PIKA_TOOLS=ON -DCMAKE_CXX_FLAGS_DEBUG=-fsanitize=address -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache
87+
run: cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DUSE_PIKA_TOOLS=ON -DCMAKE_CXX_FLAGS_DEBUG=-fsanitize=address -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11
5088

5189
- name: Build
5290
# Build your program with the given configuration
@@ -166,7 +204,7 @@ jobs:
166204
- name: Install deps
167205
run: |
168206
dnf update -y
169-
dnf install -y bash cmake wget git autoconf gcc perl-Digest-SHA tcl which tar g++ tar epel-release gcc-c++ libstdc++-devel gcc-toolset-13 binutils
207+
dnf install -y bash cmake wget git autoconf gcc perl-Digest-SHA tcl which tar g++ tar epel-release gcc-c++ libstdc++-devel gcc-toolset-12 binutils openssl openssl-devel zlib-devel
170208
dnf clean all
171209
rm -rf /var/cache/dnf
172210
@@ -180,9 +218,43 @@ jobs:
180218
with:
181219
fetch-depth: 1
182220

221+
- name: Patch glibc headers for GCC compatibility
222+
run: |
223+
# Workaround for __has_attribute compatibility issue between newer glibc and GCC
224+
# The issue is that __glibc_has_attribute uses parenthesized arguments like __glibc_has_attribute(__const__)
225+
# which is incompatible with GCC's __has_attribute implementation in GCC 11+
226+
#
227+
# Solution: Redefine __glibc_has_attribute to always return 0
228+
# This is done by replacing the macro definition itself
229+
230+
CDEFS_FILE="/usr/include/sys/cdefs.h"
231+
232+
if [ -f "$CDEFS_FILE" ]; then
233+
echo "Patching $CDEFS_FILE"
234+
echo "=== Before patching (lines with __glibc_has_attribute) ==="
235+
grep -n "__glibc_has_attribute" "$CDEFS_FILE" || echo "No matches found"
236+
echo "=========================================================="
237+
238+
cp "$CDEFS_FILE" /tmp/cdefs.h.backup
239+
240+
# Replace the macro definition that uses __has_attribute with one that always returns 0
241+
# Original: # define __glibc_has_attribute(attr) __has_attribute (attr)
242+
# Changed: # define __glibc_has_attribute(attr) 0
243+
# Use extended regex (-E) for better pattern matching
244+
sed -i -E 's/#[ \t]*define[ \t]+__glibc_has_attribute\(attr\)[ \t]+__has_attribute[ \t]*\(attr\)/#define __glibc_has_attribute(attr) 0/g' "$CDEFS_FILE"
245+
246+
echo "=== After patching (lines with __glibc_has_attribute) ==="
247+
grep -n "__glibc_has_attribute" "$CDEFS_FILE" || echo "No matches found"
248+
echo "========================================================="
249+
250+
echo "=== Verifying line 331 area ==="
251+
sed -n '325,340p' "$CDEFS_FILE"
252+
echo "==============================="
253+
fi
254+
183255
- name: Configure CMake
184256
run: |
185-
source /opt/rh/gcc-toolset-13/enable
257+
source /opt/rh/gcc-toolset-12/enable
186258
cmake -B build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DUSE_PIKA_TOOLS=ON -DCMAKE_CXX_FLAGS_DEBUG=-fsanitize=address .
187259
188260
- uses: actions/cache@v3
@@ -197,7 +269,7 @@ jobs:
197269

198270
- name: Build
199271
run: |
200-
source /opt/rh/gcc-toolset-13/enable
272+
source /opt/rh/gcc-toolset-12/enable
201273
cmake --build build --config ${{ env.BUILD_TYPE }}
202274
203275
- name: Cleanup
@@ -292,13 +364,14 @@ jobs:
292364
- name: Install Deps
293365
run: |
294366
brew list --versions cmake && brew uninstall --ignore-dependencies --force cmake || true
295-
brew install gcc@13 automake cmake make binutils
367+
brew install gcc@11 automake cmake make binutils
296368
297369
- name: Configure CMake
370+
# Use GCC 11 to avoid potential __has_attribute compatibility issues with GCC 13
298371
run: |
299-
GCC_PREFIX=$(brew --prefix gcc@13)
300-
export CC=$GCC_PREFIX/bin/gcc-13
301-
cmake -B build -DCMAKE_C_COMPILER=$GCC_PREFIX/bin/gcc-13 -DUSE_PIKA_TOOLS=ON -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_FLAGS_DEBUG=-fsanitize=address -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache
372+
GCC_PREFIX=$(brew --prefix gcc@11)
373+
export CC=$GCC_PREFIX/bin/gcc-11
374+
cmake -B build -DCMAKE_C_COMPILER=$GCC_PREFIX/bin/gcc-11 -DUSE_PIKA_TOOLS=ON -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_FLAGS_DEBUG=-fsanitize=address -D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache
302375
303376
- name: Build
304377
run: |

CMakeLists.txt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
2222
endif()
2323
endif()
2424

25-
link_directories("/opt/rh/gcc-toolset-13/root/lib/gcc/x86_64-redhat-linux/13")
25+
# Link directories for gcc-toolset on RHEL/Rocky/CentOS
26+
# Support gcc-toolset-11, gcc-toolset-12 and gcc-toolset-13
27+
if(EXISTS "/opt/rh/gcc-toolset-13/root/lib/gcc/x86_64-redhat-linux/13")
28+
link_directories("/opt/rh/gcc-toolset-13/root/lib/gcc/x86_64-redhat-linux/13")
29+
elseif(EXISTS "/opt/rh/gcc-toolset-12/root/lib/gcc/x86_64-redhat-linux/12")
30+
link_directories("/opt/rh/gcc-toolset-12/root/lib/gcc/x86_64-redhat-linux/12")
31+
elseif(EXISTS "/opt/rh/gcc-toolset-11/root/lib/gcc/x86_64-redhat-linux/11")
32+
link_directories("/opt/rh/gcc-toolset-11/root/lib/gcc/x86_64-redhat-linux/11")
33+
endif()
2634

2735
############# You should enable sanitizer if you are developing pika #############
2836
# Uncomment the following two lines to enable AddressSanitizer to detect memory leaks and other memory-related bugs.
@@ -648,6 +656,10 @@ else()
648656
endif()
649657
set(LEVELDB_INCLUDE_DIR ${INSTALL_INCLUDEDIR})
650658

659+
# Workaround for __has_attribute compatibility issue with newer GCC and glibc
660+
# The issue is that newer glibc uses __glibc_has_attribute which conflicts with GCC's __has_attribute
661+
# We need to set an include path that has a fixed version of the problematic header
662+
651663
ExternalProject_Add(brpc
652664
DEPENDS
653665
gflags
@@ -657,13 +669,14 @@ ExternalProject_Add(brpc
657669
snappy
658670
zlib
659671
URL
660-
https://github.com/apache/brpc/archive/refs/tags/1.6.0.tar.gz
672+
https://github.com/apache/brpc/archive/refs/tags/1.11.0.tar.gz
661673
URL_HASH
662-
MD5=0d37cea25bd006e89806f461ef7e39ba
674+
MD5=f55e582fb8032768f9070865b48e892d
663675
DOWNLOAD_NO_PROGRESS
664676
1
665677
UPDATE_COMMAND
666678
""
679+
667680
LOG_CONFIGURE
668681
1
669682
LOG_BUILD
@@ -677,6 +690,8 @@ ExternalProject_Add(brpc
677690
-DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}
678691
-DCMAKE_BUILD_TYPE=${LIB_BUILD_TYPE}
679692
-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
693+
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
694+
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
680695
-DWITH_GLOG=ON
681696
-DWITH_SNAPPY=ON
682697
-DBUILD_SHARED_LIBS=OFF
@@ -724,6 +739,8 @@ ExternalProject_Add(braft
724739
-DCMAKE_INSTALL_PREFIX=${STAGED_INSTALL_PREFIX}
725740
-DCMAKE_BUILD_TYPE=${LIB_BUILD_TYPE}
726741
-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}
742+
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
743+
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
727744
-DWITH_GLOG=ON
728745
-DBUILD_SHARED_LIBS=OFF
729746
-DBUILD_UNIT_TESTS=OFF

include/pika_db.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class DB : public std::enable_shared_from_this<DB>, public pstd::noncopyable {
9494
std::shared_ptr<storage::Storage> storage() const;
9595
void GetBgSaveMetaData(std::vector<std::string>* fileNames, std::string* snapshot_uuid);
9696
void BgSaveDB();
97+
pstd::Status CreateCheckpoint(const std::string& checkpoint_dir);
98+
pstd::Status LoadDBFromCheckpoint(const std::string& checkpoint_dir);
9799
void SetBinlogIoError();
98100
void SetBinlogIoErrorrelieve();
99101
bool IsBinlogIoError();

include/pika_server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ enum TaskType {
7171
kCompactRangeSets,
7272
kCompactRangeZSets,
7373
kCompactRangeList,
74+
kLoadDBFromCheckpoint,
75+
kCreateCheckpoint,
7476
};
7577

7678
struct TaskArg {

src/pika_db.cc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,75 @@ void DB::BgSaveDB() {
6767
g_pika_server->BGSaveTaskSchedule(&DoBgSave, static_cast<void*>(bg_task_arg));
6868
}
6969

70+
pstd::Status DB::CreateCheckpoint(const std::string& checkpoint_dir) {
71+
std::string checkpoint_sub_path = checkpoint_dir;
72+
if (!checkpoint_sub_path.empty() && checkpoint_sub_path.back() != '/') {
73+
checkpoint_sub_path.push_back('/');
74+
}
75+
checkpoint_sub_path += db_name_;
76+
77+
if (!pstd::FileExists(checkpoint_sub_path)) {
78+
if (pstd::CreatePath(checkpoint_sub_path, 0755) != 0) {
79+
return Status::IOError("Failed to create checkpoint path", checkpoint_sub_path);
80+
}
81+
}
82+
83+
std::shared_lock guard(dbs_rw_);
84+
auto tasks = storage_->CreateCheckpoint(checkpoint_sub_path);
85+
for (auto& task : tasks) {
86+
auto status = task.get();
87+
if (!status.ok()) {
88+
return Status::Corruption("Create checkpoint failed: " + status.ToString());
89+
}
90+
}
91+
return Status::OK();
92+
}
93+
94+
pstd::Status DB::LoadDBFromCheckpoint(const std::string& checkpoint_dir) {
95+
std::string checkpoint_sub_path = checkpoint_dir;
96+
if (!checkpoint_sub_path.empty() && checkpoint_sub_path.back() != '/') {
97+
checkpoint_sub_path.push_back('/');
98+
}
99+
checkpoint_sub_path += db_name_;
100+
101+
if (!pstd::FileExists(checkpoint_sub_path)) {
102+
return Status::NotFound("Checkpoint dir does not exist: " + checkpoint_sub_path);
103+
}
104+
105+
std::lock_guard<std::shared_mutex> guard(dbs_rw_);
106+
opened_ = false;
107+
108+
auto old_storage = storage_;
109+
storage_.reset();
110+
if (old_storage) {
111+
old_storage->Close();
112+
}
113+
114+
storage_ = std::make_shared<storage::Storage>();
115+
auto checkpoint_tasks = storage_->LoadCheckpoint(checkpoint_sub_path, db_path_);
116+
for (auto& task : checkpoint_tasks) {
117+
auto status = task.get();
118+
if (!status.ok()) {
119+
storage_.reset();
120+
return Status::Corruption("Load checkpoint failed: " + status.ToString());
121+
}
122+
}
123+
124+
storage::StorageOptions storage_options = g_pika_server->storage_options();
125+
auto open_status = storage_->Open(storage_options, db_path_);
126+
if (!open_status.ok()) {
127+
storage_.reset();
128+
return Status::Corruption("Storage open failed: " + open_status.ToString());
129+
}
130+
131+
if (!g_pika_conf->raft_enabled()) {
132+
storage_->DisableWal(false);
133+
}
134+
135+
opened_ = true;
136+
return Status::OK();
137+
}
138+
70139
void DB::SetBinlogIoError() { return binlog_io_error_.store(true); }
71140
void DB::SetBinlogIoErrorrelieve() { return binlog_io_error_.store(false); }
72141
bool DB::IsBinlogIoError() { return binlog_io_error_.load(); }
@@ -206,6 +275,11 @@ bool DB::FlushDBWithoutLock() {
206275
}
207276

208277
LOG(INFO) << db_name_ << " Delete old db...";
278+
// First close the storage properly to cancel background work and wait for threads
279+
// This prevents crash when RocksDB background threads access destroyed resources
280+
if (storage_) {
281+
storage_->Close();
282+
}
209283
storage_.reset();
210284

211285
std::string dbpath = db_path_;

src/pika_server.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,37 @@ Status PikaServer::DoSameThingSpecificDB(const std::set<std::string>& dbs, const
497497
case TaskType::kCompactRangeList:
498498
db_item.second->CompactRange(storage::DataType::kLists, arg.argv[0], arg.argv[1]);
499499
break;
500+
case TaskType::kLoadDBFromCheckpoint: {
501+
// arg.argv[0] should contain checkpoint_path
502+
if (arg.argv.empty()) {
503+
LOG(ERROR) << "LoadDBFromCheckpoint requires checkpoint_path argument";
504+
return Status::InvalidArgument("Missing checkpoint_path");
505+
}
506+
std::string checkpoint_path = arg.argv[0];
507+
auto s = db_item.second->LoadDBFromCheckpoint(checkpoint_path);
508+
if (!s.ok()) {
509+
LOG(ERROR) << "Failed to load DB from checkpoint: " << s.ToString();
510+
return s;
511+
}
512+
LOG(INFO) << "Successfully loaded DB " << db_item.first << " from checkpoint: " << checkpoint_path;
513+
break;
514+
}
515+
case TaskType::kCreateCheckpoint: {
516+
// arg.argv[0] should contain checkpoint_path
517+
if (arg.argv.empty()) {
518+
LOG(ERROR) << "CreateCheckpoint requires checkpoint_path argument";
519+
return Status::InvalidArgument("Missing checkpoint_path");
520+
}
521+
std::string checkpoint_path = arg.argv[0];
522+
auto s = db_item.second->CreateCheckpoint(checkpoint_path);
523+
if (!s.ok()) {
524+
LOG(ERROR) << "Failed to create checkpoint: " << s.ToString();
525+
return s;
526+
}
527+
LOG(INFO) << "Successfully created checkpoint for DB " << db_item.first << " at: " << checkpoint_path;
528+
break;
529+
}
530+
500531
default:
501532
break;
502533
}

0 commit comments

Comments
 (0)