Initial commit

This commit is contained in:
dev
2025-02-27 21:53:53 +08:00
commit 815e55e4c0
1291 changed files with 185445 additions and 0 deletions

1
src/fdb/CMakeLists.txt Normal file
View File

@@ -0,0 +1 @@
target_add_lib(fdb common fdb_c)

283
src/fdb/FDB.cc Normal file
View File

@@ -0,0 +1,283 @@
#include "FDB.h"
#include <atomic>
#include <folly/CancellationToken.h>
#include <folly/Likely.h>
#include <folly/experimental/coro/Baton.h>
#include <folly/experimental/coro/CurrentExecutor.h>
#include <folly/experimental/coro/DetachOnCancel.h>
#include <folly/logging/xlog.h>
#include "foundationdb/fdb_c_types.h"
namespace hf3fs::kv::fdb {
#define TOU8(str) reinterpret_cast<const uint8_t *>(str.data()), str.length()
#define VIEW(name) std::string_view(reinterpret_cast<const char *>(name), name##_length)
#define SELECTOR(selector) TOU8(selector.key), selector.orEqual, selector.offset
template <>
void Result<Int64Result, int64_t>::extractValue() {
error_ = fdb_future_get_int64(future_.get(), &value_);
}
template <>
void Result<KeyResult, String>::extractValue() {
const uint8_t *data;
int data_length;
error_ = fdb_future_get_key(future_.get(), &data, &data_length);
if (error_ == 0) {
value_ = VIEW(data);
}
}
template <>
void Result<ValueResult, std::optional<String>>::extractValue() {
fdb_bool_t present = false;
const uint8_t *data;
int data_length;
error_ = fdb_future_get_value(future_.get(), &present, &data, &data_length);
if (error_ == 0 && present) {
value_ = VIEW(data);
}
}
template <>
void Result<KeyArrayResult, std::vector<String>>::extractValue() {
const FDBKey *keys;
int count = 0;
error_ = fdb_future_get_key_array(future_.get(), &keys, &count);
if (error_ == 0 && count) {
value_.reserve(count);
for (int i = 0; i < count; ++i) {
value_.emplace_back(VIEW(keys[i].key));
}
}
}
template <>
void Result<StringArrayResult, std::vector<String>>::extractValue() {
const char **strings;
int count = 0;
error_ = fdb_future_get_string_array(future_.get(), &strings, &count);
if (error_ == 0 && count) {
value_.reserve(count);
for (int i = 0; i < count; ++i) {
value_.emplace_back(strings[i]);
}
}
}
template <>
void Result<KeyValueArrayResult, std::pair<std::vector<KeyValue>, bool>>::extractValue() {
const FDBKeyValue *kv;
int count;
fdb_bool_t more = false;
error_ = fdb_future_get_keyvalue_array(future_.get(), &kv, &count, &more);
if (error_ == 0 && count) {
auto &vec = value_.first;
vec.reserve(count);
for (int i = 0; i < count; ++i) {
vec.emplace_back(VIEW(kv[i].key), VIEW(kv[i].value));
}
value_.second = more;
}
}
template <>
void Result<KeyRangeArrayResult, std::vector<KeyRange>>::extractValue() {
const FDBKeyRange *ranges;
int count;
error_ = fdb_future_get_keyrange_array(future_.get(), &ranges, &count);
if (error_ == 0 && count) {
value_.reserve(count);
for (int i = 0; i < count; ++i) {
KeyRange range;
range.beginKey = VIEW(ranges[i].begin_key);
range.endKey = VIEW(ranges[i].end_key);
value_.push_back(std::move(range));
}
}
}
template <>
void Result<EmptyResult, EmptyValue>::extractValue() {}
static void coroCallback(FDBFuture *, void *para) {
auto baton = static_cast<folly::coro::Baton *>(para);
baton->post();
}
template <class T, class V>
Task<T> Result<T, V>::toTask(FDBFuture *f) {
T result;
result.future_.reset(f);
folly::coro::Baton baton;
result.error_ = fdb_future_set_callback(f, coroCallback, &baton);
if (result.error()) {
co_return result;
}
std::atomic_bool cancel = false;
auto token = co_await folly::coro::co_current_cancellation_token;
folly::CancellationCallback cb(token, [&]() {
cancel = true;
fdb_future_cancel(f);
});
co_await baton;
if (cancel.load()) {
throw folly::OperationCancelled();
}
result.error_ = fdb_future_get_error(f);
if (result.error()) {
co_return result;
}
result.extractValue();
co_return result;
}
// Global
fdb_error_t DB::selectAPIVersion(int version) { return fdb_select_api_version(version); }
std::string_view DB::errorMsg(fdb_error_t code) { return fdb_get_error(code); }
bool DB::evaluatePredicate(int predicate_test, fdb_error_t code) { return fdb_error_predicate(predicate_test, code); }
// Network
fdb_error_t DB::setNetworkOption(FDBNetworkOption option, std::string_view value /* = {} */) {
return fdb_network_set_option(option, TOU8(value));
}
fdb_error_t DB::setupNetwork() { return fdb_setup_network(); }
fdb_error_t DB::runNetwork() { return fdb_run_network(); }
fdb_error_t DB::stopNetwork() { return fdb_stop_network(); }
// DB
fdb_error_t DB::setOption(FDBDatabaseOption option, std::string_view value) {
return fdb_database_set_option(db_.get(), option, TOU8(value));
}
Task<Int64Result> DB::rebootWorker(std::string_view address, bool check /* = false */, int duration /* = 0 */) {
co_return co_await Int64Result::toTask(fdb_database_reboot_worker(db_.get(), TOU8(address), check, duration));
}
Task<EmptyResult> DB::forceRecoveryWithDataLoss(std::string_view dcid) {
co_return co_await EmptyResult::toTask(fdb_database_force_recovery_with_data_loss(db_.get(), TOU8(dcid)));
}
Task<EmptyResult> DB::createSnapshot(std::string_view uid, std::string_view snapCommand) {
co_return co_await EmptyResult::toTask(fdb_database_create_snapshot(db_.get(), TOU8(uid), TOU8(snapCommand)));
}
Task<KeyResult> DB::purgeBlobGranules(const KeyRangeView &range, int64_t purgeVersion, fdb_bool_t force) {
co_return co_await KeyResult::toTask(
fdb_database_purge_blob_granules(db_.get(), TOU8(range.beginKey), TOU8(range.endKey), purgeVersion, force));
}
Task<EmptyResult> DB::waitPurgeGranulesComplete(std::string_view purgeKey) {
co_return co_await EmptyResult::toTask(fdb_database_wait_purge_granules_complete(db_.get(), TOU8(purgeKey)));
}
void Transaction::reset() { fdb_transaction_reset(tr_.get()); }
void Transaction::cancel() { fdb_transaction_cancel(tr_.get()); }
[[nodiscard]] fdb_error_t Transaction::setOption(FDBTransactionOption option, std::string_view value /* = {} */) {
return fdb_transaction_set_option(tr_.get(), option, TOU8(value));
}
void Transaction::setReadVersion(int64_t version) { fdb_transaction_set_read_version(tr_.get(), version); }
Task<Int64Result> Transaction::getReadVersion() {
co_return co_await Int64Result::toTask(fdb_transaction_get_read_version(tr_.get()));
}
Task<Int64Result> Transaction::getApproximateSize() {
co_return co_await Int64Result::toTask(fdb_transaction_get_approximate_size(tr_.get()));
}
Task<KeyResult> Transaction::getVersionstamp() {
co_return co_await KeyResult::toTask(fdb_transaction_get_versionstamp(tr_.get()));
}
Task<ValueResult> Transaction::get(std::string_view key, fdb_bool_t snapshot /* = false */) {
co_return co_await ValueResult::toTask(fdb_transaction_get(tr_.get(), TOU8(key), snapshot));
}
Task<KeyResult> Transaction::getKey(const KeySelector &selector, fdb_bool_t snapshot /* = false */) {
co_return co_await KeyResult::toTask(fdb_transaction_get_key(tr_.get(), SELECTOR(selector), snapshot));
}
Task<StringArrayResult> Transaction::getAddressesForKey(std::string_view key) {
co_return co_await StringArrayResult::toTask(fdb_transaction_get_addresses_for_key(tr_.get(), TOU8(key)));
}
Task<KeyValueArrayResult> Transaction::getRange(const KeySelector &begin,
const KeySelector &end,
GetRangeLimits limits /* = GetRangeLimits() */,
int iteration /* = 0 */,
bool snapshot /* = false */,
bool reverse /* = false */,
FDBStreamingMode streamingMode /* = FDB_STREAMING_MODE_SERIAL */) {
co_return co_await KeyValueArrayResult::toTask(fdb_transaction_get_range(tr_.get(),
SELECTOR(begin),
SELECTOR(end),
limits.rows.value_or(-1),
limits.bytes.value_or(-1),
streamingMode,
iteration,
snapshot,
reverse));
}
Task<Int64Result> Transaction::getEstimatedRangeSizeBytes(const KeyRangeView &range) {
co_return co_await Int64Result::toTask(
fdb_transaction_get_estimated_range_size_bytes(tr_.get(), TOU8(range.beginKey), TOU8(range.endKey)));
}
Task<KeyArrayResult> Transaction::getRangeSplitPoints(const KeyRangeView &range, int64_t chunkSize) {
co_return co_await KeyArrayResult::toTask(
fdb_transaction_get_range_split_points(tr_.get(), TOU8(range.beginKey), TOU8(range.endKey), chunkSize));
}
Task<EmptyResult> Transaction::watch(std::string_view key) {
co_return co_await EmptyResult::toTask(fdb_transaction_watch(tr_.get(), TOU8(key)));
}
Task<EmptyResult> Transaction::commit() {
if (UNLIKELY(readonly_)) {
// Prevent tools like admin_cli or fsck from mistakenly modifying data
XLOGF(CRITICAL, "disallow call commit on a read-only FDBContext!!!");
EmptyResult result;
result.error_ = 1000; /* operation failed */
co_return result;
}
co_return co_await EmptyResult::toTask(fdb_transaction_commit(tr_.get()));
}
Task<EmptyResult> Transaction::onError(fdb_error_t err) {
co_return co_await EmptyResult::toTask(fdb_transaction_on_error(tr_.get(), err));
}
void Transaction::clear(std::string_view key) { return fdb_transaction_clear(tr_.get(), TOU8(key)); }
void Transaction::clearRange(const KeyRangeView &range) {
fdb_transaction_clear_range(tr_.get(), TOU8(range.beginKey), TOU8(range.endKey));
}
void Transaction::set(std::string_view key, std::string_view value) {
fdb_transaction_set(tr_.get(), TOU8(key), TOU8(value));
}
void Transaction::atomicOp(std::string_view key, std::string_view param, FDBMutationType operationType) {
return fdb_transaction_atomic_op(tr_.get(), TOU8(key), TOU8(param), operationType);
}
[[nodiscard]] fdb_error_t Transaction::getCommittedVersion(int64_t *outVersion) {
return fdb_transaction_get_committed_version(tr_.get(), outVersion);
}
fdb_error_t Transaction::addConflictRange(const KeyRangeView &range, FDBConflictRangeType type) {
return fdb_transaction_add_conflict_range(tr_.get(), TOU8(range.beginKey), TOU8(range.endKey), type);
}
} // namespace hf3fs::kv::fdb

206
src/fdb/FDB.h Normal file
View File

@@ -0,0 +1,206 @@
#pragma once
#include <folly/Utility.h>
#include <folly/experimental/coro/FutureUtil.h>
#include <folly/experimental/coro/Promise.h>
#include <folly/experimental/coro/Task.h>
#include "common/kv/ITransaction.h"
#include "common/utils/String.h"
#include "foundationdb/fdb_c_options.g.h"
#include "foundationdb/fdb_c_types.h"
#define FDB_API_VERSION 710
#include <foundationdb/fdb_c.h>
namespace hf3fs::kv {
class FDBKVEngine;
namespace fdb {
using folly::coro::Task;
using KeyValue = IReadOnlyTransaction::KeyValue;
// Structs for returned values.
struct KeyRange {
String beginKey;
String endKey;
};
// Structs for parameters.
struct KeyRangeView {
std::string_view beginKey;
std::string_view endKey;
};
struct KeySelector {
std::string_view key; // Find the last item less than key
bool orEqual = true; // (or equal to key, if this is true)
int offset = 0; // and then move forward this many items (or backward if negative)
KeySelector() = default;
KeySelector(std::string_view key, bool orEqual, int offset)
: key(std::move(key)),
orEqual(orEqual),
offset(offset) {}
};
struct GetRangeLimits {
std::optional<uint32_t> rows;
std::optional<uint32_t> bytes;
explicit GetRangeLimits(std::optional<uint32_t> r = {}, std::optional<uint32_t> b = {})
: rows(r),
bytes(b) {}
};
template <class T, class V>
class Result {
public:
fdb_error_t error() const { return error_; };
V &value() { return value_; }
const V &value() const { return value_; }
protected:
friend class DB;
friend class Transaction;
static Task<T> toTask(FDBFuture *f);
void extractValue();
protected:
struct FDBFutureDeleter {
constexpr FDBFutureDeleter() noexcept = default;
void operator()(FDBFuture *future) const { future ? fdb_future_destroy(future) : void(); }
};
std::unique_ptr<FDBFuture, FDBFutureDeleter> future_;
fdb_error_t error_ = 0;
V value_{};
};
class Int64Result : public Result<Int64Result, int64_t> {};
class KeyResult : public Result<KeyResult, String> {};
class ValueResult : public Result<ValueResult, std::optional<String>> {};
class KeyArrayResult : public Result<KeyArrayResult, std::vector<String>> {};
class StringArrayResult : public Result<StringArrayResult, std::vector<String>> {};
class KeyValueArrayResult : public Result<KeyValueArrayResult, std::pair<std::vector<KeyValue>, bool>> {};
class KeyRangeArrayResult : public Result<KeyRangeArrayResult, std::vector<KeyRange>> {};
struct EmptyValue {};
class EmptyResult : public Result<EmptyResult, EmptyValue> {};
class DB {
public:
static fdb_error_t selectAPIVersion(int version);
static std::string_view errorMsg(fdb_error_t code);
static bool evaluatePredicate(int predicate_test, fdb_error_t code);
// network
static fdb_error_t setNetworkOption(FDBNetworkOption option, std::string_view value = {});
static fdb_error_t setupNetwork();
static fdb_error_t runNetwork();
static fdb_error_t stopNetwork();
public:
DB() = default;
explicit DB(const String &clusterFilePath, bool readonly)
: readonly_(readonly) {
auto path = clusterFilePath.empty() ? nullptr : clusterFilePath.c_str();
FDBDatabase *db;
error_ = fdb_create_database(path, &db);
if (error_ == 0) {
db_.reset(db);
}
}
fdb_error_t error() const { return error_; }
explicit operator bool() const { return error() == 0; }
operator FDBDatabase *() const { return db_.get(); }
fdb_error_t setOption(FDBDatabaseOption option, std::string_view value = {});
Task<Int64Result> rebootWorker(std::string_view address, bool check = false, int duration = 0);
Task<EmptyResult> forceRecoveryWithDataLoss(std::string_view dcid);
Task<EmptyResult> createSnapshot(std::string_view uid, std::string_view snapCommand);
Task<KeyResult> purgeBlobGranules(const KeyRangeView &range, int64_t purgeVersion, fdb_bool_t force);
Task<EmptyResult> waitPurgeGranulesComplete(std::string_view purgeKey);
bool readonly() const { return readonly_; }
private:
friend class hf3fs::kv::FDBKVEngine;
struct FDBDatabaseDeleter {
constexpr FDBDatabaseDeleter() noexcept = default;
void operator()(FDBDatabase *db) const { db ? fdb_database_destroy(db) : void(); }
};
std::unique_ptr<FDBDatabase, FDBDatabaseDeleter> db_;
bool readonly_ = false;
fdb_error_t error_ = 1;
};
class Transaction final {
public:
Transaction(DB &db)
: readonly_(db.readonly()) {
FDBTransaction *tr;
error_ = fdb_database_create_transaction(db, &tr);
if (error_ == 0) {
tr_.reset(tr);
}
}
fdb_error_t error() const { return error_; }
explicit operator bool() const { return error_ == 0 && tr_; }
fdb_error_t setOption(FDBTransactionOption option, std::string_view value = {});
// Read transaction
void setReadVersion(int64_t version);
Task<Int64Result> getReadVersion();
Task<ValueResult> get(std::string_view key, fdb_bool_t snapshot = false);
Task<KeyResult> getKey(const KeySelector &selector, fdb_bool_t snapshot = false);
Task<EmptyResult> watch(std::string_view key);
Task<StringArrayResult> getAddressesForKey(std::string_view key);
Task<KeyValueArrayResult> getRange(const KeySelector &begin,
const KeySelector &end,
GetRangeLimits limits = GetRangeLimits(),
int iteration = 0,
bool snapshot = false,
bool reverse = false,
FDBStreamingMode streamingMode = FDB_STREAMING_MODE_SERIAL);
Task<Int64Result> getEstimatedRangeSizeBytes(const KeyRangeView &range);
Task<KeyArrayResult> getRangeSplitPoints(const KeyRangeView &range, int64_t chunkSize);
Task<EmptyResult> onError(fdb_error_t err);
void cancel();
void reset();
// Write transaction
fdb_error_t addConflictRange(const KeyRangeView &range, FDBConflictRangeType type);
void atomicOp(std::string_view key, std::string_view param, FDBMutationType operationType);
void set(std::string_view key, std::string_view value);
void clear(std::string_view key);
void clearRange(const KeyRangeView &range);
Task<EmptyResult> commit();
fdb_error_t getCommittedVersion(int64_t *outVersion);
Task<Int64Result> getApproximateSize();
Task<KeyResult> getVersionstamp();
private:
struct FDBTransactionDeleter {
constexpr FDBTransactionDeleter() noexcept = default;
void operator()(FDBTransaction *tr) const { tr ? fdb_transaction_destroy(tr) : void(); }
};
std::unique_ptr<FDBTransaction, FDBTransactionDeleter> tr_;
bool readonly_ = false;
fdb_error_t error_ = 1;
};
} // namespace fdb
} // namespace hf3fs::kv

22
src/fdb/FDBConfig.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <map>
#include <string>
#include "common/utils/ConfigBase.h"
namespace hf3fs::kv::fdb {
struct FDBConfig : public ConfigBase<FDBConfig> {
CONFIG_ITEM(clusterFile, "");
CONFIG_ITEM(enableMultipleClient, false);
CONFIG_ITEM(externalClientDir, "");
CONFIG_ITEM(externalClientPath, "");
CONFIG_ITEM(multipleClientThreadNum, 4L, ConfigCheckers::checkPositive);
CONFIG_ITEM(trace_file, "");
CONFIG_ITEM(trace_format, "json");
CONFIG_ITEM(casual_read_risky, false);
CONFIG_ITEM(default_backoff, 0);
CONFIG_ITEM(readonly, false);
};
} // namespace hf3fs::kv::fdb

104
src/fdb/FDBContext.cc Normal file
View File

@@ -0,0 +1,104 @@
#include "FDBContext.h"
#include <fmt/core.h>
#include <folly/logging/xlog.h>
#include <memory>
#include "common/utils/String.h"
#include "foundationdb/fdb_c_options.g.h"
namespace hf3fs::kv::fdb {
#define CHECK_FDB_COND(condition, ...) XLOGF_IF(FATAL, !(condition), "FoundationDB error. " __VA_ARGS__)
#define CHECK_FDB_ERR(err, msg, ...) \
XLOGF_IF(FATAL, (err) != 0, "FoundationDB error: {}. " msg, DB::errorMsg(err), #__VA_ARGS__)
#define CHECK_FDB_OP(cmd, ...) \
do { \
auto err = (cmd); \
CHECK_FDB_ERR(err, #__VA_ARGS__); \
} while (false)
std::shared_ptr<FDBContext> FDBContext::create(const FDBConfig &config) {
auto context = std::shared_ptr<FDBContext>(new FDBContext(config));
return context;
}
FDBContext::FDBContext(const FDBConfig &config)
: config_(config) {
CHECK_FDB_OP(DB::selectAPIVersion(FDB_API_VERSION), "Failed to set API Version = {}.", FDB_API_VERSION);
if (config_.enableMultipleClient()) {
XLOGF(INFO, "FoundationDB: enable externalClientDir");
const String &externalDir = config_.externalClientDir();
const String &externalPath = config_.externalClientPath();
CHECK_FDB_COND((!externalDir.empty() || !externalPath.empty()),
"'externalClientDir' or 'externalClientPath' should not be empty when 'enableMultipleClient'.");
// CHECK_FDB_OP(DB::setNetworkOption(FDB_NET_OPTION_DISABLE_LOCAL_CLIENT), "Failed to disable local client.");
if (!externalPath.empty()) {
CHECK_FDB_OP(DB::setNetworkOption(FDB_NET_OPTION_EXTERNAL_CLIENT_LIBRARY, externalPath),
"Failed to set FDB_NET_OPTION_EXTERNAL_CLIENT_LIBRARY = {}.",
externalPath);
} else {
CHECK_FDB_OP(DB::setNetworkOption(FDB_NET_OPTION_EXTERNAL_CLIENT_DIRECTORY, externalDir),
"Failed to set FDB_NET_OPTION_EXTERNAL_CLIENT_DIRECTORY = {}.",
externalDir);
}
auto threadCnt = config_.multipleClientThreadNum();
auto threadCntOption = std::string_view{reinterpret_cast<const char *>(&threadCnt), sizeof(threadCnt)};
CHECK_FDB_OP(DB::setNetworkOption(FDB_NET_OPTION_CLIENT_THREADS_PER_VERSION, threadCntOption),
"Failed to set FDB_NET_OPTION_CLIENT_THREADS_PER_VERSION = {}.",
threadCnt);
CHECK_FDB_OP(DB::setNetworkOption(FDB_NET_OPTION_CALLBACKS_ON_EXTERNAL_THREADS),
"Failed to set FDB_NET_OPTION_CALLBACKS_ON_EXTERNAL_THREADS.");
}
if (!config_.trace_file().empty()) {
CHECK_FDB_OP(DB::setNetworkOption(FDB_NET_OPTION_TRACE_ENABLE, config_.trace_file()),
"Failed to set FDB_NET_OPTION_TRACE_ENABLE to {}",
config_.trace_file());
CHECK_FDB_OP(DB::setNetworkOption(FDB_NET_OPTION_TRACE_FORMAT, config_.trace_format()),
"Failed to set FDB_NET_OPTION_TRACE_FORMAT to {}",
config_.trace_format());
}
if (config_.default_backoff()) {
CHECK_FDB_OP(
DB::setNetworkOption(FDB_NET_OPTION_KNOB, fmt::format("DEFAULT_BACKOFF={}", config_.default_backoff())),
"Failed to set DEFAULT_BACKOFF to {}",
config_.default_backoff());
}
CHECK_FDB_OP(DB::setupNetwork(), "Failed to setup network thread.");
networkThread = std::thread([] {
pthread_setname_np(pthread_self(), "fdb_net");
DB::runNetwork();
});
}
FDBContext::~FDBContext() {
if (networkThread.joinable()) {
CHECK_FDB_OP(DB::stopNetwork(), "Failed to stop network thread.");
networkThread.join();
}
}
DB FDBContext::getDB() const {
DB db(config_.clusterFile(), config_.readonly());
CHECK_FDB_ERR(db.error(), "Failed to get fdb::DB instance.");
if (config_.casual_read_risky()) {
CHECK_FDB_ERR(db.setOption(FDB_DB_OPTION_TRANSACTION_CAUSAL_READ_RISKY),
"Failed to set FDB_DB_OPTION_TRANSACTION_CAUSAL_READ_RISKY");
}
return db;
}
#undef CHECK_FDB_OP
#undef CHECK_FDB_COND
} // namespace hf3fs::kv::fdb

27
src/fdb/FDBContext.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <memory>
#include "FDB.h"
#include "FDBConfig.h"
#include "common/utils/ConfigBase.h"
namespace hf3fs::kv::fdb {
// FDBContext encapsulates the setup and cleanup of FoundationDB.
// It is designed to be used in the main function.
class FDBContext {
public:
static std::shared_ptr<FDBContext> create(const FDBConfig &config);
~FDBContext();
DB getDB() const;
int64_t maxDbCount() const { return config_.enableMultipleClient() ? config_.multipleClientThreadNum() : 1; }
private:
explicit FDBContext(const FDBConfig &config);
FDBConfig config_; // don't support hot update config
std::thread networkThread;
};
} // namespace hf3fs::kv::fdb

45
src/fdb/FDBKVEngine.h Normal file
View File

@@ -0,0 +1,45 @@
#pragma once
#include <folly/Likely.h>
#include <gtest/gtest_prod.h>
#include "FDB.h"
#include "FDBTransaction.h"
#include "common/kv/IKVEngine.h"
namespace hf3fs {
namespace meta {
template <typename KV>
class MetaTestBase;
}
namespace kv {
class FDBKVEngine : public IKVEngine {
public:
FDBKVEngine(fdb::DB db)
: db_(std::move(db)) {}
std::unique_ptr<IReadOnlyTransaction> createReadonlyTransaction() override { return createReadWriteTransaction(); }
std::unique_ptr<IReadWriteTransaction> createReadWriteTransaction() override {
fdb::Transaction tr(db_);
if (UNLIKELY(tr.error())) {
return nullptr;
}
return std::make_unique<FDBTransaction>(std::move(tr));
}
private:
template <typename KV>
friend class hf3fs::meta::MetaTestBase;
FRIEND_TEST(TestFDBTransaction, Readonly);
void setReadonly(bool rdonly) { db_.readonly_ = rdonly; }
fdb::DB db_;
};
} // namespace kv
} // namespace hf3fs

134
src/fdb/FDBRetryStrategy.h Normal file
View File

@@ -0,0 +1,134 @@
#pragma once
#include <algorithm>
#include <chrono>
#include <folly/Likely.h>
#include <folly/Random.h>
#include <folly/experimental/coro/Sleep.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
#include <folly/logging/xlog.h>
#include <foundationdb/fdb_c_types.h>
#include <string_view>
#include <utility>
#include "common/kv/ITransaction.h"
#include "common/utils/Coroutine.h"
#include "common/utils/Duration.h"
#include "common/utils/Result.h"
#include "common/utils/Status.h"
#include "fdb/FDBTransaction.h"
namespace hf3fs::kv {
class FDBRetryStrategy {
public:
static constexpr Duration kMinBackoff = 10_ms; // same with FDB, 0.01s
struct Config {
Duration maxBackoff = 1_s;
size_t maxRetryCount = 10;
bool retryMaybeCommitted = true;
};
FDBRetryStrategy()
: FDBRetryStrategy(Config()) {}
FDBRetryStrategy(Config config)
: config_(config),
backoff_(kMinBackoff),
retry_(0) {}
template <typename Txn>
Result<Void> init(Txn *txn) {
retry_ = 0;
backoff_ = kMinBackoff;
auto *fdbTransaction = dynamic_cast<FDBTransaction *>(txn);
if (fdbTransaction) {
uint64_t value = std::chrono::duration_cast<std::chrono::milliseconds>(config_.maxBackoff).count();
auto result = fdbTransaction->setOption(FDBTransactionOption::FDB_TR_OPTION_MAX_RETRY_DELAY,
std::string_view((char *)&value, sizeof(value)));
if (result.hasError()) {
XLOGF(ERR, "Failed to set option on FDBTransaction, {}", result.error().describe());
RETURN_ERROR(result);
}
}
return Void{};
}
template <typename Txn>
CoTryTask<void> onError(Txn *txn, Status error) {
if (retry_ >= config_.maxRetryCount) {
XLOGF(ERR, "Transaction failed after retry {} times, error {}", retry_, error.describe());
co_return makeError(std::move(error));
}
if (!TransactionHelper::isTransactionError(error)) {
XLOGF(DBG, "Not transaction error {}", error);
co_return makeError(std::move(error));
}
XLOGF(DBG, "Transaction error {}", error);
SCOPE_EXIT {
// update retry and backoff before exit.
backoff_ = std::min(config_.maxBackoff, Duration(backoff_ * 2));
retry_++;
};
auto *fdbTransaction = dynamic_cast<FDBTransaction *>(txn);
if (fdbTransaction) {
co_return co_await fdbBackoff(fdbTransaction, std::move(error));
} else {
co_return co_await defaultBackoff(txn, std::move(error));
}
}
CoTryTask<void> fdbBackoff(FDBTransaction *txn, Status error) {
auto errcode = txn->errcode();
if (UNLIKELY(!errcode)) {
XLOGF_IF(CRITICAL,
error.code() != TransactionCode::kTooOld,
"Failed to get FDB errcode, error {}, stacktrace {}",
error,
folly::symbolizer::getStackTraceStr());
co_return co_await defaultBackoff(txn, std::move(error));
}
FDBErrorPredicate predict =
config_.retryMaybeCommitted ? FDB_ERROR_PREDICATE_RETRYABLE : FDB_ERROR_PREDICATE_RETRYABLE_NOT_COMMITTED;
if (!fdb_error_predicate(predict, errcode)) {
XLOGF(ERR, "Transaction error not retryable: {}, errcode {}", error, errcode);
co_return makeError(std::move(error));
}
XLOGF(DBG, "FDBRetryStrategy backoff by FoundationDB");
auto ok = co_await txn->onError(errcode);
if (!ok) {
co_return makeError(std::move(error));
}
co_return Void{};
}
template <typename Txn>
CoTryTask<void> defaultBackoff(Txn *txn, Status error) {
// fallback to our backoff implementation
if (!TransactionHelper::isRetryable(error, config_.retryMaybeCommitted)) {
XLOGF(ERR, "Transaction error not retryable: {}", error);
co_return makeError(std::move(error));
}
XLOGF(WARN, "Transaction retryable error: {}", error);
XLOGF(DBG, "FDBRetryStrategy backoff transaction {}ms", backoff_.count());
txn->reset();
auto duration = Duration(backoff_ / 100 * folly::Random::rand32(80, 120));
co_await folly::coro::sleep(duration.asUs());
co_return Void{};
}
private:
Config config_;
Duration backoff_;
size_t retry_;
};
} // namespace hf3fs::kv

438
src/fdb/FDBTransaction.cc Normal file
View File

@@ -0,0 +1,438 @@
#include "FDBTransaction.h"
#include <atomic>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <folly/Likely.h>
#include <folly/Portability.h>
#include <folly/Random.h>
#include <folly/experimental/coro/Sleep.h>
#include <folly/futures/detail/Types.h>
#include <folly/lang/Bits.h>
#include <folly/logging/xlog.h>
#include <foundationdb/fdb_c_types.h>
#include <string>
#include <string_view>
#include <vector>
#include "common/monitor/Recorder.h"
#include "common/utils/Coroutine.h"
#include "common/utils/FaultInjection.h"
#include "common/utils/MagicEnum.hpp"
#include "common/utils/RandomUtils.h"
#include "common/utils/Result.h"
#include "common/utils/SerDeser.h"
#include "common/utils/Status.h"
#include "common/utils/UtcTime.h"
#include "fdb/FDB.h"
#include "foundationdb/fdb_c_options.g.h"
#define CHECK_FDB_ERRCODE(op, code) \
do { \
fdb_error_t errcode = code; \
if (UNLIKELY(errcode != 0)) { \
errcode_ = errcode; \
if (UNLIKELY(errcode == error_code_not_committed)) { \
XLOGF(WARN, \
"FDBTransaction {} failed, err: {}, msg: {}.", \
magic_enum::enum_name(op), \
errcode, \
fdb::DB::errorMsg(errcode)); \
} else { \
XLOGF(ERR, \
"FDBTransaction {} failed, err: {}, msg: {}.", \
magic_enum::enum_name(op), \
errcode, \
fdb::DB::errorMsg(errcode)); \
} \
co_return makeFDBError(errcode, op); \
} \
} while (0)
#define FAULT_INJECTION_ON_COMMIT(db_name, injectMaybeCommitted) \
do { \
if (FAULT_INJECTION()) { \
auto delayMs = folly::Random::rand32(20, 100); \
errcode_ = hf3fs::RandomUtils::randomSelect(std::vector<fdb_error_t>{error_code_not_committed, \
error_code_commit_unknown_result, \
error_code_transaction_too_old, \
error_code_tag_throttled}); \
XLOGF(WARN, "Inject fault on commit, errCode: {}, delayMs: {}.", errcode_.load(), delayMs); \
if (errcode_ == error_code_commit_unknown_result && folly::Random::oneIn(2)) { \
injectMaybeCommitted = true; \
XLOGF(WARN, "Inject maybeCommitted on commit, commit transaction in " #db_name "."); \
} else { \
co_return makeError(convertError(errcode_, op), "Inject fault on commit."); \
} \
} \
} while (0)
#define FAULT_INJECTION_ON_GET(op_name) \
do { \
if (FAULT_INJECTION()) { \
auto delayMs = folly::Random::rand32(20, 100); \
errcode_ = hf3fs::RandomUtils::randomSelect( \
std::vector<fdb_error_t>{error_code_transaction_too_old, error_code_tag_throttled}); \
XLOGF(WARN, "Inject fault on " #op_name ", errCode: {}, delayMs: {}.", errcode_.load(), delayMs); \
co_await folly::coro::sleep(std::chrono::milliseconds(delayMs)); \
co_return makeError(convertError(errcode_, op), "Inject fault on " #op_name); \
} \
} while (0)
namespace hf3fs::kv {
namespace {
enum class Op {
Get = 0,
SnapshotGet,
GetRange,
SnapshotGetRange,
AddReadConflict,
Commit,
Cancel,
Set,
SetVersionstampedKey,
SetVersionstampedValue,
Clear,
ClearRange,
GetReadVersion,
};
template <Op op>
struct OpRecorder {
static constexpr bool recordFailed = false;
static constexpr bool recordLatency = false;
static monitor::CountRecorder totalRecorder;
};
#define OP_RECORDER_BASIC(op, name) \
template <> \
struct OpRecorder<op> { \
static constexpr bool recordFailed = false; \
static constexpr bool recordLatency = false; \
static inline monitor::CountRecorder totalRecorder{"fdb_total_count_" name}; \
}
#define OP_RECORDER_FAILED(op, name) \
template <> \
struct OpRecorder<op> { \
static constexpr bool recordFailed = true; \
static constexpr bool recordLatency = false; \
static inline monitor::CountRecorder totalRecorder{"fdb_total_count_" name}; \
static inline monitor::CountRecorder failedRecorder{"fdb_failed_count_" name}; \
}
#define OP_RECORDER_LATENCY(op, name) \
template <> \
struct OpRecorder<op> { \
static constexpr bool recordFailed = true; \
static constexpr bool recordLatency = true; \
static inline monitor::CountRecorder totalRecorder{"fdb_total_count_" name}; \
static inline monitor::CountRecorder failedRecorder{"fdb_failed_count_" name}; \
static inline monitor::LatencyRecorder latencyRecorder{"fdb_latency_" name}; \
}
OP_RECORDER_BASIC(Op::Set, "set");
OP_RECORDER_BASIC(Op::SetVersionstampedKey, "set_versionstamped_key");
OP_RECORDER_BASIC(Op::SetVersionstampedValue, "set_versionstamped_value");
OP_RECORDER_BASIC(Op::Cancel, "cancel");
OP_RECORDER_BASIC(Op::Clear, "clear");
OP_RECORDER_BASIC(Op::ClearRange, "clear_range");
OP_RECORDER_FAILED(Op::AddReadConflict, "add_read_conflict");
OP_RECORDER_LATENCY(Op::Get, "get");
OP_RECORDER_LATENCY(Op::SnapshotGet, "snapshot_get");
OP_RECORDER_LATENCY(Op::GetRange, "get_range");
OP_RECORDER_LATENCY(Op::SnapshotGetRange, "snapshot_get_range");
OP_RECORDER_LATENCY(Op::Commit, "commit");
OP_RECORDER_LATENCY(Op::GetReadVersion, "get_read_version");
monitor::CountRecorder retryConflict("fdb.retry_conflict");
monitor::CountRecorder retryOther("fdb.retry_other");
monitor::LatencyRecorder retryBackoff("fdb.retry_backoff");
#define ERROR(name, code, desp) constexpr int error_code_##name = code;
FOLLY_PUSH_WARNING
FOLLY_GNU_DISABLE_WARNING("-Wpedantic")
#include "error_definitions.h"
FOLLY_POP_WARNING
#undef ERROR
uint32_t convertError(fdb_error_t code, Op op) {
// see: Transaction::onError (foundationdb/fdbclient/NativeAPI.actor.cpp:7036)
// see: fdb_error_predicate (foundationdb/bindings/c/fdb_c.cpp:71)
assert(code != 0);
switch (code) {
case error_code_not_committed:
return TransactionCode::kConflict;
case error_code_commit_unknown_result:
return TransactionCode::kMaybeCommitted;
case error_code_proxy_memory_limit_exceeded:
return TransactionCode::kResourceConstrained;
case error_code_process_behind:
return TransactionCode::kProcessBehind;
case error_code_batch_transaction_throttled:
case error_code_tag_throttled:
return TransactionCode::kThrottled;
case error_code_transaction_too_old:
return TransactionCode::kTooOld;
case error_code_future_version:
return TransactionCode::kFutureVersion;
case error_code_connection_failed:
case error_code_blocked_from_network_thread:
return TransactionCode::kNetworkError;
case error_code_transaction_cancelled:
return TransactionCode::kCanceled;
case error_code_database_locked:
case error_code_unknown_tenant:
return TransactionCode::kRetryable;
case error_code_cluster_version_changed:
return op == Op::Commit ? TransactionCode::kMaybeCommitted : TransactionCode::kRetryable;
}
return TransactionCode::kFailed;
}
inline folly::Unexpected<Status> makeFDBError(fdb_error_t code, Op op) {
auto msg = fmt::format("FDB error: {}, msg: {}", code, fdb::DB::errorMsg(code));
return makeError(convertError(code, op), std::move(msg));
}
template <Op op>
struct OpWrapper {
using Clock = std::chrono::steady_clock;
template <typename F>
static std::invoke_result_t<F, Op> run(F &&f) {
OpRecorder<op>::totalRecorder.addSample(1);
auto start = Clock::now();
auto result = co_await f(op);
auto duration = Clock::now() - start;
if (UNLIKELY(result.hasError())) {
if constexpr (OpRecorder<op>::recordFailed) {
auto tagset = monitor::TagSet::create("statusCode", String(StatusCode::toString(result.error().code())));
OpRecorder<op>::failedRecorder.addSample(1, tagset);
}
}
if constexpr (OpRecorder<op>::recordLatency) {
OpRecorder<op>::latencyRecorder.addSample(duration);
}
co_return result;
}
};
std::string appendOffset(std::string_view str, uint32_t offset) {
String buf;
buf.reserve(str.size() + sizeof(offset));
Serializer ser{buf};
ser.putRaw(str.data(), str.size());
ser.put(folly::Endian::little32(offset));
assert(buf.size() == str.size() + 4);
return buf;
}
} // namespace
CoTryTask<std::optional<String>> FDBTransaction::get(std::string_view key) {
co_return co_await OpWrapper<Op::Get>::run([&](Op op) -> CoTryTask<std::optional<String>> {
FAULT_INJECTION_ON_GET(get);
auto result = co_await tr_.get(key);
CHECK_FDB_ERRCODE(op, result.error());
co_return std::move(result.value());
});
}
CoTryTask<FDBTransaction::GetRangeResult> FDBTransaction::getRange(const KeySelector &begin,
const KeySelector &end,
int32_t limit) {
co_return co_await OpWrapper<Op::GetRange>::run([&](Op op) -> CoTryTask<GetRangeResult> {
FAULT_INJECTION_ON_GET(getRange);
fdb::KeySelector innerBegin(begin.key, !begin.inclusive, 1);
fdb::KeySelector innerEnd(end.key, end.inclusive, 1);
fdb::GetRangeLimits limits(limit);
auto result = co_await tr_.getRange(innerBegin, innerEnd, limits, /* iteration */ 0, /* snapshot */ false);
CHECK_FDB_ERRCODE(op, result.error());
co_return GetRangeResult(std::move(result.value().first), result.value().second);
});
}
CoTryTask<FDBTransaction::GetRangeResult> FDBTransaction::snapshotGetRange(const KeySelector &begin,
const KeySelector &end,
int32_t limit) {
co_return co_await OpWrapper<Op::SnapshotGetRange>::run([&](Op op) -> CoTryTask<GetRangeResult> {
FAULT_INJECTION_ON_GET(snapshotGetRange);
fdb::KeySelector innerBegin(begin.key, !begin.inclusive, 1);
fdb::KeySelector innerEnd(end.key, end.inclusive, 1);
fdb::GetRangeLimits limits(limit);
auto result = co_await tr_.getRange(innerBegin, innerEnd, limits, /* iteration */ 0, /* snapshot */ true);
CHECK_FDB_ERRCODE(op, result.error());
co_return GetRangeResult(std::move(result.value().first), result.value().second);
});
}
CoTryTask<void> FDBTransaction::cancel() {
co_return co_await OpWrapper<Op::Cancel>::run([&](Op) -> CoTryTask<void> {
tr_.cancel();
co_return Void{};
});
}
CoTryTask<std::optional<String>> FDBTransaction::snapshotGet(std::string_view key) {
co_return co_await OpWrapper<Op::SnapshotGet>::run([&](Op op) -> CoTryTask<std::optional<String>> {
FAULT_INJECTION_ON_GET(snapshotGet);
auto result = co_await tr_.get(key, /* snapshot = */ true);
CHECK_FDB_ERRCODE(op, result.error());
co_return std::move(result.value());
});
}
CoTryTask<void> FDBTransaction::addReadConflict(std::string_view key) {
co_return co_await OpWrapper<Op::AddReadConflict>::run([&](Op op) -> CoTryTask<void> {
String endKey = TransactionHelper::keyAfter(key);
fdb::KeyRangeView range;
range.beginKey = key;
range.endKey = endKey;
auto result = tr_.addConflictRange(range, FDBConflictRangeType::FDB_CONFLICT_RANGE_TYPE_READ);
CHECK_FDB_ERRCODE(op, result);
co_return Void{};
});
}
CoTryTask<void> FDBTransaction::addReadConflictRange(std::string_view begin, std::string_view end) {
co_return co_await OpWrapper<Op::AddReadConflict>::run([&](Op op) -> CoTryTask<void> {
fdb::KeyRangeView range;
range.beginKey = begin;
range.endKey = end;
auto result = tr_.addConflictRange(range, FDBConflictRangeType::FDB_CONFLICT_RANGE_TYPE_READ);
CHECK_FDB_ERRCODE(op, result);
co_return Void{};
});
}
CoTryTask<void> FDBTransaction::set(std::string_view key, std::string_view value) {
co_return co_await OpWrapper<Op::Set>::run([&](Op) -> CoTryTask<void> {
tr_.set(key, value);
co_return Void{};
});
}
CoTryTask<void> FDBTransaction::setVersionstampedKey(std::string_view key, uint32_t offset, std::string_view value) {
if (offset + 10 > key.size()) {
co_return makeError(StatusCode::kInvalidArg,
fmt::format("setVersionstampedKey: {} + 10 > key.size {}", offset, key.size()));
}
co_return co_await OpWrapper<Op::SetVersionstampedKey>::run([&](Op) -> CoTryTask<void> {
tr_.atomicOp(appendOffset(key, offset), value, FDB_MUTATION_TYPE_SET_VERSIONSTAMPED_KEY);
co_return Void{};
});
}
CoTryTask<void> FDBTransaction::setVersionstampedValue(std::string_view key, std::string_view value, uint32_t offset) {
if (offset + 10 > value.size()) {
co_return makeError(StatusCode::kInvalidArg,
fmt::format("setVersionstampedValue: {} + 10 > value.size {}", offset, value.size()));
}
co_return co_await OpWrapper<Op::SetVersionstampedValue>::run([&](Op) -> CoTryTask<void> {
tr_.atomicOp(key, appendOffset(value, offset), FDB_MUTATION_TYPE_SET_VERSIONSTAMPED_VALUE);
co_return Void{};
});
}
CoTryTask<void> FDBTransaction::clear(std::string_view key) {
co_return co_await OpWrapper<Op::Clear>::run([&](Op) -> CoTryTask<void> {
tr_.clear(key);
co_return Void{};
});
}
CoTryTask<void> FDBTransaction::clearRange(std::string_view begin, std::string_view end) {
co_return co_await OpWrapper<Op::ClearRange>::run([&](Op) -> CoTryTask<void> {
fdb::KeyRangeView range;
range.beginKey = begin;
range.endKey = end;
tr_.clearRange(range);
co_return Void{};
});
}
CoTryTask<void> FDBTransaction::commit() {
co_return co_await OpWrapper<Op::Commit>::run([&](Op op) -> CoTryTask<void> {
bool injectMaybeCommitted = false;
FAULT_INJECTION_ON_COMMIT(FoundationDB, injectMaybeCommitted);
auto result = co_await tr_.commit();
if (UNLIKELY(injectMaybeCommitted)) {
errcode_ = error_code_commit_unknown_result;
XLOGF(WARN, "Inject maybeCommitted error after commit, FoundationDB commit result is {}.", result.error());
co_return makeError(convertError(errcode_, op), "Fault injection commit unknown result.");
}
CHECK_FDB_ERRCODE(op, result.error());
co_return Void{};
});
}
void FDBTransaction::reset() {
errcode_ = 0;
tr_.reset();
}
Result<Void> FDBTransaction::setOption(FDBTransactionOption option, std::string_view value) {
fdb_error_t errcode = tr_.setOption(option, value);
if (errcode != 0) {
XLOGF(ERR, "FDBTransaction failed to set option {}, errcode {}", magic_enum::enum_name(option), errcode);
return makeError(StatusCode::kInvalidArg);
}
return Void{};
}
CoTask<bool> FDBTransaction::onError(fdb_error_t errcode) {
if (errcode == error_code_not_committed) {
retryConflict.addSample(1);
} else {
retryOther.addSample(1);
}
auto begin = SteadyClock::now();
auto ret = co_await tr_.onError(errcode);
retryBackoff.addSample(SteadyClock::now() - begin);
co_return ret.error() == 0;
}
Status FDBTransaction::testFDBError(fdb_error_t errCode, bool commit) {
return makeFDBError(errCode, commit ? Op::Commit : Op::Get).error();
}
CoTryTask<int64_t> FDBTransaction::getReadVersion() {
co_return co_await OpWrapper<Op::GetReadVersion>::run([&](Op op) -> CoTryTask<int64_t> {
auto r = co_await tr_.getReadVersion();
CHECK_FDB_ERRCODE(Op::GetReadVersion, r.error());
co_return r.value();
});
}
void FDBTransaction::setReadVersion(int64_t version) {
if (version >= 0) {
tr_.setReadVersion(version);
}
}
int64_t FDBTransaction::getCommittedVersion() {
int64_t version;
auto errcode = tr_.getCommittedVersion(&version);
return errcode == 0 ? version : -1;
}
} // namespace hf3fs::kv

60
src/fdb/FDBTransaction.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#include <cstdint>
#include <optional>
#include <string_view>
#include "common/kv/ITransaction.h"
#include "common/utils/Coroutine.h"
#include "common/utils/Status.h"
#include "fdb/FDB.h"
namespace hf3fs::kv {
class FDBTransaction : public IReadWriteTransaction {
public:
FDBTransaction(fdb::Transaction &&tr)
: tr_(std::move(tr)),
errcode_(0) {}
// Read operations
CoTryTask<std::optional<String>> snapshotGet(std::string_view key) override;
CoTryTask<GetRangeResult> snapshotGetRange(const KeySelector &begin, const KeySelector &end, int32_t limit) override;
CoTryTask<std::optional<String>> get(std::string_view key) override;
CoTryTask<GetRangeResult> getRange(const KeySelector &begin, const KeySelector &end, int32_t limit) override;
CoTryTask<void> cancel() override;
CoTryTask<void> addReadConflict(std::string_view key) override;
CoTryTask<void> addReadConflictRange(std::string_view begin, std::string_view end) override;
// Write operations
CoTryTask<void> set(std::string_view key, std::string_view value) override;
CoTryTask<void> clear(std::string_view key) override;
CoTryTask<void> setVersionstampedKey(std::string_view key, uint32_t offset, std::string_view value) override;
CoTryTask<void> setVersionstampedValue(std::string_view key, std::string_view value, uint32_t offset) override;
CoTryTask<void> clearRange(std::string_view begin, std::string_view end); // only used in test.
CoTryTask<void> commit() override;
void reset() override;
Result<Void> setOption(FDBTransactionOption option, std::string_view value = {});
CoTask<bool> onError(fdb_error_t errcode);
CoTryTask<int64_t> getReadVersion();
int64_t getCommittedVersion() override;
void setReadVersion(int64_t version) override;
fdb_error_t errcode() const { return errcode_; }
private:
friend class TestFDBTransaction;
static Status testFDBError(int errCode, bool commit);
fdb::Transaction tr_;
std::atomic<fdb_error_t> errcode_;
};
} // namespace hf3fs::kv

63
src/fdb/HybridKvEngine.cc Normal file
View File

@@ -0,0 +1,63 @@
#include "HybridKvEngine.h"
#include <folly/Random.h>
#include "FDBContext.h"
#include "FDBKVEngine.h"
#include "HybridKvEngineConfig.h"
#include "common/kv/mem/MemKVEngine.h"
namespace hf3fs::kv {
HybridKvEngine::HybridKvEngine() = default;
HybridKvEngine::~HybridKvEngine() = default;
std::shared_ptr<HybridKvEngine> HybridKvEngine::fromMem() {
std::shared_ptr<HybridKvEngine> p(new HybridKvEngine);
p->kvEngines_.push_back(std::make_unique<kv::MemKVEngine>());
return p;
}
std::shared_ptr<HybridKvEngine> HybridKvEngine::fromFdb(const kv::fdb::FDBConfig &config) {
std::shared_ptr<HybridKvEngine> p(new HybridKvEngine);
p->fdbContext_ = kv::fdb::FDBContext::create(config);
p->kvEngines_.reserve(p->fdbContext_->maxDbCount());
for (auto i = 0; i < p->fdbContext_->maxDbCount(); ++i) {
p->kvEngines_.push_back(std::make_unique<kv::FDBKVEngine>(p->fdbContext_->getDB()));
}
return p;
}
std::shared_ptr<HybridKvEngine> HybridKvEngine::from(bool useMemKV, const kv::fdb::FDBConfig &config) {
if (useMemKV) return fromMem();
return fromFdb(config);
}
std::shared_ptr<HybridKvEngine> HybridKvEngine::from(const HybridKvEngineConfig &config) {
return from(config.use_memkv(), config.fdb());
}
std::shared_ptr<HybridKvEngine> HybridKvEngine::from(const HybridKvEngineConfig &config,
bool useMemKV,
const kv::fdb::FDBConfig &fdbConfig) {
static const HybridKvEngineConfig defaultHybridConfig;
if (config == defaultHybridConfig) {
return from(useMemKV, fdbConfig);
} else {
return from(config);
}
}
kv::IKVEngine &HybridKvEngine::pick() const {
assert(!kvEngines_.empty());
auto idx = folly::Random::rand32() % kvEngines_.size();
return *kvEngines_[idx];
}
std::unique_ptr<kv::IReadOnlyTransaction> HybridKvEngine::createReadonlyTransaction() {
return pick().createReadonlyTransaction();
}
std::unique_ptr<kv::IReadWriteTransaction> HybridKvEngine::createReadWriteTransaction() {
return pick().createReadWriteTransaction();
}
} // namespace hf3fs::kv

46
src/fdb/HybridKvEngine.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include "common/kv/IKVEngine.h"
namespace hf3fs::kv::fdb {
struct FDBConfig;
class FDBContext;
} // namespace hf3fs::kv::fdb
namespace hf3fs::kv {
struct HybridKvEngineConfig;
class HybridKvEngine : public kv::IKVEngine {
public:
static std::shared_ptr<HybridKvEngine> fromMem();
static std::shared_ptr<HybridKvEngine> fromFdb(const kv::fdb::FDBConfig &config);
static std::shared_ptr<HybridKvEngine> from(bool useMemKV, const kv::fdb::FDBConfig &config);
static std::shared_ptr<HybridKvEngine> from(const HybridKvEngineConfig &config);
// use `config` if explicitly set, else fallback to use `useMemKV` and `fdbConfig`.
static std::shared_ptr<HybridKvEngine> from(const HybridKvEngineConfig &config,
bool useMemKV,
const kv::fdb::FDBConfig &fdbConfig);
// config contains:
// 1. use_memkv (fallback mode)
// 2. fdb (fallback mode)
// 3. kv_engine (new)
static std::shared_ptr<HybridKvEngine> fromSuperConfig(const auto &cfg) {
return from(cfg.kv_engine(), cfg.use_memkv(), cfg.fdb());
}
~HybridKvEngine();
std::unique_ptr<kv::IReadOnlyTransaction> createReadonlyTransaction() override;
std::unique_ptr<kv::IReadWriteTransaction> createReadWriteTransaction() override;
private:
HybridKvEngine();
kv::IKVEngine &pick() const;
std::shared_ptr<kv::fdb::FDBContext> fdbContext_;
std::vector<std::unique_ptr<kv::IKVEngine>> kvEngines_;
};
} // namespace hf3fs::kv

View File

@@ -0,0 +1,12 @@
#pragma once
#include "FDBConfig.h"
namespace hf3fs::kv {
struct HybridKvEngineConfig : public ConfigBase<HybridKvEngineConfig> {
bool operator==(const HybridKvEngineConfig &other) const { return static_cast<const ConfigBase &>(*this) == other; }
CONFIG_ITEM(use_memkv, false);
CONFIG_OBJ(fdb, kv::fdb::FDBConfig);
};
} // namespace hf3fs::kv

316
src/fdb/error_definitions.h Normal file
View File

@@ -0,0 +1,316 @@
/*
* error_definitions.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* 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.
*/
#ifdef ERROR
// SOMEDAY: Split this into flow, fdbclient, fdbserver error headers?
// Error codes defined here are primarily for programmatic use, not debugging: a separate
// error should be defined if and only if there is a sensible situation in which code could
// catch and react specifically to that error. So for example there is only one
// internal_error code even though there are a huge number of internal errors; extra
// information is logged in the trace file.
// 1xxx Normal failure (plausibly these should not even be "errors", but they are failures of
// the way operations are currently defined)
// clang-format off
ERROR( success, 0, "Success" )
ERROR( end_of_stream, 1, "End of stream" )
ERROR( operation_failed, 1000, "Operation failed")
ERROR( wrong_shard_server, 1001, "Shard is not available from this server")
ERROR( operation_obsolete, 1002, "Operation result no longer necessary")
ERROR( cold_cache_server, 1003, "Cache server is not warm for this range")
ERROR( timed_out, 1004, "Operation timed out" )
ERROR( coordinated_state_conflict, 1005, "Conflict occurred while changing coordination information" )
ERROR( all_alternatives_failed, 1006, "All alternatives failed" )
ERROR( transaction_too_old, 1007, "Transaction is too old to perform reads or be committed" )
ERROR( no_more_servers, 1008, "Not enough physical servers available" )
ERROR( future_version, 1009, "Request for future version" )
ERROR( movekeys_conflict, 1010, "Conflicting attempts to change data distribution" )
ERROR( tlog_stopped, 1011, "TLog stopped" )
ERROR( server_request_queue_full, 1012, "Server request queue is full" )
ERROR( not_committed, 1020, "Transaction not committed due to conflict with another transaction" )
ERROR( commit_unknown_result, 1021, "Transaction may or may not have committed" )
ERROR( transaction_cancelled, 1025, "Operation aborted because the transaction was cancelled" )
ERROR( connection_failed, 1026, "Network connection failed" )
ERROR( coordinators_changed, 1027, "Coordination servers have changed" )
ERROR( new_coordinators_timed_out, 1028, "New coordination servers did not respond in a timely way" )
ERROR( watch_cancelled, 1029, "Watch cancelled because storage server watch limit exceeded" )
ERROR( request_maybe_delivered, 1030, "Request may or may not have been delivered" )
ERROR( transaction_timed_out, 1031, "Operation aborted because the transaction timed out" )
ERROR( too_many_watches, 1032, "Too many watches currently set" )
ERROR( locality_information_unavailable, 1033, "Locality information not available" )
ERROR( watches_disabled, 1034, "Watches cannot be set if read your writes is disabled" )
ERROR( default_error_or, 1035, "Default error for an ErrorOr object" )
ERROR( accessed_unreadable, 1036, "Read or wrote an unreadable key" )
ERROR( process_behind, 1037, "Storage process does not have recent mutations" )
ERROR( database_locked, 1038, "Database is locked" )
ERROR( cluster_version_changed, 1039, "The protocol version of the cluster has changed" )
ERROR( external_client_already_loaded, 1040, "External client has already been loaded" )
ERROR( lookup_failed, 1041, "DNS lookup failed" )
ERROR( proxy_memory_limit_exceeded, 1042, "CommitProxy commit memory limit exceeded" )
ERROR( shutdown_in_progress, 1043, "Operation no longer supported due to shutdown" )
ERROR( serialization_failed, 1044, "Failed to deserialize an object" )
ERROR( connection_unreferenced, 1048, "No peer references for connection" )
ERROR( connection_idle, 1049, "Connection closed after idle timeout" )
ERROR( disk_adapter_reset, 1050, "The disk queue adpater reset" )
ERROR( batch_transaction_throttled, 1051, "Batch GRV request rate limit exceeded")
ERROR( dd_cancelled, 1052, "Data distribution components cancelled")
ERROR( dd_not_found, 1053, "Data distributor not found")
ERROR( wrong_connection_file, 1054, "Connection file mismatch")
ERROR( version_already_compacted, 1055, "The requested changes have been compacted away")
ERROR( local_config_changed, 1056, "Local configuration file has changed. Restart and apply these changes" )
ERROR( failed_to_reach_quorum, 1057, "Failed to reach quorum from configuration database nodes. Retry sending these requests" )
ERROR( unsupported_format_version, 1058, "Format version not supported" )
ERROR( unknown_change_feed, 1059, "Change feed not found" )
ERROR( change_feed_not_registered, 1060, "Change feed not registered" )
ERROR( granule_assignment_conflict, 1061, "Conflicting attempts to assign blob granules" )
ERROR( change_feed_cancelled, 1062, "Change feed was cancelled" )
ERROR( blob_granule_file_load_error, 1063, "Error loading a blob file during granule materialization" )
ERROR( blob_granule_transaction_too_old, 1064, "Read version is older than blob granule history supports" )
ERROR( blob_manager_replaced, 1065, "This blob manager has been replaced." )
ERROR( change_feed_popped, 1066, "Tried to read a version older than what has been popped from the change feed" )
ERROR( remote_kvs_cancelled, 1067, "The remote key-value store is cancelled" )
ERROR( page_header_wrong_page_id, 1068, "Page header does not match location on disk" )
ERROR( page_header_checksum_failed, 1069, "Page header checksum failed" )
ERROR( page_header_version_not_supported, 1070, "Page header version is not supported" )
ERROR( page_encoding_not_supported, 1071, "Page encoding type is not supported or not valid" )
ERROR( page_decoding_failed, 1072, "Page content decoding failed" )
ERROR( unexpected_encoding_type, 1073, "Page content decoding failed" )
ERROR( encryption_key_not_found, 1074, "Encryption key not found" )
ERROR( broken_promise, 1100, "Broken promise" )
ERROR( operation_cancelled, 1101, "Asynchronous operation cancelled" )
ERROR( future_released, 1102, "Future has been released" )
ERROR( connection_leaked, 1103, "Connection object leaked" )
ERROR( never_reply, 1104, "Never reply to the request" )
ERROR( recruitment_failed, 1200, "Recruitment of a server failed" ) // Be careful, catching this will delete the data of a storage server or tlog permanently
ERROR( move_to_removed_server, 1201, "Attempt to move keys to a storage server that was removed" )
ERROR( worker_removed, 1202, "Normal worker shut down" ) // Be careful, catching this will delete the data of a storage server or tlog permanently
ERROR( cluster_recovery_failed, 1203, "Cluster recovery failed")
ERROR( master_max_versions_in_flight, 1204, "Master hit maximum number of versions in flight" )
ERROR( tlog_failed, 1205, "Cluster recovery terminating because a TLog failed" ) // similar to tlog_stopped, but the tlog has actually died
ERROR( worker_recovery_failed, 1206, "Recovery of a worker process failed" )
ERROR( please_reboot, 1207, "Reboot of server process requested" )
ERROR( please_reboot_delete, 1208, "Reboot of server process requested, with deletion of state" )
ERROR( commit_proxy_failed, 1209, "Master terminating because a CommitProxy failed" )
ERROR( resolver_failed, 1210, "Cluster recovery terminating because a Resolver failed" )
ERROR( server_overloaded, 1211, "Server is under too much load and cannot respond" )
ERROR( backup_worker_failed, 1212, "Cluster recovery terminating because a backup worker failed")
ERROR( tag_throttled, 1213, "Transaction tag is being throttled" )
ERROR( grv_proxy_failed, 1214, "Cluster recovery terminating because a GRVProxy failed" )
ERROR( dd_tracker_cancelled, 1215, "The data distribution tracker has been cancelled" )
ERROR( failed_to_progress, 1216, "Process has failed to make sufficient progress" )
ERROR( invalid_cluster_id, 1217, "Attempted to join cluster with a different cluster ID" )
ERROR( restart_cluster_controller, 1218, "Restart cluster controller process" )
ERROR( please_reboot_remote_kv_store, 1219, "Need to reboot the storage engine process as it died abnormally")
// 15xx Platform errors
ERROR( platform_error, 1500, "Platform error" )
ERROR( large_alloc_failed, 1501, "Large block allocation failed" )
ERROR( performance_counter_error, 1502, "QueryPerformanceCounter error" )
ERROR( io_error, 1510, "Disk i/o operation failed" )
ERROR( file_not_found, 1511, "File not found" )
ERROR( bind_failed, 1512, "Unable to bind to network" )
ERROR( file_not_readable, 1513, "File could not be read" )
ERROR( file_not_writable, 1514, "File could not be written" )
ERROR( no_cluster_file_found, 1515, "No cluster file found in current directory or default location" )
ERROR( file_too_large, 1516, "File too large to be read" )
ERROR( non_sequential_op, 1517, "Non sequential file operation not allowed" )
ERROR( http_bad_response, 1518, "HTTP response was badly formed" )
ERROR( http_not_accepted, 1519, "HTTP request not accepted" )
ERROR( checksum_failed, 1520, "A data checksum failed" )
ERROR( io_timeout, 1521, "A disk IO operation failed to complete in a timely manner" )
ERROR( file_corrupt, 1522, "A structurally corrupt data file was detected" )
ERROR( http_request_failed, 1523, "HTTP response code not received or indicated failure" )
ERROR( http_auth_failed, 1524, "HTTP request failed due to bad credentials" )
ERROR( http_bad_request_id, 1525, "HTTP response contained an unexpected X-Request-ID header" )
// 2xxx Attempt (presumably by a _client_) to do something illegal. If an error is known to
// be internally caused, it should be 41xx
ERROR( client_invalid_operation, 2000, "Invalid API call" )
ERROR( commit_read_incomplete, 2002, "Commit with incomplete read" )
ERROR( test_specification_invalid, 2003, "Invalid test specification" )
ERROR( key_outside_legal_range, 2004, "Key outside legal range" )
ERROR( inverted_range, 2005, "Range begin key larger than end key" )
ERROR( invalid_option_value, 2006, "Option set with an invalid value" )
ERROR( invalid_option, 2007, "Option not valid in this context" )
ERROR( network_not_setup, 2008, "Action not possible before the network is configured" )
ERROR( network_already_setup, 2009, "Network can be configured only once" )
ERROR( read_version_already_set, 2010, "Transaction already has a read version set" )
ERROR( version_invalid, 2011, "Version not valid" )
ERROR( range_limits_invalid, 2012, "Range limits not valid" )
ERROR( invalid_database_name, 2013, "Database name must be 'DB'" )
ERROR( attribute_not_found, 2014, "Attribute not found" )
ERROR( future_not_set, 2015, "Future not ready" )
ERROR( future_not_error, 2016, "Future not an error" )
ERROR( used_during_commit, 2017, "Operation issued while a commit was outstanding" )
ERROR( invalid_mutation_type, 2018, "Unrecognized atomic mutation type" )
ERROR( attribute_too_large, 2019, "Attribute too large for type int" )
ERROR( transaction_invalid_version, 2020, "Transaction does not have a valid commit version" )
ERROR( no_commit_version, 2021, "Transaction is read-only and therefore does not have a commit version" )
ERROR( environment_variable_network_option_failed, 2022, "Environment variable network option could not be set" )
ERROR( transaction_read_only, 2023, "Attempted to commit a transaction specified as read-only" )
ERROR( invalid_cache_eviction_policy, 2024, "Invalid cache eviction policy, only random and lru are supported" )
ERROR( network_cannot_be_restarted, 2025, "Network can only be started once" )
ERROR( blocked_from_network_thread, 2026, "Detected a deadlock in a callback called from the network thread" )
ERROR( invalid_config_db_range_read, 2027, "Invalid configuration database range read" )
ERROR( invalid_config_db_key, 2028, "Invalid configuration database key provided" )
ERROR( invalid_config_path, 2029, "Invalid configuration path" )
ERROR( mapper_bad_index, 2030, "The index in K[] or V[] is not a valid number or out of range" )
ERROR( mapper_no_such_key, 2031, "A mapped key is not set in database" )
ERROR( mapper_bad_range_decriptor, 2032, "\"{...}\" must be the last element of the mapper tuple" )
ERROR( quick_get_key_values_has_more, 2033, "One of the mapped range queries is too large" )
ERROR( quick_get_value_miss, 2034, "Found a mapped key that is not served in the same SS" )
ERROR( quick_get_key_values_miss, 2035, "Found a mapped range that is not served in the same SS" )
ERROR( blob_granule_no_ryw, 2036, "Blob Granule Read Transactions must be specified as ryw-disabled" )
ERROR( blob_granule_not_materialized, 2037, "Blob Granule Read was not materialized" )
ERROR( get_mapped_key_values_has_more, 2038, "getMappedRange does not support continuation for now" )
ERROR( get_mapped_range_reads_your_writes, 2039, "getMappedRange tries to read data that were previously written in the transaction" )
ERROR( checkpoint_not_found, 2040, "Checkpoint not found" )
ERROR( key_not_tuple, 2041, "The key cannot be parsed as a tuple" );
ERROR( value_not_tuple, 2042, "The value cannot be parsed as a tuple" );
ERROR( mapper_not_tuple, 2043, "The mapper cannot be parsed as a tuple" );
ERROR( incompatible_protocol_version, 2100, "Incompatible protocol version" )
ERROR( transaction_too_large, 2101, "Transaction exceeds byte limit" )
ERROR( key_too_large, 2102, "Key length exceeds limit" )
ERROR( value_too_large, 2103, "Value length exceeds limit" )
ERROR( connection_string_invalid, 2104, "Connection string invalid" )
ERROR( address_in_use, 2105, "Local address in use" )
ERROR( invalid_local_address, 2106, "Invalid local address" )
ERROR( tls_error, 2107, "TLS error" )
ERROR( unsupported_operation, 2108, "Operation is not supported" )
ERROR( too_many_tags, 2109, "Too many tags set on transaction" )
ERROR( tag_too_long, 2110, "Tag set on transaction is too long" )
ERROR( too_many_tag_throttles, 2111, "Too many tag throttles have been created" )
ERROR( special_keys_cross_module_read, 2112, "Special key space range read crosses modules. Refer to the `special_key_space_relaxed' transaction option for more details." )
ERROR( special_keys_no_module_found, 2113, "Special key space range read does not intersect a module. Refer to the `special_key_space_relaxed' transaction option for more details." )
ERROR( special_keys_write_disabled, 2114, "Special Key space is not allowed to write by default. Refer to the `special_key_space_enable_writes` transaction option for more details." )
ERROR( special_keys_no_write_module_found, 2115, "Special key space key or keyrange in set or clear does not intersect a module" )
ERROR( special_keys_cross_module_clear, 2116, "Special key space clear crosses modules" )
ERROR( special_keys_api_failure, 2117, "Api call through special keys failed. For more information, call get on special key 0xff0xff/error_message to get a json string of the error message." )
ERROR( client_lib_invalid_metadata, 2118, "Invalid client library metadata." )
ERROR( client_lib_already_exists, 2119, "Client library with same identifier already exists on the cluster." )
ERROR( client_lib_not_found, 2120, "Client library for the given identifier not found." )
ERROR( client_lib_not_available, 2121, "Client library exists, but is not available for download." )
ERROR( client_lib_invalid_binary, 2122, "Invalid client library binary." )
ERROR( tenant_name_required, 2130, "Tenant name must be specified to access data in the cluster" )
ERROR( tenant_not_found, 2131, "Tenant does not exist" )
ERROR( tenant_already_exists, 2132, "A tenant with the given name already exists" )
ERROR( tenant_not_empty, 2133, "Cannot delete a non-empty tenant" )
ERROR( invalid_tenant_name, 2134, "Tenant name cannot begin with \\xff");
ERROR( tenant_prefix_allocator_conflict, 2135, "The database already has keys stored at the prefix allocated for the tenant");
ERROR( tenants_disabled, 2136, "Tenants have been disabled in the cluster");
ERROR( unknown_tenant, 2137, "Tenant is not available from this server")
// 2200 - errors from bindings and official APIs
ERROR( api_version_unset, 2200, "API version is not set" )
ERROR( api_version_already_set, 2201, "API version may be set only once" )
ERROR( api_version_invalid, 2202, "API version not valid" )
ERROR( api_version_not_supported, 2203, "API version not supported" )
ERROR( exact_mode_without_limits, 2210, "EXACT streaming mode requires limits, but none were given" )
ERROR( invalid_tuple_data_type, 2250, "Unrecognized data type in packed tuple")
ERROR( invalid_tuple_index, 2251, "Tuple does not have element at specified index")
ERROR( key_not_in_subspace, 2252, "Cannot unpack key that is not in subspace" )
ERROR( manual_prefixes_not_enabled, 2253, "Cannot specify a prefix unless manual prefixes are enabled" )
ERROR( prefix_in_partition, 2254, "Cannot specify a prefix in a partition" )
ERROR( cannot_open_root_directory, 2255, "Root directory cannot be opened" )
ERROR( directory_already_exists, 2256, "Directory already exists" )
ERROR( directory_does_not_exist, 2257, "Directory does not exist" )
ERROR( parent_directory_does_not_exist, 2258, "Directory's parent does not exist" )
ERROR( mismatched_layer, 2259, "Directory has already been created with a different layer string" )
ERROR( invalid_directory_layer_metadata, 2260, "Invalid directory layer metadata" )
ERROR( cannot_move_directory_between_partitions, 2261, "Directory cannot be moved between partitions" )
ERROR( cannot_use_partition_as_subspace, 2262, "Directory partition cannot be used as subspace" )
ERROR( incompatible_directory_version, 2263, "Directory layer was created with an incompatible version" )
ERROR( directory_prefix_not_empty, 2264, "Database has keys stored at the prefix chosen by the automatic prefix allocator" )
ERROR( directory_prefix_in_use, 2265, "Directory layer already has a conflicting prefix" )
ERROR( invalid_destination_directory, 2266, "Target directory is invalid" )
ERROR( cannot_modify_root_directory, 2267, "Root directory cannot be modified" )
ERROR( invalid_uuid_size, 2268, "UUID is not sixteen bytes");
// 2300 - backup and restore errors
ERROR( backup_error, 2300, "Backup error")
ERROR( restore_error, 2301, "Restore error")
ERROR( backup_duplicate, 2311, "Backup duplicate request")
ERROR( backup_unneeded, 2312, "Backup unneeded request")
ERROR( backup_bad_block_size, 2313, "Backup file block size too small")
ERROR( backup_invalid_url, 2314, "Backup Container URL invalid")
ERROR( backup_invalid_info, 2315, "Backup Container info invalid")
ERROR( backup_cannot_expire, 2316, "Cannot expire requested data from backup without violating minimum restorability")
ERROR( backup_auth_missing, 2317, "Cannot find authentication details (such as a password or secret key) for the specified Backup Container URL")
ERROR( backup_auth_unreadable, 2318, "Cannot read or parse one or more sources of authentication information for Backup Container URLs")
ERROR( backup_does_not_exist, 2319, "Backup does not exist")
ERROR( backup_not_filterable_with_key_ranges, 2320, "Backup before 6.3 cannot be filtered with key ranges")
ERROR( backup_not_overlapped_with_keys_filter, 2321, "Backup key ranges doesn't overlap with key ranges filter")
ERROR( restore_invalid_version, 2361, "Invalid restore version")
ERROR( restore_corrupted_data, 2362, "Corrupted backup data")
ERROR( restore_missing_data, 2363, "Missing backup data")
ERROR( restore_duplicate_tag, 2364, "Restore duplicate request")
ERROR( restore_unknown_tag, 2365, "Restore tag does not exist")
ERROR( restore_unknown_file_type, 2366, "Unknown backup/restore file type")
ERROR( restore_unsupported_file_version, 2367, "Unsupported backup file version")
ERROR( restore_bad_read, 2368, "Unexpected number of bytes read")
ERROR( restore_corrupted_data_padding, 2369, "Backup file has unexpected padding bytes")
ERROR( restore_destination_not_empty, 2370, "Attempted to restore into a non-empty destination database")
ERROR( restore_duplicate_uid, 2371, "Attempted to restore using a UID that had been used for an aborted restore")
ERROR( task_invalid_version, 2381, "Invalid task version")
ERROR( task_interrupted, 2382, "Task execution stopped due to timeout, abort, or completion by another worker")
ERROR( invalid_encryption_key_file, 2383, "The provided encryption key file has invalid contents" )
ERROR( key_not_found, 2400, "Expected key is missing")
ERROR( json_malformed, 2401, "JSON string was malformed")
ERROR( json_eof_expected, 2402, "JSON string did not terminate where expected")
// 2500 - disk snapshot based backup errors
ERROR( snap_disable_tlog_pop_failed, 2500, "Failed to disable tlog pops")
ERROR( snap_storage_failed, 2501, "Failed to snapshot storage nodes")
ERROR( snap_tlog_failed, 2502, "Failed to snapshot TLog nodes")
ERROR( snap_coord_failed, 2503, "Failed to snapshot coordinator nodes")
ERROR( snap_enable_tlog_pop_failed, 2504, "Failed to enable tlog pops")
ERROR( snap_path_not_whitelisted, 2505, "Snapshot create binary path not whitelisted")
ERROR( snap_not_fully_recovered_unsupported, 2506, "Unsupported when the cluster is not fully recovered")
ERROR( snap_log_anti_quorum_unsupported, 2507, "Unsupported when log anti quorum is configured")
ERROR( snap_with_recovery_unsupported, 2508, "Cluster recovery during snapshot operation not supported")
ERROR( snap_invalid_uid_string, 2509, "The given uid string is not a 32-length hex string")
// 27XX - Encryption operations errors
ERROR( encrypt_ops_error, 2700, "Encryption operation error")
ERROR( encrypt_header_metadata_mismatch, 2701, "Encryption header metadata mismatch")
ERROR( encrypt_key_not_found, 2702, "Expected encryption key is missing")
ERROR( encrypt_key_ttl_expired, 2703, "Expected encryption key TTL has expired")
ERROR( encrypt_header_authtoken_mismatch, 2704, "Encryption header authentication token mismatch")
ERROR( encrypt_update_cipher, 2705, "Attempt to update encryption cipher key")
ERROR( encrypt_invalid_id, 2706, "Invalid encryption domainId or encryption cipher key id")
// 4xxx Internal errors (those that should be generated only by bugs) are decimal 4xxx
ERROR( unknown_error, 4000, "An unknown error occurred" ) // C++ exception not of type Error
ERROR( internal_error, 4100, "An internal error occurred" )
ERROR( not_implemented, 4200, "Not implemented yet" )
// clang-format on
#undef ERROR
#endif