mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-10 18:05:44 +02:00
* feat(namespaces): Initial support for multi-tenant #3050 This PR introduces a way to create multiple, separate and isolated namespaces in Dragonfly. Each user can be associated with a single namespace, and will not be able to interact with other namespaces. This is still experimental, and lacks some important features, such as: * Replication and RDB saving completely ignores non-default namespaces * Defrag and statistics either use the default namespace or all namespaces without separation To associate a user with a namespace, use the `ACL` command with the `TENANT:<namespace>` flag: ``` ACL SETUSER user TENANT:namespace1 ON >user_pass +@all ~* ``` For more examples and up to date info check `tests/dragonfly/acl_family_test.py` - specifically the `test_namespaces` function.
94 lines
2.5 KiB
C++
94 lines
2.5 KiB
C++
// Copyright 2022, DragonflyDB authors. All rights reserved.
|
|
// See LICENSE for licensing terms.
|
|
//
|
|
|
|
#include "server/journal/executor.h"
|
|
|
|
#include <absl/strings/str_cat.h>
|
|
#include <absl/strings/str_split.h>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
#include "base/logging.h"
|
|
#include "server/main_service.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace dfly {
|
|
|
|
namespace {
|
|
// Build a CmdData from parts passed to absl::StrCat.
|
|
template <typename... Ts> journal::ParsedEntry::CmdData BuildFromParts(Ts... parts) {
|
|
vector<string> raw_parts{absl::StrCat(std::forward<Ts>(parts))...};
|
|
|
|
auto cmd_str = accumulate(raw_parts.begin(), raw_parts.end(), std::string{});
|
|
auto buf = make_unique<char[]>(cmd_str.size());
|
|
memcpy(buf.get(), cmd_str.data(), cmd_str.size());
|
|
|
|
CmdArgVec slice_parts{};
|
|
size_t start = 0;
|
|
for (const auto& part : raw_parts) {
|
|
slice_parts.emplace_back(buf.get() + start, part.size());
|
|
start += part.size();
|
|
}
|
|
|
|
return {std::move(buf), std::move(slice_parts)};
|
|
}
|
|
} // namespace
|
|
|
|
JournalExecutor::JournalExecutor(Service* service)
|
|
: service_{service},
|
|
reply_builder_{facade::ReplyMode::NONE},
|
|
conn_context_{nullptr, nullptr, &reply_builder_} {
|
|
conn_context_.is_replicating = true;
|
|
conn_context_.journal_emulated = true;
|
|
conn_context_.skip_acl_validation = true;
|
|
conn_context_.ns = &namespaces.GetDefaultNamespace();
|
|
}
|
|
|
|
JournalExecutor::~JournalExecutor() {
|
|
conn_context_.Inject(nullptr);
|
|
}
|
|
|
|
void JournalExecutor::Execute(DbIndex dbid, absl::Span<journal::ParsedEntry::CmdData> cmds) {
|
|
SelectDb(dbid);
|
|
for (auto& cmd : cmds) {
|
|
Execute(cmd);
|
|
}
|
|
}
|
|
|
|
void JournalExecutor::Execute(DbIndex dbid, journal::ParsedEntry::CmdData& cmd) {
|
|
SelectDb(dbid);
|
|
Execute(cmd);
|
|
}
|
|
|
|
void JournalExecutor::FlushAll() {
|
|
auto cmd = BuildFromParts("FLUSHALL");
|
|
Execute(cmd);
|
|
}
|
|
|
|
void JournalExecutor::FlushSlots(const cluster::SlotRange& slot_range) {
|
|
auto cmd = BuildFromParts("DFLYCLUSTER", "FLUSHSLOTS", slot_range.start, slot_range.end);
|
|
Execute(cmd);
|
|
}
|
|
|
|
void JournalExecutor::Execute(journal::ParsedEntry::CmdData& cmd) {
|
|
auto span = CmdArgList{cmd.cmd_args.data(), cmd.cmd_args.size()};
|
|
service_->DispatchCommand(span, &conn_context_);
|
|
}
|
|
|
|
void JournalExecutor::SelectDb(DbIndex dbid) {
|
|
conn_context_.conn_state.db_index = dbid;
|
|
|
|
if (ensured_dbs_.size() <= dbid)
|
|
ensured_dbs_.resize(dbid + 1);
|
|
|
|
if (!ensured_dbs_[dbid]) {
|
|
auto cmd = BuildFromParts("SELECT", dbid);
|
|
Execute(cmd);
|
|
ensured_dbs_[dbid] = true;
|
|
}
|
|
}
|
|
|
|
} // namespace dfly
|