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 the ability to checkpoint an existing server, and spawn a read-only server on that view. #2548

Open
wants to merge 24 commits into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e8fd991
Add checkpoints
Sep 20, 2024
b2ce62b
Move global variables to Config
nathanlo-hrt Sep 24, 2024
770ce93
Merge branch 'unstable' into unstable
nathanlo99 Sep 24, 2024
b3e93fe
Fix lint error, rename checkpoint -> snapshot, deduplicate error mess…
nathanlo99 Sep 30, 2024
60b0860
Merge branch 'unstable' into unstable
nathanlo99 Sep 30, 2024
810a86c
Trigger tests
nathanlo-hrt Oct 3, 2024
b866c5f
Merge branch 'unstable' into unstable
git-hulk Oct 5, 2024
5a43069
Merge branch 'unstable' into unstable
nathanlo99 Oct 14, 2024
916ce73
Merge branch 'unstable' into unstable
aleksraiden Oct 15, 2024
2edb82b
Merge branch 'unstable' into unstable
nathanlo99 Oct 18, 2024
50a32d1
Merge branch 'unstable' into unstable
nathanlo99 Oct 21, 2024
d27c1bc
Move functionality out of main.cc
nathanlo-hrt Oct 21, 2024
e46818a
Format
nathanlo-hrt Oct 24, 2024
e86e115
Merge branch 'unstable' into unstable
nathanlo99 Oct 24, 2024
6f76141
Merge branch 'unstable' into unstable
nathanlo99 Nov 5, 2024
53b0ed7
Move creating snapshot from constructor to Open()
nathanlo-hrt Nov 5, 2024
7f6d0a2
Make snapshot-dir read-only
nathanlo-hrt Nov 5, 2024
694f839
Remove trailing whitespace
nathanlo-hrt Nov 6, 2024
19f25b9
Fix 'const prevents move' issue
nathanlo-hrt Nov 6, 2024
786908f
Revert changes in main.cc
nathanlo-hrt Nov 7, 2024
838a4ab
Merge branch 'unstable' into unstable
nathanlo99 Nov 7, 2024
9e6865b
Merge branch 'unstable' into unstable
nathanlo99 Nov 11, 2024
15776e5
Merge branch 'unstable' into unstable
nathanlo99 Nov 13, 2024
79309ff
Merge branch 'unstable' into unstable
nathanlo99 Nov 14, 2024
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
7 changes: 7 additions & 0 deletions kvrocks.conf
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ db-name change.me.db
# Note that you must specify a directory here, not a file name.
dir /tmp/kvrocks

# Optional: A snapshot directory
#
# If specified, Kvrocks will snapshot the DB at [dir] to the specified [snapshot-dir],
# and start a read-only server from the snapshot.
#
# snapshot-dir /tmp/kvrocks-snapshot-changeme

# You can configure where to store your server logs by the log-dir.
# If you don't specify one, we will use the above `dir` as our default log directory.
# We also can send logs to stdout/stderr is as simple as:
Expand Down
5 changes: 4 additions & 1 deletion src/cli/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ int main(int argc, char *argv[]) {
#endif

engine::Storage storage(&config);
s = storage.Open();
const bool read_only = config.snapshot_dir != "";
const DBOpenMode open_mode = read_only ? kDBOpenModeForReadOnly : kDBOpenModeDefault;
s = storage.Open(open_mode);

if (!s.IsOK()) {
LOG(ERROR) << "Failed to open: " << s.Msg();
return 1;
Expand Down
3 changes: 1 addition & 2 deletions src/config/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@
#include <rocksdb/env.h>
#include <strings.h>

#include <algorithm>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
#include <iterator>
#include <limits>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -190,6 +188,7 @@ Config::Config() {
new IntField(&force_compact_file_min_deleted_percentage, 10, 1, 100)},
{"db-name", true, new StringField(&db_name, "change.me.db")},
{"dir", true, new StringField(&dir, kDefaultDir)},
{"snapshot-dir", false, new StringField(&snapshot_dir, "")},
nathanlo99 marked this conversation as resolved.
Show resolved Hide resolved
{"backup-dir", false, new StringField(&backup_dir, kDefaultBackupDir)},
{"log-dir", true, new StringField(&log_dir, "")},
{"log-level", false, new EnumField<int>(&log_level, log_levels, google::INFO)},
Expand Down
4 changes: 4 additions & 0 deletions src/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ struct Config {
std::string replica_announce_ip;
uint32_t replica_announce_port = 0;

// The following option exists so users can spawn a read-only server from a snapshot
// of a running server.
std::string snapshot_dir;

bool persist_cluster_nodes_enabled = true;
bool slot_id_encoded = false;
bool cluster_enabled = false;
Expand Down
35 changes: 35 additions & 0 deletions src/storage/storage.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "redis_metadata.h"
#include "rocksdb/cache.h"
#include "rocksdb_crc32c.h"
#include "scope_exit.h"
#include "server/server.h"
#include "storage/batch_indexer.h"
#include "table_properties_collector.h"
Expand Down Expand Up @@ -75,12 +76,46 @@ const int64_t kIORateLimitMaxMb = 1024000;

using rocksdb::Slice;

static Status CreateSnapshot(Config &config, const std::string &snapshot_location) {
Copy link
Member

Choose a reason for hiding this comment

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

Actually I don't really understand how this works, seems this create a checkpoint when opening the file? Do you need create it on server and consume it on cli?

Copy link
Author

@nathanlo99 nathanlo99 Nov 5, 2024

Choose a reason for hiding this comment

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

Do you need create it on server and consume it on cli?

I'm not sure i understand the second part of your question

Actually I don't really understand how this works, seems this create a checkpoint when opening the file?

As for how this works, it will read the data from the dir passed by the user, then use RocksDB snapshot functionality to make a read-only copy of that data into the snapshot-dir directory, then spawn a read-only server on that copy.

This is helpful for my team to debug a running kvrocks app without accidentally changing the values of the data or closing the existing read-write server.

// The Storage destructor deletes anything at the checkpoint_dir, so we need to make
// sure it's empty in case the user happens to use a snapshot name which matches the
// default (checkpoint/)
const std::string old_checkpoint_dir = std::exchange(config.checkpoint_dir, "");
const auto checkpoint_dir_guard =
MakeScopeExit([&config, &old_checkpoint_dir] { config.checkpoint_dir = old_checkpoint_dir; });

engine::Storage storage(&config);
if (const auto s = storage.Open(kDBOpenModeForReadOnly); !s.IsOK()) {
return {Status::NotOK, fmt::format("failed to open DB in read-only mode: {}", s.Msg())};
}

rocksdb::Checkpoint *snapshot = nullptr;
if (const auto s = rocksdb::Checkpoint::Create(storage.GetDB(), &snapshot); !s.ok()) {
return {Status::NotOK, s.ToString()};
}

std::unique_ptr<rocksdb::Checkpoint> snapshot_guard(snapshot);
if (const auto s = snapshot->CreateCheckpoint(snapshot_location + "/db"); !s.ok()) {
return {Status::NotOK, s.ToString()};
}

return Status::OK();
}

Storage::Storage(Config *config)
: backup_creating_time_secs_(util::GetTimeStamp<std::chrono::seconds>()),
env_(rocksdb::Env::Default()),
config_(config),
lock_mgr_(16),
db_stats_(std::make_unique<DBStats>()) {
if (config->snapshot_dir != "") {
if (const auto s = CreateSnapshot(*config, config->snapshot_dir); !s.IsOK()) {
throw std::runtime_error(fmt::format("Failed to create snapshot: {}", s.Msg()));
nathanlo99 marked this conversation as resolved.
Show resolved Hide resolved
}
LOG(INFO) << "Starting server in read-only mode with snapshot dir: " << config->snapshot_dir;
config->db_dir = config->snapshot_dir + "/db";
}

Metadata::InitVersionCounter();
SetWriteOptions(config->rocks_db.write_options);
}
Expand Down
Loading